Home | History | Annotate | Download | only in madvise
      1 // SPDX-License-Identifier: GPL-2.0 or later
      2 /*
      3  *  Copyright (c) Zilogic Systems Pvt. Ltd., 2018
      4  *  Email : code (at) zilogic.com
      5  *
      6  * This program is free software;  you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License as published by
      8  * the Free Software Foundation; either version 2 of the License, or
      9  * (at your option) any later version.
     10  *
     11  * This program is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     14  * the GNU General Public License for more details.
     15  */
     16 
     17 /*
     18  * test cases for madvise(2) system call, advise value as "MADV_WIPEONFORK".
     19  *
     20  * DESCRIPTION
     21  * Present the child process with zero-filled memory in this
     22  * range after a fork(2).
     23  * The MADV_WIPEONFORK operation can be applied only to
     24  * private anonymous pages.
     25  * Within the child created by fork(2), the MADV_WIPEONFORK
     26  * setting remains in place on the specified map_address range.
     27  * The MADV_KEEPONFORK operation undo the effect of MADV_WIPEONFORK.
     28  *
     29  * Test-Case 1 : madvise with "MADV_WIPEONFORK"
     30  * flow :	Map memory area as private anonymous page.
     31  *		Mark memory area as wipe-on-fork.
     32  *		On fork, child process memory should be zeroed.
     33  *
     34  * Test-Case 2 : madvise with "MADV_WIPEONFORK" and "ZERO" length
     35  * flow :	Map memory area as private anonymous page.
     36  *		Mark memory area as wipe-on-fork, with length zero.
     37  *		On fork, child process memory should be accessible.
     38  *
     39  * Test-Case 3 : "MADV_WIPEONFORK" on Grand child
     40  * flow :	Map memory area as private anonymous.
     41  *		Mark memory areas as wipe-on-fork.
     42  *		On fork, child process memory should be zeroed.
     43  *		In child, fork to create grand-child,
     44  *		memory should be zeroed.
     45  *
     46  * Test-Case 4 : Undo "MADV_WIPEONFORK" by "MADV_KEEPONFORK"
     47  * flow :	Map memory area as private anonymous page.
     48  *		Mark memory area as wipe-on-fork.
     49  *		Mark memory area as keep-on-fork.
     50  *		On fork, child process memory should be retained.
     51  **/
     52 
     53 #include <stdio.h>
     54 #include <errno.h>
     55 #include <unistd.h>
     56 #include <stdlib.h>
     57 
     58 #include "lapi/mmap.h"
     59 #include "tst_test.h"
     60 #include "tst_safe_macros.h"
     61 
     62 #define MAP_SIZE (16 * 1024)
     63 
     64 static char pattern[MAP_SIZE];
     65 static char zero[MAP_SIZE];
     66 
     67 static const struct test_case {
     68 	int size;
     69 	int advise1;
     70 	int advise2;
     71 	char *exp;
     72 	int grand_child;
     73 	const char *desc;
     74 } tcases[] = {
     75 	{MAP_SIZE, MADV_NORMAL,	MADV_WIPEONFORK, zero,    0,
     76 	"MADV_WIPEONFORK zeroes memory in child"},
     77 	{0,	   MADV_NORMAL, MADV_WIPEONFORK, pattern, 0,
     78 	"MADV_WIPEONFORK with zero length does nothing"},
     79 	{MAP_SIZE, MADV_NORMAL, MADV_WIPEONFORK, zero,    1,
     80 	"MADV_WIPEONFORK zeroes memory in grand-child"},
     81 	{MAP_SIZE, MADV_WIPEONFORK, MADV_KEEPONFORK, pattern, 0,
     82 	"MADV_KEEPONFORK will undo MADV_WIPEONFORK"},
     83 };
     84 
     85 static void cmp_area(char *addr, const struct test_case *tc)
     86 {
     87 	int i;
     88 
     89 	for (i = 0; i < tc->size; i++) {
     90 		if (addr[i] != tc->exp[i]) {
     91 			tst_res(TFAIL, "In PID %d, addr[%d] = 0x%02x, "
     92 				"expected[%d] = 0x%02x", getpid(),
     93 				i, addr[i], i, tc->exp[i]);
     94 			break;
     95 		}
     96 	}
     97 
     98 	tst_res(TPASS, "In PID %d, Matched expected pattern", getpid());
     99 }
    100 
    101 static int set_advice(char *addr, int size, int advise)
    102 {
    103 	TEST(madvise(addr, size, advise));
    104 
    105 	if (TST_RET == -1) {
    106 		if (TST_ERR == EINVAL) {
    107 			tst_res(TCONF, "madvise(%p, %d, 0x%x) is not supported",
    108 			addr, size, advise);
    109 		} else {
    110 			tst_res(TFAIL | TTERRNO, "madvise(%p, %d, 0x%x)",
    111 			addr, size, advise);
    112 		}
    113 
    114 		return 1;
    115 	}
    116 
    117 	tst_res(TPASS, "madvise(%p, %d, 0x%x)",	addr, size, advise);
    118 	return 0;
    119 }
    120 
    121 static char *mem_map(void)
    122 {
    123 	char *ptr;
    124 
    125 	ptr = SAFE_MMAP(NULL, MAP_SIZE,
    126 			PROT_READ | PROT_WRITE,
    127 			MAP_PRIVATE | MAP_ANONYMOUS,
    128 			-1, 0);
    129 
    130 	memcpy(ptr, pattern, MAP_SIZE);
    131 
    132 	return ptr;
    133 }
    134 
    135 static void test_madvise(unsigned int test_nr)
    136 {
    137 	const struct test_case *tc = &tcases[test_nr];
    138 	char *addr;
    139 	pid_t pid;
    140 
    141 	addr = mem_map();
    142 
    143 	tst_res(TINFO, "%s", tc->desc);
    144 	if (set_advice(addr, tc->size, tc->advise1))
    145 		goto un_map;
    146 
    147 	if (!set_advice(addr, tc->size, tc->advise2)) {
    148 		pid = SAFE_FORK();
    149 
    150 		if (!pid) {
    151 			if (tc->grand_child) {
    152 				pid = SAFE_FORK();
    153 
    154 				if (!pid) {
    155 					cmp_area(addr, tc);
    156 					exit(0);
    157 				}
    158 			} else {
    159 				cmp_area(addr, tc);
    160 				exit(0);
    161 			}
    162 		}
    163 		tst_reap_children();
    164 	}
    165 
    166 un_map:
    167 	SAFE_MUNMAP(addr, MAP_SIZE);
    168 }
    169 
    170 static void setup(void)
    171 {
    172 	unsigned int i;
    173 
    174 	for (i = 0; i < MAP_SIZE; i++)
    175 		pattern[i] = i % 0x03;
    176 }
    177 
    178 static struct tst_test test = {
    179 	.tcnt = ARRAY_SIZE(tcases),
    180 	.forks_child = 1,
    181 	.test = test_madvise,
    182 	.setup = setup,
    183 };
    184