]>
Commit | Line | Data |
---|---|---|
f427ee49 A |
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 | } |