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

mincore_selftest.c (8485B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * kselftest suite for mincore().
      4 *
      5 * Copyright (C) 2020 Collabora, Ltd.
      6 */
      7
      8#define _GNU_SOURCE
      9
     10#include <stdio.h>
     11#include <errno.h>
     12#include <unistd.h>
     13#include <stdlib.h>
     14#include <sys/mman.h>
     15#include <string.h>
     16#include <fcntl.h>
     17
     18#include "../kselftest.h"
     19#include "../kselftest_harness.h"
     20
     21/* Default test file size: 4MB */
     22#define MB (1UL << 20)
     23#define FILE_SIZE (4 * MB)
     24
     25
     26/*
     27 * Tests the user interface. This test triggers most of the documented
     28 * error conditions in mincore().
     29 */
     30TEST(basic_interface)
     31{
     32	int retval;
     33	int page_size;
     34	unsigned char vec[1];
     35	char *addr;
     36
     37	page_size = sysconf(_SC_PAGESIZE);
     38
     39	/* Query a 0 byte sized range */
     40	retval = mincore(0, 0, vec);
     41	EXPECT_EQ(0, retval);
     42
     43	/* Addresses in the specified range are invalid or unmapped */
     44	errno = 0;
     45	retval = mincore(NULL, page_size, vec);
     46	EXPECT_EQ(-1, retval);
     47	EXPECT_EQ(ENOMEM, errno);
     48
     49	errno = 0;
     50	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
     51		MAP_SHARED | MAP_ANONYMOUS, -1, 0);
     52	ASSERT_NE(MAP_FAILED, addr) {
     53		TH_LOG("mmap error: %s", strerror(errno));
     54	}
     55
     56	/* <addr> argument is not page-aligned */
     57	errno = 0;
     58	retval = mincore(addr + 1, page_size, vec);
     59	EXPECT_EQ(-1, retval);
     60	EXPECT_EQ(EINVAL, errno);
     61
     62	/* <length> argument is too large */
     63	errno = 0;
     64	retval = mincore(addr, -1, vec);
     65	EXPECT_EQ(-1, retval);
     66	EXPECT_EQ(ENOMEM, errno);
     67
     68	/* <vec> argument points to an illegal address */
     69	errno = 0;
     70	retval = mincore(addr, page_size, NULL);
     71	EXPECT_EQ(-1, retval);
     72	EXPECT_EQ(EFAULT, errno);
     73	munmap(addr, page_size);
     74}
     75
     76
     77/*
     78 * Test mincore() behavior on a private anonymous page mapping.
     79 * Check that the page is not loaded into memory right after the mapping
     80 * but after accessing it (on-demand allocation).
     81 * Then free the page and check that it's not memory-resident.
     82 */
     83TEST(check_anonymous_locked_pages)
     84{
     85	unsigned char vec[1];
     86	char *addr;
     87	int retval;
     88	int page_size;
     89
     90	page_size = sysconf(_SC_PAGESIZE);
     91
     92	/* Map one page and check it's not memory-resident */
     93	errno = 0;
     94	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
     95			MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     96	ASSERT_NE(MAP_FAILED, addr) {
     97		TH_LOG("mmap error: %s", strerror(errno));
     98	}
     99	retval = mincore(addr, page_size, vec);
    100	ASSERT_EQ(0, retval);
    101	ASSERT_EQ(0, vec[0]) {
    102		TH_LOG("Page found in memory before use");
    103	}
    104
    105	/* Touch the page and check again. It should now be in memory */
    106	addr[0] = 1;
    107	mlock(addr, page_size);
    108	retval = mincore(addr, page_size, vec);
    109	ASSERT_EQ(0, retval);
    110	ASSERT_EQ(1, vec[0]) {
    111		TH_LOG("Page not found in memory after use");
    112	}
    113
    114	/*
    115	 * It shouldn't be memory-resident after unlocking it and
    116	 * marking it as unneeded.
    117	 */
    118	munlock(addr, page_size);
    119	madvise(addr, page_size, MADV_DONTNEED);
    120	retval = mincore(addr, page_size, vec);
    121	ASSERT_EQ(0, retval);
    122	ASSERT_EQ(0, vec[0]) {
    123		TH_LOG("Page in memory after being zapped");
    124	}
    125	munmap(addr, page_size);
    126}
    127
    128
    129/*
    130 * Check mincore() behavior on huge pages.
    131 * This test will be skipped if the mapping fails (ie. if there are no
    132 * huge pages available).
    133 *
    134 * Make sure the system has at least one free huge page, check
    135 * "HugePages_Free" in /proc/meminfo.
    136 * Increment /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages if
    137 * needed.
    138 */
    139TEST(check_huge_pages)
    140{
    141	unsigned char vec[1];
    142	char *addr;
    143	int retval;
    144	int page_size;
    145
    146	page_size = sysconf(_SC_PAGESIZE);
    147
    148	errno = 0;
    149	addr = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
    150		MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
    151		-1, 0);
    152	if (addr == MAP_FAILED) {
    153		if (errno == ENOMEM)
    154			SKIP(return, "No huge pages available.");
    155		else
    156			TH_LOG("mmap error: %s", strerror(errno));
    157	}
    158	retval = mincore(addr, page_size, vec);
    159	ASSERT_EQ(0, retval);
    160	ASSERT_EQ(0, vec[0]) {
    161		TH_LOG("Page found in memory before use");
    162	}
    163
    164	addr[0] = 1;
    165	mlock(addr, page_size);
    166	retval = mincore(addr, page_size, vec);
    167	ASSERT_EQ(0, retval);
    168	ASSERT_EQ(1, vec[0]) {
    169		TH_LOG("Page not found in memory after use");
    170	}
    171
    172	munlock(addr, page_size);
    173	munmap(addr, page_size);
    174}
    175
    176
    177/*
    178 * Test mincore() behavior on a file-backed page.
    179 * No pages should be loaded into memory right after the mapping. Then,
    180 * accessing any address in the mapping range should load the page
    181 * containing the address and a number of subsequent pages (readahead).
    182 *
    183 * The actual readahead settings depend on the test environment, so we
    184 * can't make a lot of assumptions about that. This test covers the most
    185 * general cases.
    186 */
    187TEST(check_file_mmap)
    188{
    189	unsigned char *vec;
    190	int vec_size;
    191	char *addr;
    192	int retval;
    193	int page_size;
    194	int fd;
    195	int i;
    196	int ra_pages = 0;
    197
    198	page_size = sysconf(_SC_PAGESIZE);
    199	vec_size = FILE_SIZE / page_size;
    200	if (FILE_SIZE % page_size)
    201		vec_size++;
    202
    203	vec = calloc(vec_size, sizeof(unsigned char));
    204	ASSERT_NE(NULL, vec) {
    205		TH_LOG("Can't allocate array");
    206	}
    207
    208	errno = 0;
    209	fd = open(".", O_TMPFILE | O_RDWR, 0600);
    210	if (fd < 0) {
    211		ASSERT_EQ(errno, EOPNOTSUPP) {
    212			TH_LOG("Can't create temporary file: %s",
    213			       strerror(errno));
    214		}
    215		SKIP(goto out_free, "O_TMPFILE not supported by filesystem.");
    216	}
    217	errno = 0;
    218	retval = fallocate(fd, 0, 0, FILE_SIZE);
    219	if (retval) {
    220		ASSERT_EQ(errno, EOPNOTSUPP) {
    221			TH_LOG("Error allocating space for the temporary file: %s",
    222			       strerror(errno));
    223		}
    224		SKIP(goto out_close, "fallocate not supported by filesystem.");
    225	}
    226
    227	/*
    228	 * Map the whole file, the pages shouldn't be fetched yet.
    229	 */
    230	errno = 0;
    231	addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
    232			MAP_SHARED, fd, 0);
    233	ASSERT_NE(MAP_FAILED, addr) {
    234		TH_LOG("mmap error: %s", strerror(errno));
    235	}
    236	retval = mincore(addr, FILE_SIZE, vec);
    237	ASSERT_EQ(0, retval);
    238	for (i = 0; i < vec_size; i++) {
    239		ASSERT_EQ(0, vec[i]) {
    240			TH_LOG("Unexpected page in memory");
    241		}
    242	}
    243
    244	/*
    245	 * Touch a page in the middle of the mapping. We expect the next
    246	 * few pages (the readahead window) to be populated too.
    247	 */
    248	addr[FILE_SIZE / 2] = 1;
    249	retval = mincore(addr, FILE_SIZE, vec);
    250	ASSERT_EQ(0, retval);
    251	ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
    252		TH_LOG("Page not found in memory after use");
    253	}
    254
    255	i = FILE_SIZE / 2 / page_size + 1;
    256	while (i < vec_size && vec[i]) {
    257		ra_pages++;
    258		i++;
    259	}
    260	EXPECT_GT(ra_pages, 0) {
    261		TH_LOG("No read-ahead pages found in memory");
    262	}
    263
    264	EXPECT_LT(i, vec_size) {
    265		TH_LOG("Read-ahead pages reached the end of the file");
    266	}
    267	/*
    268	 * End of the readahead window. The rest of the pages shouldn't
    269	 * be in memory.
    270	 */
    271	if (i < vec_size) {
    272		while (i < vec_size && !vec[i])
    273			i++;
    274		EXPECT_EQ(vec_size, i) {
    275			TH_LOG("Unexpected page in memory beyond readahead window");
    276		}
    277	}
    278
    279	munmap(addr, FILE_SIZE);
    280out_close:
    281	close(fd);
    282out_free:
    283	free(vec);
    284}
    285
    286
    287/*
    288 * Test mincore() behavior on a page backed by a tmpfs file.  This test
    289 * performs the same steps as the previous one. However, we don't expect
    290 * any readahead in this case.
    291 */
    292TEST(check_tmpfs_mmap)
    293{
    294	unsigned char *vec;
    295	int vec_size;
    296	char *addr;
    297	int retval;
    298	int page_size;
    299	int fd;
    300	int i;
    301	int ra_pages = 0;
    302
    303	page_size = sysconf(_SC_PAGESIZE);
    304	vec_size = FILE_SIZE / page_size;
    305	if (FILE_SIZE % page_size)
    306		vec_size++;
    307
    308	vec = calloc(vec_size, sizeof(unsigned char));
    309	ASSERT_NE(NULL, vec) {
    310		TH_LOG("Can't allocate array");
    311	}
    312
    313	errno = 0;
    314	fd = open("/dev/shm", O_TMPFILE | O_RDWR, 0600);
    315	ASSERT_NE(-1, fd) {
    316		TH_LOG("Can't create temporary file: %s",
    317			strerror(errno));
    318	}
    319	errno = 0;
    320	retval = fallocate(fd, 0, 0, FILE_SIZE);
    321	ASSERT_EQ(0, retval) {
    322		TH_LOG("Error allocating space for the temporary file: %s",
    323			strerror(errno));
    324	}
    325
    326	/*
    327	 * Map the whole file, the pages shouldn't be fetched yet.
    328	 */
    329	errno = 0;
    330	addr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE,
    331			MAP_SHARED, fd, 0);
    332	ASSERT_NE(MAP_FAILED, addr) {
    333		TH_LOG("mmap error: %s", strerror(errno));
    334	}
    335	retval = mincore(addr, FILE_SIZE, vec);
    336	ASSERT_EQ(0, retval);
    337	for (i = 0; i < vec_size; i++) {
    338		ASSERT_EQ(0, vec[i]) {
    339			TH_LOG("Unexpected page in memory");
    340		}
    341	}
    342
    343	/*
    344	 * Touch a page in the middle of the mapping. We expect only
    345	 * that page to be fetched into memory.
    346	 */
    347	addr[FILE_SIZE / 2] = 1;
    348	retval = mincore(addr, FILE_SIZE, vec);
    349	ASSERT_EQ(0, retval);
    350	ASSERT_EQ(1, vec[FILE_SIZE / 2 / page_size]) {
    351		TH_LOG("Page not found in memory after use");
    352	}
    353
    354	i = FILE_SIZE / 2 / page_size + 1;
    355	while (i < vec_size && vec[i]) {
    356		ra_pages++;
    357		i++;
    358	}
    359	ASSERT_EQ(ra_pages, 0) {
    360		TH_LOG("Read-ahead pages found in memory");
    361	}
    362
    363	munmap(addr, FILE_SIZE);
    364	close(fd);
    365	free(vec);
    366}
    367
    368TEST_HARNESS_MAIN