]> git.saurik.com Git - apple/xnu.git/blob - tests/proc_rlimit.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / tests / proc_rlimit.c
1 /*
2 * cd $XNU/tests
3 * xcrun -sdk macosx.internal/iphoneos.internal make proc_rlimit LDFLAGS="-ldarwintest"
4 */
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <sys/resource.h>
8 #include <errno.h>
9 #include <sys/sysctl.h>
10 #include <darwintest.h>
11
12 /* Defined in <sys/resource.h> but not visible to user space */
13 #define RLIMIT_NLIMITS 9
14
15 /* Defined in <sys/resource.h> and visible to user space */
16 static const char *RESOURCE_STRING[] = {
17 "RLIMIT_CPU", /* #define RLIMIT_CPU 0 */
18 "RLIMIT_FSIZE", /* #define RLIMIT_FSIZE 1 */
19 "RLIMIT_DATA", /* #define RLIMIT_DATA 2 */
20 "RLIMIT_STACK", /* #define RLIMIT_STACK 3 */
21 "RLIMIT_CORE", /* #define RLIMIT_CORE 4 */
22 "RLIMIT_AS/RSS", /* #define RLIMIT_AS 5 */
23 /* #define RLIMIT_RSS RLIMIT_AS */
24 "RLIMIT_MEMLOCK", /* #define RLIMIT_MEMLOCK 6 */
25 "RLIMIT_NPROC", /* #define RLIMIT_NPROC 7 */
26 "RLIMIT_NOFILE" /* #define RLIMIT_NOFILE 8 */
27 };
28
29 /* Change limit values by this arbitrary amount */
30 #define LIMIT_DIFF 64
31
32 /* Limit type */
33 #define SOFT_LIMIT 0
34 #define HARD_LIMIT 1
35
36 /* Action on changing limit values */
37 #define LOWER 0
38 #define RAISE 1
39
40 static struct rlimit orig_rlimit[RLIMIT_NLIMITS];
41
42 /* Maximum number of open files allowed by normal user */
43 static rlim_t maxfilesperproc;
44 static size_t maxfilesperproc_size = sizeof(maxfilesperproc);
45
46 /* Maximum number of open files allowed by super user */
47 static rlim_t maxfiles;
48 static size_t maxfiles_size = sizeof(maxfiles);
49
50 /* Maximum number of simultaneous processes allowed by normal user */
51 static rlim_t maxprocperuid;
52 static size_t maxprocperuid_size = sizeof(maxprocperuid);
53
54 /* Maximum number of simultaneous processes allowed by super user */
55 static rlim_t maxproc;
56 static size_t maxproc_size = sizeof(maxproc);
57
58 static bool superuser = FALSE;
59
60 static int
61 get_initial_rlimits(void)
62 {
63 int err = -1;
64 int i;
65
66 for (i = 0; i < RLIMIT_NLIMITS; i++) {
67 err = getrlimit(i, &orig_rlimit[i]);
68 T_QUIET; T_EXPECT_EQ(0, err, "getrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) %s", RESOURCE_STRING[i], orig_rlimit[i].rlim_cur, orig_rlimit[i].rlim_max, err == 0 ? "" : strerror(errno));
69 }
70 return err;
71 }
72
73 static void
74 print_rlimits(bool initial_limits)
75 {
76 int err;
77 int i;
78
79 for (i = 0; i < RLIMIT_NLIMITS; i++) {
80 struct rlimit lim;
81
82 if (initial_limits) {
83 lim = orig_rlimit[i];
84 } else {
85 err = getrlimit(i, &lim);
86 T_QUIET; T_EXPECT_EQ(0, err, "getrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) %s", RESOURCE_STRING[i], lim.rlim_cur, lim.rlim_max, err == 0 ? "" : strerror(errno));
87 }
88 T_LOG("%35s soft: 0x%16llx hard 0x%16llx", RESOURCE_STRING[i], lim.rlim_cur, lim.rlim_max);
89 }
90 }
91
92 /*
93 * Change "limit_type" of all of the process's "rlimit" by amount
94 *
95 * limit_type: SOFT_LIMIT/HARD_LIMIT
96 * amount: rlim_t
97 * action: RAISE/LOWER
98 */
99 static void
100 change_rlimits(int limit_type, rlim_t amount, int action)
101 {
102 int err = -1;
103 int i;
104
105 for (i = 0; i < RLIMIT_NLIMITS; i++) {
106 struct rlimit newlim; // for setrlimit
107 struct rlimit verifylim; // for getrlimit
108 bool expect_failure = FALSE;
109 int expect_errno = 0;
110
111 /* Get the current limit values */
112 err = getrlimit(i, &newlim);
113 T_EXPECT_EQ(0, err, "getrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) %s", RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, err == 0 ? "" : strerror(errno));
114
115 /* Changing soft limit */
116 if (limit_type == SOFT_LIMIT) {
117 if (action == RAISE) {
118 /* Raising soft limits to exceed hard limits is not allowed and we expect to see failure on setrlimit call later */
119 if (newlim.rlim_cur + amount > newlim.rlim_max) {
120 expect_failure = TRUE;
121 expect_errno = EINVAL;
122 }
123 newlim.rlim_cur += amount;
124 } else if (action == LOWER) {
125 if (newlim.rlim_cur == 0) {
126 /* Soft limit might be 0 already, if so skip lowering it */
127 } else {
128 newlim.rlim_cur -= amount;
129 }
130 } else {
131 T_FAIL("Unknown action on soft limit: %d", action);
132 }
133 }
134 /* Changing hard limit */
135 else if (limit_type == HARD_LIMIT) {
136 if (action == RAISE) {
137 newlim.rlim_max += amount;
138
139 /* Raising hard limits is not allowed for normal user and we expect to see failure on setrlimit call later */
140 expect_failure = TRUE;
141 expect_errno = EPERM;
142 } else if (action == LOWER) {
143 if (newlim.rlim_max == 0) {
144 /* Hard limit might be 0 already, if so skip lowering it (e.g., RLIMIT_CORE on iOS) */
145 } else {
146 newlim.rlim_max -= amount;
147 }
148 /* Soft limit might need to be changed as well since soft cannot be greater than hard */
149 if (newlim.rlim_cur > newlim.rlim_max) {
150 newlim.rlim_cur = newlim.rlim_max;
151 }
152 } else {
153 T_FAIL("Unknown action on hard limit: %d", action);
154 }
155 }
156 /* Changing unknown limit type */
157 else {
158 T_FAIL("Unknown limit type: %d", limit_type);
159 }
160
161 /* Request the kernel to change limit values */
162 err = setrlimit(i, &newlim);
163
164 if (expect_failure) {
165 /* We expect the setrlimit call to fail */
166 T_EXPECT_EQ(-1, err, "setrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) failed as expected: %s", RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, strerror(errno));
167 T_EXPECT_EQ(expect_errno, errno, "Expect errno %d, errno returned %d", expect_errno, errno);
168 continue;
169 } else {
170 T_EXPECT_EQ(0, err, "setrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) %s", RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, err == 0 ? "" : strerror(errno));
171 }
172
173 /* Verify the kernel correctly changed the limit values */
174 err = getrlimit(i, &verifylim);
175 T_EXPECT_EQ(0, err, "getrlimit(%15s, soft: 0x%16llx, hard 0x%16llx) %s", RESOURCE_STRING[i], verifylim.rlim_cur, verifylim.rlim_max, err == 0 ? "" : strerror(errno));
176
177 /* The kernel forces the hard limit of RLIMIT_NOFILE to be at most maxfileperproc for normal user when changing the hard limit with setrlimit */
178 if (i == RLIMIT_NOFILE && limit_type == HARD_LIMIT && newlim.rlim_max > maxfilesperproc) {
179 if (newlim.rlim_cur != verifylim.rlim_cur ||
180 maxfilesperproc != verifylim.rlim_max) {
181 T_FAIL("Mismatch limit values %s despite a successful setrlimit call (setrlimit'd soft 0x%16llx hard 0x%16llx but getrlimit'd soft 0x%16llx hard 0x%16llx)",
182 RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, verifylim.rlim_cur, verifylim.rlim_max);
183 }
184 }
185 /* The kernel forces the hard limit of RLIMIT_NPROC to be at most maxproc for normal user when changing either soft/hard limit with setrlimit */
186 else if (i == RLIMIT_NPROC && newlim.rlim_max > maxprocperuid) {
187 if (newlim.rlim_cur != verifylim.rlim_cur ||
188 maxprocperuid != verifylim.rlim_max) {
189 T_FAIL("Mismatch limit values %s despite a successful setrlimit call (setrlimit'd soft 0x%16llx hard 0x%16llx but getrlimit'd soft 0x%16llx hard 0x%16llx)",
190 RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, verifylim.rlim_cur, verifylim.rlim_max);
191 }
192 } else {
193 if (newlim.rlim_cur != verifylim.rlim_cur ||
194 newlim.rlim_max != verifylim.rlim_max) {
195 T_FAIL("Mismatch limit values %s despite a successful setrlimit call (setrlimit'd soft 0x%16llx hard 0x%16llx but getrlimit'd soft 0x%16llx hard 0x%16llx)",
196 RESOURCE_STRING[i], newlim.rlim_cur, newlim.rlim_max, verifylim.rlim_cur, verifylim.rlim_max);
197 }
198 }
199 }
200 }
201
202 T_DECL(proc_rlimit,
203 "Test basic functionalities of the getrlimit and setrlimit")
204 {
205 int err;
206 struct rlimit lim;
207
208 T_SETUPBEGIN;
209
210 if (geteuid() == 0) {
211 superuser = TRUE;
212 T_SKIP("This test should not be run as super user.");
213 }
214
215 /* Use sysctl to query the real limits of RLIMIT_NOFILE/RLIMIT_NPROC for normal user on Apple's systems */
216 err = sysctlbyname("kern.maxfilesperproc", &maxfilesperproc, &maxfilesperproc_size, NULL, 0);
217 T_EXPECT_EQ_INT(0, err, "maxfilesperproc: %llu", maxfilesperproc);
218
219 err = sysctlbyname("kern.maxprocperuid", &maxprocperuid, &maxprocperuid_size, NULL, 0);
220 T_EXPECT_EQ_INT(0, err, "maxprocperuid: %llu", maxprocperuid);
221
222 /* Use sysctl to query the real limits of RLIMIT_NOFILE/RLIMIT_NPROC for super user on Apple's systems (placeholder for adding super user tests) */
223 err = sysctlbyname("kern.maxfiles", &maxfiles, &maxfiles_size, NULL, 0);
224 T_EXPECT_EQ_INT(0, err, "maxfiles: %llu", maxfiles);
225
226 err = sysctlbyname("kern.maxproc", &maxproc, &maxproc_size, NULL, 0);
227 T_EXPECT_EQ_INT(0, err, "maxproc: %llu", maxproc);
228
229 /* Issue getrlimit syscall to retrieve the initial resource limit values before calling setrlimit */
230 err = get_initial_rlimits();
231 T_EXPECT_EQ(0, err, "Obtained initial resource values.");
232
233 /* Print out resource limit values to stdout for less-painful triage in case needed */
234 T_LOG("Resource limits before the test:");
235 print_rlimits(TRUE);
236
237 T_SETUPEND;
238
239 /* Lower soft limits by arbitrary amount */
240 T_LOG("---------Lowering soft limits by 0x%x---------:\n", LIMIT_DIFF);
241 change_rlimits(SOFT_LIMIT, LIMIT_DIFF, LOWER);
242
243 /* Raise soft limits back to the orginal values */
244 T_LOG("---------Raising soft limits by 0x%x---------:\n", LIMIT_DIFF);
245 change_rlimits(SOFT_LIMIT, LIMIT_DIFF, RAISE);
246
247 /* Lower hard limits */
248 T_LOG("---------Lowering hard limits by 0x%x---------:", LIMIT_DIFF);
249 change_rlimits(HARD_LIMIT, LIMIT_DIFF, LOWER);
250
251 /* Raise soft limits to exceed hard limits (setrlimit should fail, but the darwintest should pass) */
252 T_LOG("---------Attempting to raised soft limits by 0x%x to exceed hard limits---------:", LIMIT_DIFF);
253 change_rlimits(SOFT_LIMIT, LIMIT_DIFF, RAISE);
254
255 /* Raise hard limits (setrlimit should fail, but the darwintest should pass) */
256 T_LOG("---------Attempting to raise hard limits by 0x%x---------:", LIMIT_DIFF);
257 change_rlimits(HARD_LIMIT, LIMIT_DIFF, RAISE);
258
259 /* Get and set a non-existing resource limit */
260 T_LOG("---------Accessing a non-existing resource---------:");
261 err = getrlimit(RLIMIT_NLIMITS + 1, &lim);
262 T_EXPECT_EQ(-1, err, "Expect getrlimit to fail when accessing a non-existing resource: %s\n", strerror(errno));
263 T_EXPECT_EQ(EINVAL, errno, "Expect errno %d, errno returned %d", EINVAL, errno);
264
265 err = setrlimit(RLIMIT_NLIMITS + 1, &lim);
266 T_EXPECT_EQ(-1, err, "Expect setrlimit to fail when accessing a non-existing resource: %s\n", strerror(errno));
267 T_EXPECT_EQ(EINVAL, errno, "Expect errno %d, errno returned %d", EINVAL, errno);
268
269 T_LOG("Resource limits after the test:");
270 print_rlimits(FALSE);
271 }