Hi Pedro
On Fri, Aug 16, 2024 at 5:18 PM Pedro Falcato pedro.falcato@gmail.com wrote:
Add more mseal traversal tests across VMAs, where we could possibly screw up sealing checks. These test more across-vma traversal for mprotect, munmap and madvise. Particularly, we test for the case where a regular VMA is followed by a sealed VMA.
Signed-off-by: Pedro Falcato pedro.falcato@gmail.com
tools/testing/selftests/mm/mseal_test.c | 111 +++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/mm/mseal_test.c b/tools/testing/selftests/mm/mseal_test.c index 259bef4945e9..0d4d40fb0f88 100644 --- a/tools/testing/selftests/mm/mseal_test.c +++ b/tools/testing/selftests/mm/mseal_test.c @@ -766,6 +766,42 @@ static void test_seal_mprotect_partial_mprotect(bool seal) REPORT_TEST_PASS(); }
+static void test_seal_mprotect_partial_mprotect_tail(bool seal) +{
void *ptr;
unsigned long page_size = getpagesize();
unsigned long size = 2 * page_size;
int ret;
int prot;
/*
* Check if a partial mseal (that results in two vmas) works correctly.
* It might mprotect the first, but it'll never touch the second (msealed) vma.
*/
setup_single_address(size, &ptr);
FAIL_TEST_IF_FALSE(ptr != (void *)-1);
if (seal) {
ret = sys_mseal(ptr + page_size, size);
you are allocating 2 pages , and I assume you are sealing the second page, so the size should be page_size. ret = sys_mseal(ptr + page_size, page_size);
FAIL_TEST_IF_FALSE(!ret);
}
ret = sys_mprotect(ptr, size, PROT_EXEC);
if (seal)
FAIL_TEST_IF_FALSE(ret < 0);
else
FAIL_TEST_IF_FALSE(!ret);
if (seal) {
FAIL_TEST_IF_FALSE(get_vma_size(ptr + page_size, &prot) > 0);
FAIL_TEST_IF_FALSE(prot == 0x4);
To test partial mprotect, the test needs to add the check for the first page to be changed, Also to avoid the merge, a PROT_NONE page can to be added in front.
}
REPORT_TEST_PASS();
+}
static void test_seal_mprotect_two_vma_with_gap(bool seal) { void *ptr; @@ -983,6 +1019,41 @@ static void test_seal_munmap_vma_with_gap(bool seal) REPORT_TEST_PASS(); }
+static void test_seal_munmap_partial_across_vmas(bool seal) +{
void *ptr;
unsigned long page_size = getpagesize();
unsigned long size = 2 * page_size;
int ret;
int prot;
/*
* Check if a partial mseal (that results in two vmas) works correctly.
* It might unmap the first, but it'll never unmap the second (msealed) vma.
*/
setup_single_address(size, &ptr);
FAIL_TEST_IF_FALSE(ptr != (void *)-1);
if (seal) {
ret = sys_mseal(ptr + page_size, size);
ret = sys_mseal(ptr + page_size, page_size);
FAIL_TEST_IF_FALSE(!ret);
}
ret = sys_munmap(ptr, size);
if (seal)
FAIL_TEST_IF_FALSE(ret < 0);
else
FAIL_TEST_IF_FALSE(!ret);
if (seal) {
FAIL_TEST_IF_FALSE(get_vma_size(ptr + page_size, &prot) > 0);
FAIL_TEST_IF_FALSE(prot == 0x4);
To test partial unmap, the test needs to add the check for the first page to be freed, Also to avoid the merge, a PROT_NONE page needs to be in front.
The test_seal_munmap_partial_across_vmas shows the behavior difference with in-loop approach and out-loop approach. Previously, both VMAs will not be freed, now the first VMA will be freed, and the second VMA (sealed) won't.
This brings to the line you previously mentioned: [1] and I quote: "munmap is atomic and always has been. It's required by POSIX."
So this is no longer true for the current series. Linux doesn't need to be POSIX compliant, from previous conversation on this topic with Linus [2], so I'm open to that. If this is accepted by Linus, it would be better to add comments on munmap code or tests, for future readers (in case they are curious about reasoning. )
[1] https://lore.kernel.org/linux-mm/CAKbZUD3_3KN4fAyQsD+=p3PV8svAvVyS278umX40Eh... [2] https://lore.kernel.org/linux-mm/CAHk-=wgDv5vPx2xoxNQh+kbvLsskWubGGGK69cqF_i...
}
REPORT_TEST_PASS();
+}
static void test_munmap_start_freed(bool seal) { void *ptr; @@ -1735,6 +1806,37 @@ static void test_seal_discard_ro_anon(bool seal) REPORT_TEST_PASS(); }
+static void test_seal_discard_across_vmas(bool seal) +{
void *ptr;
unsigned long page_size = getpagesize();
unsigned long size = 2 * page_size;
int ret;
setup_single_address(size, &ptr);
FAIL_TEST_IF_FALSE(ptr != (void *)-1);
if (seal) {
ret = seal_single_address(ptr + page_size, page_size);
FAIL_TEST_IF_FALSE(!ret);
}
ret = sys_madvise(ptr, size, MADV_DONTNEED);
if (seal)
FAIL_TEST_IF_FALSE(ret < 0);
else
FAIL_TEST_IF_FALSE(!ret);
ret = sys_munmap(ptr, size);
if (seal)
FAIL_TEST_IF_FALSE(ret < 0);
else
FAIL_TEST_IF_FALSE(!ret);
REPORT_TEST_PASS();
+}
static void test_seal_madvise_nodiscard(bool seal) { void *ptr; @@ -1779,7 +1881,7 @@ int main(int argc, char **argv) if (!pkey_supported()) ksft_print_msg("PKEY not supported\n");
ksft_set_plan(82);
ksft_set_plan(88); test_seal_addseal(); test_seal_unmapped_start();
@@ -1825,12 +1927,17 @@ int main(int argc, char **argv) test_seal_mprotect_split(false); test_seal_mprotect_split(true);
test_seal_mprotect_partial_mprotect_tail(false);
test_seal_mprotect_partial_mprotect_tail(true);
test_seal_munmap(false); test_seal_munmap(true); test_seal_munmap_two_vma(false); test_seal_munmap_two_vma(true); test_seal_munmap_vma_with_gap(false); test_seal_munmap_vma_with_gap(true);
test_seal_munmap_partial_across_vmas(false);
test_seal_munmap_partial_across_vmas(true); test_munmap_start_freed(false); test_munmap_start_freed(true);
@@ -1862,6 +1969,8 @@ int main(int argc, char **argv) test_seal_madvise_nodiscard(true); test_seal_discard_ro_anon(false); test_seal_discard_ro_anon(true);
test_seal_discard_across_vmas(false);
test_seal_discard_across_vmas(true); test_seal_discard_ro_anon_on_rw(false); test_seal_discard_ro_anon_on_rw(true); test_seal_discard_ro_anon_on_shared(false);
-- 2.46.0