Home | History | Annotate | Download | only in mlock
      1 /*
      2  *   Copyright (c) International Business Machines  Corp., 2002
      3  *	06/2002 Written by Paul Larson
      4  *
      5  *   This program is free software;  you can redistribute it and/or modify
      6  *   it under the terms of the GNU General Public License as published by
      7  *   the Free Software Foundation; either version 2 of the License, or
      8  *   (at your option) any later version.
      9  *
     10  *   This program is distributed in the hope that it will be useful,
     11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  *   the GNU General Public License for more details.
     14  *
     15  *   You should have received a copy of the GNU General Public License
     16  *   along with this program;  if not, write to the Free Software Foundation,
     17  *   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  */
     19 
     20 /*
     21  * Test Description:
     22  *  Verify that,
     23  *   1. mlock() fails with -1 return value and sets errno to ENOMEM,
     24  *      if some of the specified address range does not correspond to
     25  *      mapped pages in the address space of the process.
     26  *   2. mlock() fails with -1 return value and sets errno to ENOMEM,
     27  *      if (Linux  2.6.9  and  later)  the caller had a non-zero RLIMIT_MEMLOCK
     28  *      soft resource limit, but tried to lock more memory than the limit
     29  *      permitted.  This limit is not enforced if the process is privileged
     30  *      (CAP_IPC_LOCK).
     31  *   3. mlock() fails with -1 return value and sets errno to EPERM,
     32  *      if (Linux 2.6.9 and later) the caller was not privileged (CAP_IPC_LOCK)
     33  *      and its RLIMIT_MEMLOCK soft resource limit was 0.
     34  */
     35 
     36 #include <errno.h>
     37 #include <unistd.h>
     38 #include <sys/mman.h>
     39 #include <pwd.h>
     40 
     41 #include "test.h"
     42 #include "safe_macros.h"
     43 
     44 char *TCID = "mlock02";
     45 
     46 #if !defined(UCLINUX)
     47 
     48 static void setup(void);
     49 static void cleanup(void);
     50 static void test_enomem1(void);
     51 static void test_enomem2(void);
     52 static void test_eperm(void);
     53 static void mlock_verify(const void *, const size_t, const int);
     54 
     55 static size_t len;
     56 static struct rlimit original;
     57 static struct passwd *ltpuser;
     58 
     59 static void (*test_func[])(void) = { test_enomem1, test_enomem2, test_eperm };
     60 
     61 int TST_TOTAL = ARRAY_SIZE(test_func);
     62 
     63 int main(int ac, char **av)
     64 {
     65 	int lc, i;
     66 
     67 	tst_parse_opts(ac, av, NULL, NULL);
     68 
     69 	setup();
     70 
     71 	for (lc = 0; TEST_LOOPING(lc); lc++) {
     72 		tst_count = 0;
     73 		for (i = 0; i < TST_TOTAL; i++)
     74 			(*test_func[i])();
     75 	}
     76 
     77 	cleanup();
     78 	tst_exit();
     79 }
     80 
     81 static void setup(void)
     82 {
     83 	tst_require_root();
     84 
     85 	tst_sig(NOFORK, DEF_HANDLER, cleanup);
     86 
     87 	TEST_PAUSE;
     88 
     89 	ltpuser = SAFE_GETPWNAM(cleanup, "nobody");
     90 
     91 	len = getpagesize();
     92 
     93 	SAFE_GETRLIMIT(cleanup, RLIMIT_MEMLOCK, &original);
     94 }
     95 
     96 static void test_enomem1(void)
     97 {
     98 	void *addr;
     99 	struct rlimit rl;
    100 
    101 	/*
    102 	 * RLIMIT_MEMLOCK resource limit.
    103 	 * In Linux kernels before 2.6.9, this limit controlled the amount
    104 	 * of  memory that could be locked by a privileged process. Since
    105 	 * Linux 2.6.9, no limits are placed on the amount of memory that a
    106 	 * privileged process may lock, and this limit instead governs the
    107 	 * amount of memory that an unprivileged process may lock. So here
    108 	 * we set RLIMIT_MEMLOCK resource limit to RLIM_INFINITY when kernel
    109 	 * is under 2.6.9, to make sure this ENOMEM error is indeed caused by
    110 	 * that some of the specified address range does not correspond to
    111 	 * mapped pages in the address space of the process.
    112 	 */
    113 	if ((tst_kvercmp(2, 6, 9)) < 0) {
    114 		rl.rlim_cur = RLIM_INFINITY;
    115 		rl.rlim_max = RLIM_INFINITY;
    116 		SAFE_SETRLIMIT(cleanup, RLIMIT_MEMLOCK, &rl);
    117 	}
    118 
    119 	addr = SAFE_MMAP(cleanup, NULL, len, PROT_READ,
    120 			 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    121 
    122 	SAFE_MUNMAP(cleanup, addr, len);
    123 
    124 	mlock_verify(addr, len, ENOMEM);
    125 }
    126 
    127 static void test_enomem2(void)
    128 {
    129 	void *addr;
    130 	struct rlimit rl;
    131 
    132 	if ((tst_kvercmp(2, 6, 9)) < 0) {
    133 		tst_resm(TCONF,
    134 			 "ENOMEM error value test for this condition needs "
    135 			 "kernel 2.6.9 or higher");
    136 		return;
    137 	}
    138 
    139 	rl.rlim_max = len - 1;
    140 	rl.rlim_cur = len - 1;
    141 	SAFE_SETRLIMIT(cleanup, RLIMIT_MEMLOCK, &rl);
    142 
    143 	addr = SAFE_MMAP(cleanup, NULL, len, PROT_READ,
    144 			 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    145 
    146 	SAFE_SETEUID(cleanup, ltpuser->pw_uid);
    147 
    148 	mlock_verify(addr, len, ENOMEM);
    149 
    150 	SAFE_SETEUID(cleanup, 0);
    151 
    152 	SAFE_MUNMAP(cleanup, addr, len);
    153 
    154 	SAFE_SETRLIMIT(cleanup, RLIMIT_MEMLOCK, &original);
    155 }
    156 
    157 static void test_eperm(void)
    158 {
    159 	void *addr;
    160 	struct rlimit rl;
    161 
    162 	if ((tst_kvercmp(2, 6, 9)) < 0) {
    163 		tst_resm(TCONF,
    164 			 "EPERM error value test needs kernel 2.6.9 or higher");
    165 		return;
    166 	}
    167 
    168 	rl.rlim_max = 0;
    169 	rl.rlim_cur = 0;
    170 	SAFE_SETRLIMIT(cleanup, RLIMIT_MEMLOCK, &rl);
    171 
    172 	addr = SAFE_MMAP(cleanup, NULL, len, PROT_READ,
    173 			 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
    174 
    175 	SAFE_SETEUID(cleanup, ltpuser->pw_uid);
    176 
    177 	mlock_verify(addr, len, EPERM);
    178 
    179 	SAFE_SETEUID(cleanup, 0);
    180 
    181 	SAFE_MUNMAP(cleanup, addr, len);
    182 
    183 	SAFE_SETRLIMIT(cleanup, RLIMIT_MEMLOCK, &original);
    184 }
    185 
    186 static void mlock_verify(const void *addr, const size_t len, const int error)
    187 {
    188 	TEST(mlock(addr, len));
    189 
    190 	if (TEST_RETURN != -1) {
    191 		tst_resm(TFAIL, "mlock succeeded unexpectedly");
    192 		return;
    193 	}
    194 
    195 	if (TEST_ERRNO != error) {
    196 		tst_resm(TFAIL | TTERRNO,
    197 			 "mlock didn't fail as expected; expected - %d : %s",
    198 			 error, strerror(error));
    199 	} else {
    200 		tst_resm(TPASS | TTERRNO, "mlock failed as expected");
    201 	}
    202 }
    203 
    204 static void cleanup(void)
    205 {
    206 }
    207 
    208 #else
    209 
    210 int TST_TOTAL = 1;
    211 
    212 int main(void)
    213 {
    214 	tst_brkm(TCONF, NULL, "test is not available on uClinux");
    215 }
    216 
    217 #endif /* if !defined(UCLINUX) */
    218