UPSTREAM: tools/selftests: add file/shmem-backed mapping guard region tests
Extend the guard region self tests to explicitly assert that guard regions work correctly for functionality specific to file-backed and shmem mappings. In addition to testing all of the existing guard region functionality that is currently tested against anonymous mappings against file-backed and shmem mappings (except those which are exclusive to anonymous mapping), we now also: * Test that MADV_SEQUENTIAL does not cause unexpected readahead behaviour. * Test that MAP_PRIVATE behaves as expected with guard regions installed in both a shared and private mapping of an fd. * Test that a read-only file can correctly establish guard regions. * Test a probable fault-around case does not interfere with guard regions (or vice-versa). * Test that truncation does not eliminate guard regions. * Test that hole punching functions as expected in the presence of guard regions. * Test that a read-only mapping of a memfd write sealed mapping can have guard regions established within it and function correctly without violation of the seal. * Test that guard regions installed into a mapping of the anonymous zero page function correctly. Link: https://lkml.kernel.org/r/90c16bec5fcaafcd1700dfa3e9988c3e1aa9ac1d.1739469950.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes <lorenzo.stoakes@oracle.com> Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: Jann Horn <jannh@google.com> Cc: John Hubbard <jhubbard@nvidia.com> Cc: Kalesh Singh <kaleshsingh@google.com> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: "Paul E . McKenney" <paulmck@kernel.org> Cc: Shuah Khan <shuah@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> (cherry picked from commit 0b6d4853d1d7d8ab65de9e6958af4d5cdf6a2b75) Bug: 402449065 Change-Id: I935bd5f63d3f6e37b236ba9d982b4f6e5411339d Signed-off-by: Suren Baghdasaryan <surenb@google.com>
This commit is contained in:
committed by
Carlos Llamas
parent
bc91eb889e
commit
df3e8432fa
@@ -213,6 +213,58 @@ static int open_file(const char *prefix, char *path)
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* Establish a varying pattern in a buffer. */
|
||||
static void set_pattern(char *ptr, size_t num_pages, size_t page_size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
char *ptr2 = &ptr[i * page_size];
|
||||
|
||||
memset(ptr2, 'a' + (i % 26), page_size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that a buffer contains the pattern set by set_pattern(), starting at a
|
||||
* page offset of pgoff within the buffer.
|
||||
*/
|
||||
static bool check_pattern_offset(char *ptr, size_t num_pages, size_t page_size,
|
||||
size_t pgoff)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < num_pages * page_size; i++) {
|
||||
size_t offset = pgoff * page_size + i;
|
||||
char actual = ptr[offset];
|
||||
char expected = 'a' + ((offset / page_size) % 26);
|
||||
|
||||
if (actual != expected)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Check that a buffer contains the pattern set by set_pattern(). */
|
||||
static bool check_pattern(char *ptr, size_t num_pages, size_t page_size)
|
||||
{
|
||||
return check_pattern_offset(ptr, num_pages, page_size, 0);
|
||||
}
|
||||
|
||||
/* Determine if a buffer contains only repetitions of a specified char. */
|
||||
static bool is_buf_eq(char *buf, size_t size, char chr)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (buf[i] != chr)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FIXTURE_SETUP(guard_regions)
|
||||
{
|
||||
self->page_size = (unsigned long)sysconf(_SC_PAGESIZE);
|
||||
@@ -1429,4 +1481,547 @@ TEST_F(guard_regions, uffd)
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark a region within a file-backed mapping using MADV_SEQUENTIAL so we
|
||||
* aggressively read-ahead, then install guard regions and assert that it
|
||||
* behaves correctly.
|
||||
*
|
||||
* We page out using MADV_PAGEOUT before checking guard regions so we drop page
|
||||
* cache folios, meaning we maximise the possibility of some broken readahead.
|
||||
*/
|
||||
TEST_F(guard_regions, madvise_sequential)
|
||||
{
|
||||
char *ptr;
|
||||
int i;
|
||||
const unsigned long page_size = self->page_size;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "MADV_SEQUENTIAL meaningful only for file-backed");
|
||||
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Establish a pattern of data in the file. */
|
||||
set_pattern(ptr, 10, page_size);
|
||||
ASSERT_TRUE(check_pattern(ptr, 10, page_size));
|
||||
|
||||
/* Mark it as being accessed sequentially. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_SEQUENTIAL), 0);
|
||||
|
||||
/* Mark every other page a guard page. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr2 = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr2, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Now page it out. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_PAGEOUT), 0);
|
||||
|
||||
/* Now make sure pages are as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *chrp = &ptr[i * page_size];
|
||||
|
||||
if (i % 2 == 0) {
|
||||
bool result = try_read_write_buf(chrp);
|
||||
|
||||
ASSERT_FALSE(result);
|
||||
} else {
|
||||
ASSERT_EQ(*chrp, 'a' + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Now remove guard pages. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/* Now make sure all data is as expected. */
|
||||
if (!check_pattern(ptr, 10, page_size))
|
||||
ASSERT_TRUE(false);
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that file-backed mappings implement guard regions with MAP_PRIVATE
|
||||
* correctly.
|
||||
*/
|
||||
TEST_F(guard_regions, map_private)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr_shared, *ptr_private;
|
||||
int i;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "MAP_PRIVATE test specific to file-backed");
|
||||
|
||||
ptr_shared = mmap_(self, variant, NULL, 10 * page_size, PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr_shared, MAP_FAILED);
|
||||
|
||||
/* Manually mmap(), do not use mmap_() wrapper so we can force MAP_PRIVATE. */
|
||||
ptr_private = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, self->fd, 0);
|
||||
ASSERT_NE(ptr_private, MAP_FAILED);
|
||||
|
||||
/* Set pattern in shared mapping. */
|
||||
set_pattern(ptr_shared, 10, page_size);
|
||||
|
||||
/* Install guard regions in every other page in the shared mapping. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr = &ptr_shared[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* Every even shared page should be guarded. */
|
||||
ASSERT_EQ(try_read_buf(&ptr_shared[i * page_size]), i % 2 != 0);
|
||||
/* Private mappings should always be readable. */
|
||||
ASSERT_TRUE(try_read_buf(&ptr_private[i * page_size]));
|
||||
}
|
||||
|
||||
/* Install guard regions in every other page in the private mapping. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr = &ptr_private[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* Every even shared page should be guarded. */
|
||||
ASSERT_EQ(try_read_buf(&ptr_shared[i * page_size]), i % 2 != 0);
|
||||
/* Every odd private page should be guarded. */
|
||||
ASSERT_EQ(try_read_buf(&ptr_private[i * page_size]), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Remove guard regions from shared mapping. */
|
||||
ASSERT_EQ(madvise(ptr_shared, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* Shared mappings should always be readable. */
|
||||
ASSERT_TRUE(try_read_buf(&ptr_shared[i * page_size]));
|
||||
/* Every even private page should be guarded. */
|
||||
ASSERT_EQ(try_read_buf(&ptr_private[i * page_size]), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Remove guard regions from private mapping. */
|
||||
ASSERT_EQ(madvise(ptr_private, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
/* Shared mappings should always be readable. */
|
||||
ASSERT_TRUE(try_read_buf(&ptr_shared[i * page_size]));
|
||||
/* Private mappings should always be readable. */
|
||||
ASSERT_TRUE(try_read_buf(&ptr_private[i * page_size]));
|
||||
}
|
||||
|
||||
/* Ensure patterns are intact. */
|
||||
ASSERT_TRUE(check_pattern(ptr_shared, 10, page_size));
|
||||
ASSERT_TRUE(check_pattern(ptr_private, 10, page_size));
|
||||
|
||||
/* Now write out every other page to MAP_PRIVATE. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr = &ptr_private[i * page_size];
|
||||
|
||||
memset(ptr, 'a' + i, page_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point the mapping is:
|
||||
*
|
||||
* 0123456789
|
||||
* SPSPSPSPSP
|
||||
*
|
||||
* Where S = shared, P = private mappings.
|
||||
*/
|
||||
|
||||
/* Now mark the beginning of the mapping guarded. */
|
||||
ASSERT_EQ(madvise(ptr_private, 5 * page_size, MADV_GUARD_INSTALL), 0);
|
||||
|
||||
/*
|
||||
* This renders the mapping:
|
||||
*
|
||||
* 0123456789
|
||||
* xxxxxPSPSP
|
||||
*/
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr = &ptr_private[i * page_size];
|
||||
|
||||
/* Ensure guard regions as expected. */
|
||||
ASSERT_EQ(try_read_buf(ptr), i >= 5);
|
||||
/* The shared mapping should always succeed. */
|
||||
ASSERT_TRUE(try_read_buf(&ptr_shared[i * page_size]));
|
||||
}
|
||||
|
||||
/* Remove the guard regions altogether. */
|
||||
ASSERT_EQ(madvise(ptr_private, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/*
|
||||
*
|
||||
* We now expect the mapping to be:
|
||||
*
|
||||
* 0123456789
|
||||
* SSSSSPSPSP
|
||||
*
|
||||
* As we removed guard regions, the private pages from the first 5 will
|
||||
* have been zapped, so on fault will reestablish the shared mapping.
|
||||
*/
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr = &ptr_private[i * page_size];
|
||||
|
||||
/*
|
||||
* Assert that shared mappings in the MAP_PRIVATE mapping match
|
||||
* the shared mapping.
|
||||
*/
|
||||
if (i < 5 || i % 2 == 0) {
|
||||
char *ptr_s = &ptr_shared[i * page_size];
|
||||
|
||||
ASSERT_EQ(memcmp(ptr, ptr_s, page_size), 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Everything else is a private mapping. */
|
||||
ASSERT_TRUE(is_buf_eq(ptr, page_size, 'a' + i));
|
||||
}
|
||||
|
||||
ASSERT_EQ(munmap(ptr_shared, 10 * page_size), 0);
|
||||
ASSERT_EQ(munmap(ptr_private, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
/* Test that guard regions established over a read-only mapping function correctly. */
|
||||
TEST_F(guard_regions, readonly_file)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "Read-only test specific to file-backed");
|
||||
|
||||
/* Map shared so we can populate with pattern, populate it, unmap. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
set_pattern(ptr, 10, page_size);
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
/* Close the fd so we can re-open read-only. */
|
||||
ASSERT_EQ(close(self->fd), 0);
|
||||
|
||||
/* Re-open read-only. */
|
||||
self->fd = open(self->path, O_RDONLY);
|
||||
ASSERT_NE(self->fd, -1);
|
||||
/* Re-map read-only. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Mark every other page guarded. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr_pg = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr_pg, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Assert that the guard regions are in place.*/
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_pg = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_pg), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Remove guard regions. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/* Ensure the data is as expected. */
|
||||
ASSERT_TRUE(check_pattern(ptr, 10, page_size));
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
TEST_F(guard_regions, fault_around)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "Fault-around test specific to file-backed");
|
||||
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Establish a pattern in the backing file. */
|
||||
set_pattern(ptr, 10, page_size);
|
||||
|
||||
/*
|
||||
* Now drop it from the page cache so we get major faults when next we
|
||||
* map it.
|
||||
*/
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_PAGEOUT), 0);
|
||||
|
||||
/* Unmap and remap 'to be sure'. */
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Now make every even page guarded. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr_p, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Now fault in every odd page. This should trigger fault-around. */
|
||||
for (i = 1; i < 10; i += 2) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_TRUE(try_read_buf(ptr_p));
|
||||
}
|
||||
|
||||
/* Finally, ensure that guard regions are intact as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
TEST_F(guard_regions, truncation)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "Truncation test specific to file-backed");
|
||||
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/*
|
||||
* Establish a pattern in the backing file, just so there is data
|
||||
* there.
|
||||
*/
|
||||
set_pattern(ptr, 10, page_size);
|
||||
|
||||
/* Now make every even page guarded. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr_p, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Now assert things are as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_write_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Now truncate to actually used size (initialised to 100). */
|
||||
ASSERT_EQ(ftruncate(self->fd, 10 * page_size), 0);
|
||||
|
||||
/* Here the guard regions will remain intact. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_write_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Now truncate to half the size, then truncate again to the full size. */
|
||||
ASSERT_EQ(ftruncate(self->fd, 5 * page_size), 0);
|
||||
ASSERT_EQ(ftruncate(self->fd, 10 * page_size), 0);
|
||||
|
||||
/* Again, guard pages will remain intact. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_write_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
TEST_F(guard_regions, hole_punch)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (variant->backing == ANON_BACKED)
|
||||
SKIP(return, "Truncation test specific to file-backed");
|
||||
|
||||
/* Establish pattern in mapping. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size,
|
||||
PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
set_pattern(ptr, 10, page_size);
|
||||
|
||||
/* Install a guard region in the middle of the mapping. */
|
||||
ASSERT_EQ(madvise(&ptr[3 * page_size], 4 * page_size,
|
||||
MADV_GUARD_INSTALL), 0);
|
||||
|
||||
/*
|
||||
* The buffer will now be:
|
||||
*
|
||||
* 0123456789
|
||||
* ***xxxx***
|
||||
*
|
||||
* Where * is data and x is the guard region.
|
||||
*/
|
||||
|
||||
/* Ensure established. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_p), i < 3 || i >= 7);
|
||||
}
|
||||
|
||||
/* Now hole punch the guarded region. */
|
||||
ASSERT_EQ(madvise(&ptr[3 * page_size], 4 * page_size,
|
||||
MADV_REMOVE), 0);
|
||||
|
||||
/* Ensure guard regions remain. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_p), i < 3 || i >= 7);
|
||||
}
|
||||
|
||||
/* Now remove guard region throughout. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/* Check that the pattern exists in non-hole punched region. */
|
||||
ASSERT_TRUE(check_pattern(ptr, 3, page_size));
|
||||
/* Check that hole punched region is zeroed. */
|
||||
ASSERT_TRUE(is_buf_eq(&ptr[3 * page_size], 4 * page_size, '\0'));
|
||||
/* Check that the pattern exists in the remainder of the file. */
|
||||
ASSERT_TRUE(check_pattern_offset(ptr, 3, page_size, 7));
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that a memfd works correctly with guard regions, that we can write
|
||||
* seal it then open the mapping read-only and still establish guard regions
|
||||
* within, remove those guard regions and have everything work correctly.
|
||||
*/
|
||||
TEST_F(guard_regions, memfd_write_seal)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (variant->backing != SHMEM_BACKED)
|
||||
SKIP(return, "memfd write seal test specific to shmem");
|
||||
|
||||
/* OK, we need a memfd, so close existing one. */
|
||||
ASSERT_EQ(close(self->fd), 0);
|
||||
|
||||
/* Create and truncate memfd. */
|
||||
self->fd = memfd_create("guard_regions_memfd_seals_test",
|
||||
MFD_ALLOW_SEALING);
|
||||
ASSERT_NE(self->fd, -1);
|
||||
ASSERT_EQ(ftruncate(self->fd, 10 * page_size), 0);
|
||||
|
||||
/* Map, set pattern, unmap. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ | PROT_WRITE, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
set_pattern(ptr, 10, page_size);
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
|
||||
/* Write-seal the memfd. */
|
||||
ASSERT_EQ(fcntl(self->fd, F_ADD_SEALS, F_SEAL_WRITE), 0);
|
||||
|
||||
/* Now map the memfd readonly. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Ensure pattern is as expected. */
|
||||
ASSERT_TRUE(check_pattern(ptr, 10, page_size));
|
||||
|
||||
/* Now make every even page guarded. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr_p, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Now assert things are as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Now remove guard regions. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/* Ensure pattern is as expected. */
|
||||
ASSERT_TRUE(check_pattern(ptr, 10, page_size));
|
||||
|
||||
/* Ensure write seal intact. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_FALSE(try_write_buf(ptr_p));
|
||||
}
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Since we are now permitted to establish guard regions in read-only anonymous
|
||||
* mappings, for the sake of thoroughness, though it probably has no practical
|
||||
* use, test that guard regions function with a mapping to the anonymous zero
|
||||
* page.
|
||||
*/
|
||||
TEST_F(guard_regions, anon_zeropage)
|
||||
{
|
||||
const unsigned long page_size = self->page_size;
|
||||
char *ptr;
|
||||
int i;
|
||||
|
||||
if (!is_anon_backed(variant))
|
||||
SKIP(return, "anon zero page test specific to anon/shmem");
|
||||
|
||||
/* Obtain a read-only i.e. anon zero page mapping. */
|
||||
ptr = mmap_(self, variant, NULL, 10 * page_size, PROT_READ, 0, 0);
|
||||
ASSERT_NE(ptr, MAP_FAILED);
|
||||
|
||||
/* Now make every even page guarded. */
|
||||
for (i = 0; i < 10; i += 2) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(madvise(ptr_p, page_size, MADV_GUARD_INSTALL), 0);
|
||||
}
|
||||
|
||||
/* Now assert things are as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_EQ(try_read_buf(ptr_p), i % 2 != 0);
|
||||
}
|
||||
|
||||
/* Now remove all guard regions. */
|
||||
ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0);
|
||||
|
||||
/* Now assert things are as expected. */
|
||||
for (i = 0; i < 10; i++) {
|
||||
char *ptr_p = &ptr[i * page_size];
|
||||
|
||||
ASSERT_TRUE(try_read_buf(ptr_p));
|
||||
}
|
||||
|
||||
/* Ensure zero page...*/
|
||||
ASSERT_TRUE(is_buf_eq(ptr, 10 * page_size, '\0'));
|
||||
|
||||
ASSERT_EQ(munmap(ptr, 10 * page_size), 0);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
||||
Reference in New Issue
Block a user