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
main(int ac,char ** av)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
setup(void)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
test_enomem1(void)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
test_enomem2(void)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
test_eperm(void)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
mlock_verify(const void * addr,const size_t len,const int error)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
cleanup(void)204 static void cleanup(void)
205 {
206 }
207
208 #else
209
210 int TST_TOTAL = 1;
211
main(void)212 int main(void)
213 {
214 tst_brkm(TCONF, NULL, "test is not available on uClinux");
215 }
216
217 #endif /* if !defined(UCLINUX) */
218