cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

madv_populate.c (7393B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
      4 *
      5 * Copyright 2021, Red Hat, Inc.
      6 *
      7 * Author(s): David Hildenbrand <david@redhat.com>
      8 */
      9#define _GNU_SOURCE
     10#include <stdlib.h>
     11#include <string.h>
     12#include <stdbool.h>
     13#include <stdint.h>
     14#include <unistd.h>
     15#include <errno.h>
     16#include <fcntl.h>
     17#include <linux/mman.h>
     18#include <sys/mman.h>
     19
     20#include "../kselftest.h"
     21#include "vm_util.h"
     22
     23/*
     24 * For now, we're using 2 MiB of private anonymous memory for all tests.
     25 */
     26#define SIZE (2 * 1024 * 1024)
     27
     28static size_t pagesize;
     29
     30static bool pagemap_is_populated(int fd, char *start)
     31{
     32	uint64_t entry = pagemap_get_entry(fd, start);
     33
     34	/* Present or swapped. */
     35	return entry & 0xc000000000000000ull;
     36}
     37
     38static void sense_support(void)
     39{
     40	char *addr;
     41	int ret;
     42
     43	addr = mmap(0, pagesize, PROT_READ | PROT_WRITE,
     44		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
     45	if (!addr)
     46		ksft_exit_fail_msg("mmap failed\n");
     47
     48	ret = madvise(addr, pagesize, MADV_POPULATE_READ);
     49	if (ret)
     50		ksft_exit_skip("MADV_POPULATE_READ is not available\n");
     51
     52	ret = madvise(addr, pagesize, MADV_POPULATE_WRITE);
     53	if (ret)
     54		ksft_exit_skip("MADV_POPULATE_WRITE is not available\n");
     55
     56	munmap(addr, pagesize);
     57}
     58
     59static void test_prot_read(void)
     60{
     61	char *addr;
     62	int ret;
     63
     64	ksft_print_msg("[RUN] %s\n", __func__);
     65
     66	addr = mmap(0, SIZE, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
     67	if (addr == MAP_FAILED)
     68		ksft_exit_fail_msg("mmap failed\n");
     69
     70	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
     71	ksft_test_result(!ret, "MADV_POPULATE_READ with PROT_READ\n");
     72
     73	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
     74	ksft_test_result(ret == -1 && errno == EINVAL,
     75			 "MADV_POPULATE_WRITE with PROT_READ\n");
     76
     77	munmap(addr, SIZE);
     78}
     79
     80static void test_prot_write(void)
     81{
     82	char *addr;
     83	int ret;
     84
     85	ksft_print_msg("[RUN] %s\n", __func__);
     86
     87	addr = mmap(0, SIZE, PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
     88	if (addr == MAP_FAILED)
     89		ksft_exit_fail_msg("mmap failed\n");
     90
     91	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
     92	ksft_test_result(ret == -1 && errno == EINVAL,
     93			 "MADV_POPULATE_READ with PROT_WRITE\n");
     94
     95	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
     96	ksft_test_result(!ret, "MADV_POPULATE_WRITE with PROT_WRITE\n");
     97
     98	munmap(addr, SIZE);
     99}
    100
    101static void test_holes(void)
    102{
    103	char *addr;
    104	int ret;
    105
    106	ksft_print_msg("[RUN] %s\n", __func__);
    107
    108	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
    109		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    110	if (addr == MAP_FAILED)
    111		ksft_exit_fail_msg("mmap failed\n");
    112	ret = munmap(addr + pagesize, pagesize);
    113	if (ret)
    114		ksft_exit_fail_msg("munmap failed\n");
    115
    116	/* Hole in the middle */
    117	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
    118	ksft_test_result(ret == -1 && errno == ENOMEM,
    119			 "MADV_POPULATE_READ with holes in the middle\n");
    120	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
    121	ksft_test_result(ret == -1 && errno == ENOMEM,
    122			 "MADV_POPULATE_WRITE with holes in the middle\n");
    123
    124	/* Hole at end */
    125	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_READ);
    126	ksft_test_result(ret == -1 && errno == ENOMEM,
    127			 "MADV_POPULATE_READ with holes at the end\n");
    128	ret = madvise(addr, 2 * pagesize, MADV_POPULATE_WRITE);
    129	ksft_test_result(ret == -1 && errno == ENOMEM,
    130			 "MADV_POPULATE_WRITE with holes at the end\n");
    131
    132	/* Hole at beginning */
    133	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_READ);
    134	ksft_test_result(ret == -1 && errno == ENOMEM,
    135			 "MADV_POPULATE_READ with holes at the beginning\n");
    136	ret = madvise(addr + pagesize, pagesize, MADV_POPULATE_WRITE);
    137	ksft_test_result(ret == -1 && errno == ENOMEM,
    138			 "MADV_POPULATE_WRITE with holes at the beginning\n");
    139
    140	munmap(addr, SIZE);
    141}
    142
    143static bool range_is_populated(char *start, ssize_t size)
    144{
    145	int fd = open("/proc/self/pagemap", O_RDONLY);
    146	bool ret = true;
    147
    148	if (fd < 0)
    149		ksft_exit_fail_msg("opening pagemap failed\n");
    150	for (; size > 0 && ret; size -= pagesize, start += pagesize)
    151		if (!pagemap_is_populated(fd, start))
    152			ret = false;
    153	close(fd);
    154	return ret;
    155}
    156
    157static bool range_is_not_populated(char *start, ssize_t size)
    158{
    159	int fd = open("/proc/self/pagemap", O_RDONLY);
    160	bool ret = true;
    161
    162	if (fd < 0)
    163		ksft_exit_fail_msg("opening pagemap failed\n");
    164	for (; size > 0 && ret; size -= pagesize, start += pagesize)
    165		if (pagemap_is_populated(fd, start))
    166			ret = false;
    167	close(fd);
    168	return ret;
    169}
    170
    171static void test_populate_read(void)
    172{
    173	char *addr;
    174	int ret;
    175
    176	ksft_print_msg("[RUN] %s\n", __func__);
    177
    178	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
    179		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    180	if (addr == MAP_FAILED)
    181		ksft_exit_fail_msg("mmap failed\n");
    182	ksft_test_result(range_is_not_populated(addr, SIZE),
    183			 "range initially not populated\n");
    184
    185	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
    186	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
    187	ksft_test_result(range_is_populated(addr, SIZE),
    188			 "range is populated\n");
    189
    190	munmap(addr, SIZE);
    191}
    192
    193static void test_populate_write(void)
    194{
    195	char *addr;
    196	int ret;
    197
    198	ksft_print_msg("[RUN] %s\n", __func__);
    199
    200	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
    201		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    202	if (addr == MAP_FAILED)
    203		ksft_exit_fail_msg("mmap failed\n");
    204	ksft_test_result(range_is_not_populated(addr, SIZE),
    205			 "range initially not populated\n");
    206
    207	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
    208	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
    209	ksft_test_result(range_is_populated(addr, SIZE),
    210			 "range is populated\n");
    211
    212	munmap(addr, SIZE);
    213}
    214
    215static bool range_is_softdirty(char *start, ssize_t size)
    216{
    217	int fd = open("/proc/self/pagemap", O_RDONLY);
    218	bool ret = true;
    219
    220	if (fd < 0)
    221		ksft_exit_fail_msg("opening pagemap failed\n");
    222	for (; size > 0 && ret; size -= pagesize, start += pagesize)
    223		if (!pagemap_is_softdirty(fd, start))
    224			ret = false;
    225	close(fd);
    226	return ret;
    227}
    228
    229static bool range_is_not_softdirty(char *start, ssize_t size)
    230{
    231	int fd = open("/proc/self/pagemap", O_RDONLY);
    232	bool ret = true;
    233
    234	if (fd < 0)
    235		ksft_exit_fail_msg("opening pagemap failed\n");
    236	for (; size > 0 && ret; size -= pagesize, start += pagesize)
    237		if (pagemap_is_softdirty(fd, start))
    238			ret = false;
    239	close(fd);
    240	return ret;
    241}
    242
    243static void test_softdirty(void)
    244{
    245	char *addr;
    246	int ret;
    247
    248	ksft_print_msg("[RUN] %s\n", __func__);
    249
    250	addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
    251		    MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    252	if (addr == MAP_FAILED)
    253		ksft_exit_fail_msg("mmap failed\n");
    254
    255	/* Clear any softdirty bits. */
    256	clear_softdirty();
    257	ksft_test_result(range_is_not_softdirty(addr, SIZE),
    258			 "range is not softdirty\n");
    259
    260	/* Populating READ should set softdirty. */
    261	ret = madvise(addr, SIZE, MADV_POPULATE_READ);
    262	ksft_test_result(!ret, "MADV_POPULATE_READ\n");
    263	ksft_test_result(range_is_not_softdirty(addr, SIZE),
    264			 "range is not softdirty\n");
    265
    266	/* Populating WRITE should set softdirty. */
    267	ret = madvise(addr, SIZE, MADV_POPULATE_WRITE);
    268	ksft_test_result(!ret, "MADV_POPULATE_WRITE\n");
    269	ksft_test_result(range_is_softdirty(addr, SIZE),
    270			 "range is softdirty\n");
    271
    272	munmap(addr, SIZE);
    273}
    274
    275int main(int argc, char **argv)
    276{
    277	int err;
    278
    279	pagesize = getpagesize();
    280
    281	ksft_print_header();
    282	ksft_set_plan(21);
    283
    284	sense_support();
    285	test_prot_read();
    286	test_prot_write();
    287	test_holes();
    288	test_populate_read();
    289	test_populate_write();
    290	test_softdirty();
    291
    292	err = ksft_get_fail_cnt();
    293	if (err)
    294		ksft_exit_fail_msg("%d out of %d tests failed\n",
    295				   err, ksft_test_num());
    296	return ksft_exit_pass();
    297}