[LTP] [PATCH v1] Test madvise with advise value 'MADV_WIPEONFORK'
Jan Stancek
jstancek@redhat.com
Thu Jul 12 12:27:25 CEST 2018
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
-------------- next part --------------
#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",
};
More information about the ltp
mailing list