[v1] Test madvise with advise value 'MADV_WIPEONFORK'

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'
Related show

Commit Message

kewal July 11, 2018, 3:59 p.m.
* 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'

Signed-off-by: Subash Ganesan <subash@zilogic.com>
Signed-off-by: Kewal Ukunde <kewal@zilogic.com>
---
 runtest/syscalls                              |   4 +
 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

Comments

Jan Stancek July 12, 2018, 10:27 a.m. | #1
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",
};
kewal July 13, 2018, 6:27 a.m. | #2
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
>
>
Jan Stancek July 13, 2018, 6:53 a.m. | #3
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;

Patch

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",
+};