Message ID | 1531324792-16559-1-git-send-email-kewal@zilogic.com |
---|---|
State | Changes Requested |
Headers | show |
Series | [v1] Test madvise with advise value 'MADV_WIPEONFORK' | expand |
On 07/11/2018 05:59 PM, kewal wrote: > > * madvise10.c :- Present the child process with zero-filled memory in > this range after a fork(2). > > Test-Case 1 : madvise with 'MADV_WIPEONFORK' > Test-Case 2 : madvise with 'MADV_WIPEONFORK' as size 'ZERO' > > * madvise11.c:- The MADV_WIPEONFORK operation can be applied only to > private anonymous pages. > > Test-Case 1 : mmap with 'MAP_SHARED | MAP_ANONYMOUS' > Test-Case 2 : mmap with 'MAP_PRIVATE' > > * madvise12.c:- Within the child created by fork(2), the MADV_WIPEONFORK > setting remains in place on the specified address range. > > Test-Case 1: 'MADV_WIPEONFORK' on Grand child > > * madvise13.c:- MADV_KEEPONFORK Undo the effect of an earlier MADV_WIPEONFORK > > Test-Case 1 : Undo 'MADV_WIPEONFORK' by 'MADV_KEEPONFORK' Hi, These tests seem to have lot of common code. We should simplify that by a header/include or combining some of them together. I'm thinking: - drop madvise11, errno tests can be added to existing madvise02 - combine the rest together, see my attached example Other comments are below/inline: > > Signed-off-by: Subash Ganesan <subash@zilogic.com> > Signed-off-by: Kewal Ukunde <kewal@zilogic.com> > --- > runtest/syscalls | 4 + New tests should be added also to respective .gitignore > testcases/kernel/syscalls/madvise/madvise10.c | 179 ++++++++++++++++++++++++++ > testcases/kernel/syscalls/madvise/madvise11.c | 132 +++++++++++++++++++ > testcases/kernel/syscalls/madvise/madvise12.c | 171 ++++++++++++++++++++++++ > testcases/kernel/syscalls/madvise/madvise13.c | 162 +++++++++++++++++++++++ > 5 files changed, 648 insertions(+) > create mode 100644 testcases/kernel/syscalls/madvise/madvise10.c > create mode 100644 testcases/kernel/syscalls/madvise/madvise11.c > create mode 100644 testcases/kernel/syscalls/madvise/madvise12.c > create mode 100644 testcases/kernel/syscalls/madvise/madvise13.c > > diff --git a/runtest/syscalls b/runtest/syscalls > index a9afecf57..a95c47c3a 100644 > --- a/runtest/syscalls > +++ b/runtest/syscalls > @@ -773,6 +773,10 @@ madvise06 madvise06 > madvise07 madvise07 > madvise08 madvise08 > madvise09 madvise09 > +madvise10 madvise10 > +madvise11 madvise11 > +madvise12 madvise12 > +madvise13 madvise13 > > newuname01 newuname01 > > diff --git a/testcases/kernel/syscalls/madvise/madvise10.c b/testcases/kernel/syscalls/madvise/madvise10.c > new file mode 100644 > index 000000000..1cf3097b0 > --- /dev/null > +++ b/testcases/kernel/syscalls/madvise/madvise10.c > @@ -0,0 +1,179 @@ > +// SPDX-License-Identifier: GPL-2.0 or later > +/* > + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 2018 > + * Email : code@zilogic.com > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See > + * the GNU General Public License for more details. > + */ > + > +/* > + * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK". > + * > + * DESCRIPTION : > + * Present the child process with zero-filled memory in this > + * range after a fork(2). > + * The MADV_WIPEONFORK operation can be applied only to > + * private anonymous pages. > + * > + * Test-Case 1 : madvise with "MADV_WIPEONFORK" > + * flow : Map memory area as private anonymous page. > + * Mark memory area as wipe-on-fork. > + * On fork, child process memory should be zeroed. > + * > + * Test-Case 2 : madvise with "MADV_WIPEONFORK" as size "ZERO" > + * flow : Map memory area as private anonymous page. > + * Mark memory area as wipe-on-fork, with length zero. > + * On fork, child process memory should be accessible. > + **/ > + > +#include <stdio.h> > +#include <signal.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <stdlib.h> > +#include <fcntl.h> > +#include <sys/mman.h> > +#include <error.h> > +#include <string.h> > +#include <errno.h> > +#include <unistd.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <stdlib.h> > + > +#include "lapi/mmap.h" > +#include "tst_test.h" > +#include "tst_safe_macros.h" > + > +#define TEST_FILE "test_file.txt" Why do you need a file when you're mapping as ANONYMOUS? > +#define MAP_SIZE (4 * 1024) > + > +static int test_file_fd; > + > +static const struct test_case { > + int size; > + int advise; > + char *advise_name; > + int expect; > +} tcases[] = { > + {.size = MAP_SIZE, > + .advise = MADV_WIPEONFORK, > + .advise_name = "MADV_WIPEONFORK", > + .expect = 0}, > + > + {.size = 0, > + .advise = MADV_WIPEONFORK, > + .advise_name = "MADV_WIPEONFORK", > + .expect = 1} > +}; > + > +static void assert_equal(int status, const struct test_case *tc) > +{ > + if (status == tc->expect) > + tst_res(TPASS, "In child data %d : madvise(%s)", > + tc->expect, tc->advise_name); > + else > + tst_res(TFAIL, "In child data %d : madvise(%s)", > + tc->expect, tc->advise_name); > +} Let's compare the whole area, not just first byte. > + > +static void safe_wait_status(int *status) > +{ > + SAFE_WAIT(status); > + > + if (!WIFEXITED(*status)) { > + if (WTERMSIG(*status) == SIGSEGV) > + tst_res(TFAIL, "Invalid access : SIGSEGV"); > + else > + tst_res(TFAIL, "Abnormal termination"); > + } else { > + *status = WEXITSTATUS(*status); > + } > +} You can use tst_res(TPASS/TFAIL,...) in child processes. Then you can avoid passing values over exit status and use tst_reap_children(), which does the same check. > + > +static void spawn_child(char *map_addr) > +{ > + pid_t pid; > + > + pid = SAFE_FORK(); > + > + if (!pid) > + exit(*map_addr); As mentioned earlier, let's check entire area. > +} > + > +static int set_advice(char *map_addr, const struct test_case *tc) > +{ > + TEST(madvise(map_addr, tc->size, tc->advise)); > + > + if (TEST_RETURN == -1) { > + tst_res(TINFO, "failed :madvise(%p, %d, %s)", > + map_addr, tc->size, tc->advise_name); Why not TFAIL|TTERRNO? > + return 1; > + } > + > + tst_res(TINFO, "success :madvise(%p, %d, %s)", > + map_addr, tc->size, tc->advise_name); > + > + return 0; > +} > + > +static void mem_map(char **map_addr) > +{ > + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, > + PROT_READ | PROT_WRITE, > + MAP_PRIVATE | MAP_ANON, MAP_ANON is deprecated, please use MAP_ANONYMOUS. > + test_file_fd, 0); What is the purpose of having this file? > + > + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", > + MAP_SIZE, "MAP_PRIVATE | MAP_ANON", test_file_fd); > +} > + > +static void test_madvise(unsigned int test_nr) > +{ > + const struct test_case *tc = &tcases[test_nr]; > + char *map_addr; > + int status; > + > + mem_map(&map_addr); > + memset(map_addr, 1, MAP_SIZE); > + > + if (set_advice(map_addr, tc)) { > + tst_res(TFAIL, "madvise failed"); Please, print also errno code. > + } else { > + spawn_child(map_addr); > + safe_wait_status(&status); > + assert_equal(status, tc); > + } > + > + SAFE_MUNMAP(map_addr, MAP_SIZE); > +} > + > +static void cleanup(void) > +{ > + SAFE_CLOSE(test_file_fd); > + SAFE_UNLINK(TEST_FILE); > +} > + > +static void setup(void) > +{ > + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); > +} > + > +static struct tst_test test = { > + .tcnt = ARRAY_SIZE(tcases), > + .needs_root = 1, Is root really needed? > + .forks_child = 1, > + .needs_tmpdir = 1, > + .test = test_madvise, > + .setup = setup, > + .cleanup = cleanup, > + .min_kver = "4.14", > +}; Similar comments apply to other tests, which share code above. Regards, Jan #include <errno.h> #include <stdio.h> #include <stdio.h> #include <sys/mman.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include "lapi/mmap.h" #include "tst_test.h" #include "tst_safe_macros.h" #define MAP_SIZE (4 * 1024) static unsigned char pattern[MAP_SIZE]; static unsigned char zero[MAP_SIZE]; static void cmp_area(unsigned char *p, unsigned char *expected, unsigned int size) { unsigned int i; for (i = 0; i < size; i++) { if (p[i] != expected[i]) { tst_res(TFAIL, "area %p [%d] != expected[%d]," " 0x%02x != 0x%02x", p, i, i, p[i], expected[i]); break; } } tst_res(TPASS, "(pid %d) area %p matches expected pattern", getpid(), p); } static void *mem_map(void) { unsigned char *p; p = SAFE_MMAP(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); memcpy(p, pattern, MAP_SIZE); return p; } static int set_advice(unsigned char *p, int length, int advice) { TEST(madvise(p, length, advice)); if (TEST_RETURN == -1) { tst_res(TFAIL|TTERRNO, "madvise(%p, %d, 0x%x)", p, length, advice); return 1; } return 0; } static void test_wipe(int zero_length, int undo_wipe) { unsigned char *map_addr; pid_t pid; int wipe_length = MAP_SIZE; unsigned char *expected = zero; if (zero_length) wipe_length = 0; if (zero_length || undo_wipe) expected = pattern; tst_res(TINFO, "WIPEONFORK zero_length: %d, undo_wipe: %d", zero_length, undo_wipe); map_addr = mem_map(); set_advice(map_addr, wipe_length, MADV_WIPEONFORK); if (undo_wipe) set_advice(map_addr, wipe_length, MADV_KEEPONFORK); pid = SAFE_FORK(); if (!pid) { cmp_area(map_addr, expected, MAP_SIZE); /* run same check for grandchild */ pid = SAFE_FORK(); if (!pid) { cmp_area(map_addr, expected, MAP_SIZE); exit(0); } exit(0); } tst_reap_children(); SAFE_MUNMAP(map_addr, MAP_SIZE); } static void test_all(void) { int zero_length, undo_wipe; for (zero_length = 0; zero_length < 2; zero_length++) for (undo_wipe = 0; undo_wipe < 2; undo_wipe++) test_wipe(zero_length, undo_wipe); } static void setup(void) { unsigned int i; for (i = 0; i < MAP_SIZE; i++) pattern[i] = i % 0xfe; } static struct tst_test test = { .test_all = test_all, .forks_child = 1, .setup = setup, .min_kver = "4.14", };
Hi Jan, As per your below comment, > -drop madvise11, errno tests can be added to existing madvise02 we found that in madvise02, the below structure uses mapped address which is mapped with the flag value as "MAP_SHARED" but as per my test cases we have to vary the flag value with "MAP_SHARED | MAP_ANONYMOUS" & "MAP_PRIVATE". I didn't found a way to add our testcases in madvise02. >static struct tcase { > int advice; > char *name; > char **addr; > int exp_errno; > int skip; >} tcases[] = { > {MADV_NORMAL, "MADV_NORMAL", &nonalign, EINVAL, 0}, > {1212, "MADV_NORMAL", &file1, EINVAL, 0}, > {MADV_REMOVE, "MADV_REMOVE", &file1, EINVAL, 0}, > {MADV_DONTNEED, "MADV_DONTNEED", &file1, EINVAL, 1}, > {MADV_MERGEABLE, "MADV_MERGEABLE", &file1, EINVAL, 0}, > {MADV_UNMERGEABLE, "MADV_UNMERGEABLE", &file1, EINVAL, 0}, > {MADV_NORMAL, "MADV_NORMAL", &file2, ENOMEM, 0}, > {MADV_WILLNEED, "MADV_WILLNEED", &file2, ENOMEM, 0}, > {MADV_WILLNEED, "MADV_WILLNEED", &tmp_addr, EBADF, 0}, > {MADV_FREE, "MADV_FREE", &file1, EINVAL, 0}, > {MADV_WIPEONFORK, "MADV_WIPEONFORK", &file1, EINVAL, 0}, >}; > file1 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); > file2 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); We will go through the rest of the comments and update with v2. Regards, Kewal Ukunde > On 07/11/2018 05:59 PM, kewal wrote: >> >> * madvise10.c :- Present the child process with zero-filled memory in >> this range after a fork(2). >> >> Test-Case 1 : madvise with 'MADV_WIPEONFORK' >> Test-Case 2 : madvise with 'MADV_WIPEONFORK' as size 'ZERO' >> >> * madvise11.c:- The MADV_WIPEONFORK operation can be applied only to >> private anonymous pages. >> >> Test-Case 1 : mmap with 'MAP_SHARED | MAP_ANONYMOUS' >> Test-Case 2 : mmap with 'MAP_PRIVATE' >> >> * madvise12.c:- Within the child created by fork(2), the MADV_WIPEONFORK >> setting remains in place on the specified address range. >> >> Test-Case 1: 'MADV_WIPEONFORK' on Grand child >> >> * madvise13.c:- MADV_KEEPONFORK Undo the effect of an earlier >> MADV_WIPEONFORK >> >> Test-Case 1 : Undo 'MADV_WIPEONFORK' by 'MADV_KEEPONFORK' > > Hi, > > These tests seem to have lot of common code. We should simplify > that by a header/include or combining some of them together. > > I'm thinking: > - drop madvise11, errno tests can be added to existing madvise02 > - combine the rest together, see my attached example > > Other comments are below/inline: > >> >> Signed-off-by: Subash Ganesan <subash@zilogic.com> >> Signed-off-by: Kewal Ukunde <kewal@zilogic.com> >> --- >> runtest/syscalls | 4 + > > New tests should be added also to respective .gitignore > >> testcases/kernel/syscalls/madvise/madvise10.c | 179 >> ++++++++++++++++++++++++++ >> testcases/kernel/syscalls/madvise/madvise11.c | 132 +++++++++++++++++++ >> testcases/kernel/syscalls/madvise/madvise12.c | 171 >> ++++++++++++++++++++++++ >> testcases/kernel/syscalls/madvise/madvise13.c | 162 >> +++++++++++++++++++++++ >> 5 files changed, 648 insertions(+) >> create mode 100644 testcases/kernel/syscalls/madvise/madvise10.c >> create mode 100644 testcases/kernel/syscalls/madvise/madvise11.c >> create mode 100644 testcases/kernel/syscalls/madvise/madvise12.c >> create mode 100644 testcases/kernel/syscalls/madvise/madvise13.c >> >> diff --git a/runtest/syscalls b/runtest/syscalls >> index a9afecf57..a95c47c3a 100644 >> --- a/runtest/syscalls >> +++ b/runtest/syscalls >> @@ -773,6 +773,10 @@ madvise06 madvise06 >> madvise07 madvise07 >> madvise08 madvise08 >> madvise09 madvise09 >> +madvise10 madvise10 >> +madvise11 madvise11 >> +madvise12 madvise12 >> +madvise13 madvise13 >> >> newuname01 newuname01 >> >> diff --git a/testcases/kernel/syscalls/madvise/madvise10.c >> b/testcases/kernel/syscalls/madvise/madvise10.c >> new file mode 100644 >> index 000000000..1cf3097b0 >> --- /dev/null >> +++ b/testcases/kernel/syscalls/madvise/madvise10.c >> @@ -0,0 +1,179 @@ >> +// SPDX-License-Identifier: GPL-2.0 or later >> +/* >> + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 > > 2018 > >> + * Email : code@zilogic.com >> + * >> + * This program is free software; you can redistribute it and/or >> modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See >> + * the GNU General Public License for more details. >> + */ >> + >> +/* >> + * test cases for madvise(2) system call, advise value as >> "MADV_WIPEONFORK". >> + * >> + * DESCRIPTION : >> + * Present the child process with zero-filled memory in this >> + * range after a fork(2). >> + * The MADV_WIPEONFORK operation can be applied only to >> + * private anonymous pages. >> + * >> + * Test-Case 1 : madvise with "MADV_WIPEONFORK" >> + * flow : Map memory area as private anonymous page. >> + * Mark memory area as wipe-on-fork. >> + * On fork, child process memory should be zeroed. >> + * >> + * Test-Case 2 : madvise with "MADV_WIPEONFORK" as size "ZERO" >> + * flow : Map memory area as private anonymous page. >> + * Mark memory area as wipe-on-fork, with length zero. >> + * On fork, child process memory should be accessible. >> + **/ >> + >> +#include <stdio.h> >> +#include <signal.h> >> +#include <sys/types.h> >> +#include <sys/stat.h> >> +#include <stdlib.h> >> +#include <fcntl.h> >> +#include <sys/mman.h> >> +#include <error.h> >> +#include <string.h> >> +#include <errno.h> >> +#include <unistd.h> >> +#include <sys/types.h> >> +#include <sys/wait.h> >> +#include <stdlib.h> >> + >> +#include "lapi/mmap.h" >> +#include "tst_test.h" >> +#include "tst_safe_macros.h" >> + >> +#define TEST_FILE "test_file.txt" > > Why do you need a file when you're mapping as ANONYMOUS? > >> +#define MAP_SIZE (4 * 1024) >> + >> +static int test_file_fd; >> + >> +static const struct test_case { >> + int size; >> + int advise; >> + char *advise_name; >> + int expect; >> +} tcases[] = { >> + {.size = MAP_SIZE, >> + .advise = MADV_WIPEONFORK, >> + .advise_name = "MADV_WIPEONFORK", >> + .expect = 0}, >> + >> + {.size = 0, >> + .advise = MADV_WIPEONFORK, >> + .advise_name = "MADV_WIPEONFORK", >> + .expect = 1} >> +}; >> + >> +static void assert_equal(int status, const struct test_case *tc) >> +{ >> + if (status == tc->expect) >> + tst_res(TPASS, "In child data %d : madvise(%s)", >> + tc->expect, tc->advise_name); >> + else >> + tst_res(TFAIL, "In child data %d : madvise(%s)", >> + tc->expect, tc->advise_name); >> +} > > Let's compare the whole area, not just first byte. > >> + >> +static void safe_wait_status(int *status) >> +{ >> + SAFE_WAIT(status); >> + >> + if (!WIFEXITED(*status)) { >> + if (WTERMSIG(*status) == SIGSEGV) >> + tst_res(TFAIL, "Invalid access : SIGSEGV"); >> + else >> + tst_res(TFAIL, "Abnormal termination"); >> + } else { >> + *status = WEXITSTATUS(*status); >> + } >> +} > > You can use tst_res(TPASS/TFAIL,...) in child processes. Then you > can avoid passing values over exit status and use tst_reap_children(), > which does the same check. > >> + >> +static void spawn_child(char *map_addr) >> +{ >> + pid_t pid; >> + >> + pid = SAFE_FORK(); >> + >> + if (!pid) >> + exit(*map_addr); > > As mentioned earlier, let's check entire area. > >> +} >> + >> +static int set_advice(char *map_addr, const struct test_case *tc) >> +{ >> + TEST(madvise(map_addr, tc->size, tc->advise)); >> + >> + if (TEST_RETURN == -1) { >> + tst_res(TINFO, "failed :madvise(%p, %d, %s)", >> + map_addr, tc->size, tc->advise_name); > > Why not TFAIL|TTERRNO? > >> + return 1; >> + } >> + >> + tst_res(TINFO, "success :madvise(%p, %d, %s)", >> + map_addr, tc->size, tc->advise_name); >> + >> + return 0; >> +} >> + >> +static void mem_map(char **map_addr) >> +{ >> + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, >> + PROT_READ | PROT_WRITE, >> + MAP_PRIVATE | MAP_ANON, > > MAP_ANON is deprecated, please use MAP_ANONYMOUS. > >> + test_file_fd, 0); > > What is the purpose of having this file? > >> + >> + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", >> + MAP_SIZE, "MAP_PRIVATE | MAP_ANON", test_file_fd); >> +} >> + >> +static void test_madvise(unsigned int test_nr) >> +{ >> + const struct test_case *tc = &tcases[test_nr]; >> + char *map_addr; >> + int status; >> + >> + mem_map(&map_addr); >> + memset(map_addr, 1, MAP_SIZE); >> + >> + if (set_advice(map_addr, tc)) { >> + tst_res(TFAIL, "madvise failed"); > > Please, print also errno code. > >> + } else { >> + spawn_child(map_addr); >> + safe_wait_status(&status); >> + assert_equal(status, tc); >> + } >> + >> + SAFE_MUNMAP(map_addr, MAP_SIZE); >> +} >> + >> +static void cleanup(void) >> +{ >> + SAFE_CLOSE(test_file_fd); >> + SAFE_UNLINK(TEST_FILE); >> +} >> + >> +static void setup(void) >> +{ >> + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); >> +} >> + >> +static struct tst_test test = { >> + .tcnt = ARRAY_SIZE(tcases), >> + .needs_root = 1, > > Is root really needed? > >> + .forks_child = 1, >> + .needs_tmpdir = 1, >> + .test = test_madvise, >> + .setup = setup, >> + .cleanup = cleanup, >> + .min_kver = "4.14", >> +}; > > Similar comments apply to other tests, which share code above. > > Regards, > Jan > >
On Fri, Jul 13, 2018 at 06:27:25AM -0000, kewal wrote: > > Hi Jan, > > As per your below comment, > > -drop madvise11, errno tests can be added to existing madvise02 > > we found that in madvise02, the below structure uses mapped address which > is mapped with the flag value as "MAP_SHARED" but as per my test cases we > have to vary the flag value with "MAP_SHARED | MAP_ANONYMOUS" & > "MAP_PRIVATE". > > I didn't found a way to add our testcases in madvise02. See attached patch - wouldn't that work? Regards, Jan diff --git a/testcases/kernel/syscalls/madvise/madvise02.c b/testcases/kernel/syscalls/madvise/madvise02.c index 710e46e54f..5bb2c44965 100644 --- a/testcases/kernel/syscalls/madvise/madvise02.c +++ b/testcases/kernel/syscalls/madvise/madvise02.c @@ -59,9 +59,11 @@ static struct stat st; static long pagesize; static char *file1; static char *file2; +static char *file3; static char *ptr_addr; static char *tmp_addr; static char *nonalign; +static char *shared_anon; static struct tcase { int advice; @@ -81,6 +83,8 @@ static struct tcase { {MADV_WILLNEED, "MADV_WILLNEED", &tmp_addr, EBADF, 0}, {MADV_FREE, "MADV_FREE", &file1, EINVAL, 0}, {MADV_WIPEONFORK, "MADV_WIPEONFORK", &file1, EINVAL, 0}, + {MADV_WIPEONFORK, "MADV_WIPEONFORK shared anon", &shared_anon, EINVAL, 0}, + {MADV_WIPEONFORK, "MADV_WIPEONFORK private file backed", &file3, EINVAL, 0}, }; static void tcases_filter(void) @@ -151,6 +155,8 @@ static void setup(void) file1 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); file2 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); SAFE_MUNMAP(file2 + st.st_size - pagesize, pagesize); + file3 = SAFE_MMAP(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + shared_anon = SAFE_MMAP(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); nonalign = file1 + 100;
diff --git a/runtest/syscalls b/runtest/syscalls index a9afecf57..a95c47c3a 100644 --- a/runtest/syscalls +++ b/runtest/syscalls @@ -773,6 +773,10 @@ madvise06 madvise06 madvise07 madvise07 madvise08 madvise08 madvise09 madvise09 +madvise10 madvise10 +madvise11 madvise11 +madvise12 madvise12 +madvise13 madvise13 newuname01 newuname01 diff --git a/testcases/kernel/syscalls/madvise/madvise10.c b/testcases/kernel/syscalls/madvise/madvise10.c new file mode 100644 index 000000000..1cf3097b0 --- /dev/null +++ b/testcases/kernel/syscalls/madvise/madvise10.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 or later +/* + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 + * Email : code@zilogic.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +/* + * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK". + * + * DESCRIPTION : + * Present the child process with zero-filled memory in this + * range after a fork(2). + * The MADV_WIPEONFORK operation can be applied only to + * private anonymous pages. + * + * Test-Case 1 : madvise with "MADV_WIPEONFORK" + * flow : Map memory area as private anonymous page. + * Mark memory area as wipe-on-fork. + * On fork, child process memory should be zeroed. + * + * Test-Case 2 : madvise with "MADV_WIPEONFORK" as size "ZERO" + * flow : Map memory area as private anonymous page. + * Mark memory area as wipe-on-fork, with length zero. + * On fork, child process memory should be accessible. + **/ + +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <error.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> + +#include "lapi/mmap.h" +#include "tst_test.h" +#include "tst_safe_macros.h" + +#define TEST_FILE "test_file.txt" +#define MAP_SIZE (4 * 1024) + +static int test_file_fd; + +static const struct test_case { + int size; + int advise; + char *advise_name; + int expect; +} tcases[] = { + {.size = MAP_SIZE, + .advise = MADV_WIPEONFORK, + .advise_name = "MADV_WIPEONFORK", + .expect = 0}, + + {.size = 0, + .advise = MADV_WIPEONFORK, + .advise_name = "MADV_WIPEONFORK", + .expect = 1} +}; + +static void assert_equal(int status, const struct test_case *tc) +{ + if (status == tc->expect) + tst_res(TPASS, "In child data %d : madvise(%s)", + tc->expect, tc->advise_name); + else + tst_res(TFAIL, "In child data %d : madvise(%s)", + tc->expect, tc->advise_name); +} + +static void safe_wait_status(int *status) +{ + SAFE_WAIT(status); + + if (!WIFEXITED(*status)) { + if (WTERMSIG(*status) == SIGSEGV) + tst_res(TFAIL, "Invalid access : SIGSEGV"); + else + tst_res(TFAIL, "Abnormal termination"); + } else { + *status = WEXITSTATUS(*status); + } +} + +static void spawn_child(char *map_addr) +{ + pid_t pid; + + pid = SAFE_FORK(); + + if (!pid) + exit(*map_addr); +} + +static int set_advice(char *map_addr, const struct test_case *tc) +{ + TEST(madvise(map_addr, tc->size, tc->advise)); + + if (TEST_RETURN == -1) { + tst_res(TINFO, "failed :madvise(%p, %d, %s)", + map_addr, tc->size, tc->advise_name); + return 1; + } + + tst_res(TINFO, "success :madvise(%p, %d, %s)", + map_addr, tc->size, tc->advise_name); + + return 0; +} + +static void mem_map(char **map_addr) +{ + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + test_file_fd, 0); + + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", + MAP_SIZE, "MAP_PRIVATE | MAP_ANON", test_file_fd); +} + +static void test_madvise(unsigned int test_nr) +{ + const struct test_case *tc = &tcases[test_nr]; + char *map_addr; + int status; + + mem_map(&map_addr); + memset(map_addr, 1, MAP_SIZE); + + if (set_advice(map_addr, tc)) { + tst_res(TFAIL, "madvise failed"); + } else { + spawn_child(map_addr); + safe_wait_status(&status); + assert_equal(status, tc); + } + + SAFE_MUNMAP(map_addr, MAP_SIZE); +} + +static void cleanup(void) +{ + SAFE_CLOSE(test_file_fd); + SAFE_UNLINK(TEST_FILE); +} + +static void setup(void) +{ + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .needs_root = 1, + .forks_child = 1, + .needs_tmpdir = 1, + .test = test_madvise, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.14", +}; diff --git a/testcases/kernel/syscalls/madvise/madvise11.c b/testcases/kernel/syscalls/madvise/madvise11.c new file mode 100644 index 000000000..aee277dbd --- /dev/null +++ b/testcases/kernel/syscalls/madvise/madvise11.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 or later +/* + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 + * Email : code@zilogic.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +/* + * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK". + * + * DESCRIPTION : + * Present the child process with zero-filled memory in this + * range after a fork(2). + * The MADV_WIPEONFORK operation can be applied only to + * private anonymous pages. + * + * Test-Case 1 : mmap with "MAP_SHARED | MAP_ANONYMOUS" + * flow : Map memory area as shared anonymous page. + * Mark memory area as wipe-on-fork, should fail. + * + * Test-Case 2 : mmap with "MAP_PRIVATE" + * flow : Map memory area as private file-backed. + * Mark memory area as wipe-on-fork, should fail. + **/ + +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <error.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> + +#include "lapi/mmap.h" +#include "tst_test.h" +#include "tst_safe_macros.h" + +#define TEST_FILE "test_file.txt" +#define MAP_SIZE (4 * 1024) +#define ADVISE "MADV_WIPEONFORK" + +static int test_file_fd; + +static const struct test_case { + int flag; + char *flag_name; +} tcases[] = { + {.flag = MAP_SHARED | MAP_ANON, + .flag_name = "MAP_SHARED | MAP_ANON"}, + + {.flag = MAP_PRIVATE, + .flag_name = "MAP_PRIVATE"} +}; + +static int set_advice(char *map_addr) +{ + TEST(madvise(map_addr, MAP_SIZE, MADV_WIPEONFORK)); + + if (TEST_RETURN == -1) { + tst_res(TINFO, "failed :madvise(%p, MAP_SIZE, %s)", + map_addr, ADVISE); + return 1; + } + + tst_res(TINFO, "success :madvise(%p, MAP_SIZE, %s)", + map_addr, ADVISE); + + return 0; +} + +static void mem_map(char **map_addr, const struct test_case *tc) +{ + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, + PROT_READ | PROT_WRITE, + tc->flag, + test_file_fd, 0); + + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", + MAP_SIZE, tc->flag_name, test_file_fd); +} + +static void test_madvise(unsigned int test_nr) +{ + const struct test_case *tc = &tcases[test_nr]; + char *map_addr; + + mem_map(&map_addr, tc); + + if (set_advice(map_addr)) + tst_res(TPASS, "madvise failed"); + else + tst_res(TFAIL, "madvise success"); + + SAFE_MUNMAP(map_addr, MAP_SIZE); +} + +static void cleanup(void) +{ + SAFE_CLOSE(test_file_fd); + SAFE_UNLINK(TEST_FILE); +} + +static void setup(void) +{ + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); +} + +static struct tst_test test = { + .tcnt = ARRAY_SIZE(tcases), + .needs_root = 1, + .forks_child = 1, + .test = test_madvise, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.14", +}; + diff --git a/testcases/kernel/syscalls/madvise/madvise12.c b/testcases/kernel/syscalls/madvise/madvise12.c new file mode 100644 index 000000000..7e6283d13 --- /dev/null +++ b/testcases/kernel/syscalls/madvise/madvise12.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 or later +/* + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 + * Email : code@zilogic.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +/* + * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK". + * + * DESCRIPTION : + * Present the child process with zero-filled memory in this + * range after a fork(2). + * The MADV_WIPEONFORK operation can be applied only to + * private anonymous pages. + * Within the child created by fork(2), the MADV_WIPEONFORK + * setting remains in place on the specified map_address range. + * + * Test-Case 1 : "MADV_WIPEONFORK" on Grand child + * flow : Map memory area as private anonymous. + * Mark memory areas as wipe-on-fork. + * On fork, child process memory should be zeroed. + * In child, fork to create grand-child, + * memory should be zeroed. + **/ + +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <error.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> + +#include "lapi/mmap.h" +#include "tst_test.h" +#include "tst_safe_macros.h" + +#define TEST_FILE "test_file.txt" +#define MAP_SIZE (4 * 1024) +#define EXP_VAL 0 + +static int test_file_fd; + +static void assert_equal(int status) +{ + if (status == EXP_VAL) + tst_res(TPASS, "In child data %d : madvise(%s)", + EXP_VAL, "MADV_WIPEONFORK"); + else + tst_res(TFAIL, "In child data %d : madvise(%s)", + EXP_VAL, "MADV_WIPEONFORK"); +} + +static void safe_wait_status(int *status) +{ + SAFE_WAIT(status); + + if (!WIFEXITED(*status)) { + if (WTERMSIG(*status) == SIGSEGV) + tst_res(TFAIL, "Invalid access : SIGSEGV"); + else + tst_res(TFAIL, "Abnormal termination"); + } +} + +static void spawn_grand_child(char *map_addr) +{ + pid_t pid; + + pid = SAFE_FORK(); + + if (!pid) + exit(*map_addr); +} + +static void spawn_child(char *map_addr) +{ + pid_t pid; + int status; + + pid = SAFE_FORK(); + + if (!pid) { + spawn_grand_child(map_addr); + safe_wait_status(&status); + exit(status); + } +} + +static int set_advice(char *map_addr) +{ + TEST(madvise(map_addr, MAP_SIZE, MADV_WIPEONFORK)); + + if (TEST_RETURN == -1) { + tst_res(TINFO, "failed :madvise(%p, %d, %s)", + map_addr, MAP_SIZE, "MADV_WIPEONFORK"); + return 1; + } + + tst_res(TINFO, "success :madvise(%p, %d, %s)", + map_addr, MAP_SIZE, "MADV_WIPEONFORK"); + + return 0; +} + +static void mem_map(char **map_addr) +{ + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + test_file_fd, 0); + + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", + MAP_SIZE, "MAP_PRIVATE | MAP_ANON", test_file_fd); +} + +static void test_madvise(void) +{ + int status; + char *map_addr; + + mem_map(&map_addr); + memset(map_addr, 1, MAP_SIZE); + + if (set_advice(map_addr)) { + tst_res(TFAIL, "madvise failed"); + } else { + spawn_child(map_addr); + safe_wait_status(&status); + assert_equal(status); + } + + SAFE_MUNMAP(map_addr, MAP_SIZE); +} + +static void cleanup(void) +{ + SAFE_CLOSE(test_file_fd); + SAFE_UNLINK(TEST_FILE); +} + +static void setup(void) +{ + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); +} + +static struct tst_test test = { + .needs_root = 1, + .forks_child = 1, + .needs_tmpdir = 1, + .test_all = test_madvise, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.14", +}; diff --git a/testcases/kernel/syscalls/madvise/madvise13.c b/testcases/kernel/syscalls/madvise/madvise13.c new file mode 100644 index 000000000..cc4433380 --- /dev/null +++ b/testcases/kernel/syscalls/madvise/madvise13.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 or later +/* + * Copyright (c) Zilogic Systems Pvt. Ltd., 2007 + * Email : code@zilogic.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + */ + +/* + * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK". + * + * DESCRIPTION : + * Present the child process with zero-filled memory in this + * range after a fork(2). + * The MADV_KEEPONFORK operation undo the effect of MADV_WIPEONFORK. + * + * Test-Case 1 : Undo "MADV_WIPEONFORK" by "MADV_KEEPONFORK" + * flow : Map memory area as private anonymous page. + * Mark memory area as wipe-on-fork. + * Mark memory area as keep-on-fork. + * On fork, child process memory should be retained. + **/ + +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <error.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> + +#include "lapi/mmap.h" +#include "tst_test.h" +#include "tst_safe_macros.h" + +#define EXP_VAL 1 +#define TEST_FILE "test_file.txt" +#define MAP_SIZE (4 * 1024) + +static char *map_addr; +static int test_file_fd; + +static void assert_equal(int status) +{ + if (status == EXP_VAL) + tst_res(TPASS, "In child data %d : madvise(%s)", + EXP_VAL, "MADV_KEEPONFORK"); + else + tst_res(TFAIL, "In child data %d : madvise(%s)", + EXP_VAL, "MADV_KEEPONFORK"); +} + +static void safe_wait_status(int *status) +{ + SAFE_WAIT(status); + + if (!WIFEXITED(*status)) { + if (WTERMSIG(*status) == SIGSEGV) + tst_res(TFAIL, "Invalid access : SIGSEGV"); + else + tst_res(TFAIL, "Abnormal termination"); + } else { + *status = WEXITSTATUS(*status); + } +} + +static void spawn_child(char *map_addr) +{ + pid_t pid; + + pid = SAFE_FORK(); + + if (!pid) + exit(*map_addr); +} + +static int set_advice(int size, int advise, char *adv_name) +{ + TEST(madvise(map_addr, size, advise)); + + if (TEST_RETURN == -1) { + tst_res(TINFO, "failed :madvise(%p, %d, %s)", + map_addr, size, adv_name); + return 1; + } + + tst_res(TINFO, "success :madvise(%p, %d, %s)", + map_addr, size, adv_name); + + return 0; +} + +static void test_madvise(void) +{ + int status; + + if (set_advice(MAP_SIZE, MADV_WIPEONFORK, + "MADV_WIPEONFORK")) { + tst_res(TFAIL, "madvise failed"); + return; + } + + if (set_advice(MAP_SIZE, MADV_KEEPONFORK, + "MADV_KEEPONFORK")) { + tst_res(TFAIL, "madvise failed"); + return; + } + + spawn_child(map_addr); + safe_wait_status(&status); + assert_equal(status); +} + +static void cleanup(void) +{ + SAFE_MUNMAP(map_addr, MAP_SIZE); + SAFE_CLOSE(test_file_fd); + SAFE_UNLINK(TEST_FILE); +} + +static void mem_map(char **map_addr) +{ + *map_addr = SAFE_MMAP(NULL, MAP_SIZE, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + test_file_fd, 0); + + tst_res(TINFO, "mmap(NULL, %d, PROT_READ | PROT_WRITE, %s, %d, 0)", + MAP_SIZE, "MAP_PRIVATE | MAP_ANON", test_file_fd); +} + +static void setup(void) +{ + mem_map(&map_addr); + memset(map_addr, 1, MAP_SIZE); + test_file_fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0777); +} + +static struct tst_test test = { + .needs_root = 1, + .forks_child = 1, + .needs_tmpdir = 1, + .test_all = test_madvise, + .setup = setup, + .cleanup = cleanup, + .min_kver = "4.14", +};