1 | /* |
2 | * This application is Copyright 2012 Red Hat, Inc. |
3 | * Doug Ledford <dledford@redhat.com> |
4 | * |
5 | * mq_open_tests 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, version 3. |
8 | * |
9 | * mq_open_tests is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * For the full text of the license, see <http://www.gnu.org/licenses/>. |
15 | * |
16 | * mq_open_tests.c |
17 | * Tests the various situations that should either succeed or fail to |
18 | * open a posix message queue and then reports whether or not they |
19 | * did as they were supposed to. |
20 | * |
21 | */ |
22 | #include <stdio.h> |
23 | #include <stdlib.h> |
24 | #include <unistd.h> |
25 | #include <fcntl.h> |
26 | #include <string.h> |
27 | #include <limits.h> |
28 | #include <errno.h> |
29 | #include <sys/types.h> |
30 | #include <sys/time.h> |
31 | #include <sys/resource.h> |
32 | #include <sys/stat.h> |
33 | #include <mqueue.h> |
34 | #include <error.h> |
35 | |
36 | #include "../kselftest.h" |
37 | |
38 | static char *usage = |
39 | "Usage:\n" |
40 | " %s path\n" |
41 | "\n" |
42 | " path Path name of the message queue to create\n" |
43 | "\n" |
44 | " Note: this program must be run as root in order to enable all tests\n" |
45 | "\n" ; |
46 | |
47 | char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default" ; |
48 | char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default" ; |
49 | char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max" ; |
50 | char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max" ; |
51 | |
52 | int default_settings; |
53 | struct rlimit saved_limits, cur_limits; |
54 | int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize; |
55 | int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize; |
56 | FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize; |
57 | char *queue_path; |
58 | char *default_queue_path = "/test1" ; |
59 | mqd_t queue = -1; |
60 | |
61 | static inline void __set(FILE *stream, int value, char *err_msg); |
62 | void shutdown(int exit_val, char *err_cause, int line_no); |
63 | static inline int get(FILE *stream); |
64 | static inline void set(FILE *stream, int value); |
65 | static inline void getr(int type, struct rlimit *rlim); |
66 | static inline void setr(int type, struct rlimit *rlim); |
67 | void validate_current_settings(); |
68 | static inline void test_queue(struct mq_attr *attr, struct mq_attr *result); |
69 | static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result); |
70 | |
71 | static inline void __set(FILE *stream, int value, char *err_msg) |
72 | { |
73 | rewind(stream); |
74 | if (fprintf(stream, "%d" , value) < 0) |
75 | perror(err_msg); |
76 | } |
77 | |
78 | |
79 | void shutdown(int exit_val, char *err_cause, int line_no) |
80 | { |
81 | static int in_shutdown = 0; |
82 | |
83 | /* In case we get called recursively by a set() call below */ |
84 | if (in_shutdown++) |
85 | return; |
86 | |
87 | if (seteuid(0) == -1) |
88 | perror("seteuid() failed" ); |
89 | |
90 | if (queue != -1) |
91 | if (mq_close(queue)) |
92 | perror("mq_close() during shutdown" ); |
93 | if (queue_path) |
94 | /* |
95 | * Be silent if this fails, if we cleaned up already it's |
96 | * expected to fail |
97 | */ |
98 | mq_unlink(queue_path); |
99 | if (default_settings) { |
100 | if (saved_def_msgs) |
101 | __set(def_msgs, saved_def_msgs, |
102 | "failed to restore saved_def_msgs" ); |
103 | if (saved_def_msgsize) |
104 | __set(def_msgsize, saved_def_msgsize, |
105 | "failed to restore saved_def_msgsize" ); |
106 | } |
107 | if (saved_max_msgs) |
108 | __set(max_msgs, saved_max_msgs, |
109 | "failed to restore saved_max_msgs" ); |
110 | if (saved_max_msgsize) |
111 | __set(max_msgsize, saved_max_msgsize, |
112 | "failed to restore saved_max_msgsize" ); |
113 | if (exit_val) |
114 | error(exit_val, errno, "%s at %d" , err_cause, line_no); |
115 | exit(0); |
116 | } |
117 | |
118 | static inline int get(FILE *stream) |
119 | { |
120 | int value; |
121 | rewind(stream); |
122 | if (fscanf(stream, "%d" , &value) != 1) |
123 | shutdown(exit_val: 4, err_cause: "Error reading /proc entry" , __LINE__ - 1); |
124 | return value; |
125 | } |
126 | |
127 | static inline void set(FILE *stream, int value) |
128 | { |
129 | int new_value; |
130 | |
131 | rewind(stream); |
132 | if (fprintf(stream, "%d" , value) < 0) |
133 | return shutdown(exit_val: 5, err_cause: "Failed writing to /proc file" , |
134 | __LINE__ - 1); |
135 | new_value = get(stream); |
136 | if (new_value != value) |
137 | return shutdown(exit_val: 5, err_cause: "We didn't get what we wrote to /proc back" , |
138 | __LINE__ - 1); |
139 | } |
140 | |
141 | static inline void getr(int type, struct rlimit *rlim) |
142 | { |
143 | if (getrlimit(type, rlim)) |
144 | shutdown(exit_val: 6, err_cause: "getrlimit()" , __LINE__ - 1); |
145 | } |
146 | |
147 | static inline void setr(int type, struct rlimit *rlim) |
148 | { |
149 | if (setrlimit(type, rlim)) |
150 | shutdown(exit_val: 7, err_cause: "setrlimit()" , __LINE__ - 1); |
151 | } |
152 | |
153 | void validate_current_settings() |
154 | { |
155 | int rlim_needed; |
156 | |
157 | if (cur_limits.rlim_cur < 4096) { |
158 | printf("Current rlimit value for POSIX message queue bytes is " |
159 | "unreasonably low,\nincreasing.\n\n" ); |
160 | cur_limits.rlim_cur = 8192; |
161 | cur_limits.rlim_max = 16384; |
162 | setr(type: RLIMIT_MSGQUEUE, rlim: &cur_limits); |
163 | } |
164 | |
165 | if (default_settings) { |
166 | rlim_needed = (cur_def_msgs + 1) * (cur_def_msgsize + 1 + |
167 | 2 * sizeof(void *)); |
168 | if (rlim_needed > cur_limits.rlim_cur) { |
169 | printf("Temporarily lowering default queue parameters " |
170 | "to something that will work\n" |
171 | "with the current rlimit values.\n\n" ); |
172 | set(def_msgs, 10); |
173 | cur_def_msgs = 10; |
174 | set(def_msgsize, 128); |
175 | cur_def_msgsize = 128; |
176 | } |
177 | } else { |
178 | rlim_needed = (cur_max_msgs + 1) * (cur_max_msgsize + 1 + |
179 | 2 * sizeof(void *)); |
180 | if (rlim_needed > cur_limits.rlim_cur) { |
181 | printf("Temporarily lowering maximum queue parameters " |
182 | "to something that will work\n" |
183 | "with the current rlimit values in case this is " |
184 | "a kernel that ties the default\n" |
185 | "queue parameters to the maximum queue " |
186 | "parameters.\n\n" ); |
187 | set(max_msgs, 10); |
188 | cur_max_msgs = 10; |
189 | set(max_msgsize, 128); |
190 | cur_max_msgsize = 128; |
191 | } |
192 | } |
193 | } |
194 | |
195 | /* |
196 | * test_queue - Test opening a queue, shutdown if we fail. This should |
197 | * only be called in situations that should never fail. We clean up |
198 | * after ourselves and return the queue attributes in *result. |
199 | */ |
200 | static inline void test_queue(struct mq_attr *attr, struct mq_attr *result) |
201 | { |
202 | int flags = O_RDWR | O_EXCL | O_CREAT; |
203 | int perms = DEFFILEMODE; |
204 | |
205 | if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) |
206 | shutdown(exit_val: 1, err_cause: "mq_open()" , __LINE__); |
207 | if (mq_getattr(queue, result)) |
208 | shutdown(exit_val: 1, err_cause: "mq_getattr()" , __LINE__); |
209 | if (mq_close(queue)) |
210 | shutdown(exit_val: 1, err_cause: "mq_close()" , __LINE__); |
211 | queue = -1; |
212 | if (mq_unlink(queue_path)) |
213 | shutdown(exit_val: 1, err_cause: "mq_unlink()" , __LINE__); |
214 | } |
215 | |
216 | /* |
217 | * Same as test_queue above, but failure is not fatal. |
218 | * Returns: |
219 | * 0 - Failed to create a queue |
220 | * 1 - Created a queue, attributes in *result |
221 | */ |
222 | static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result) |
223 | { |
224 | int flags = O_RDWR | O_EXCL | O_CREAT; |
225 | int perms = DEFFILEMODE; |
226 | |
227 | if ((queue = mq_open(queue_path, flags, perms, attr)) == -1) |
228 | return 0; |
229 | if (mq_getattr(queue, result)) |
230 | shutdown(exit_val: 1, err_cause: "mq_getattr()" , __LINE__); |
231 | if (mq_close(queue)) |
232 | shutdown(exit_val: 1, err_cause: "mq_close()" , __LINE__); |
233 | queue = -1; |
234 | if (mq_unlink(queue_path)) |
235 | shutdown(exit_val: 1, err_cause: "mq_unlink()" , __LINE__); |
236 | return 1; |
237 | } |
238 | |
239 | int main(int argc, char *argv[]) |
240 | { |
241 | struct mq_attr attr, result; |
242 | |
243 | if (argc != 2) { |
244 | printf("Using Default queue path - %s\n" , default_queue_path); |
245 | queue_path = default_queue_path; |
246 | } else { |
247 | |
248 | /* |
249 | * Although we can create a msg queue with a non-absolute path name, |
250 | * unlink will fail. So, if the name doesn't start with a /, add one |
251 | * when we save it. |
252 | */ |
253 | if (*argv[1] == '/') |
254 | queue_path = strdup(argv[1]); |
255 | else { |
256 | queue_path = malloc(strlen(argv[1]) + 2); |
257 | if (!queue_path) { |
258 | perror("malloc()" ); |
259 | exit(1); |
260 | } |
261 | queue_path[0] = '/'; |
262 | queue_path[1] = 0; |
263 | strcat(queue_path, argv[1]); |
264 | } |
265 | } |
266 | |
267 | if (getuid() != 0) |
268 | ksft_exit_skip(msg: "Not running as root, but almost all tests " |
269 | "require root in order to modify\nsystem settings. " |
270 | "Exiting.\n" ); |
271 | |
272 | /* Find out what files there are for us to make tweaks in */ |
273 | def_msgs = fopen(DEF_MSGS, "r+" ); |
274 | def_msgsize = fopen(DEF_MSGSIZE, "r+" ); |
275 | max_msgs = fopen(MAX_MSGS, "r+" ); |
276 | max_msgsize = fopen(MAX_MSGSIZE, "r+" ); |
277 | |
278 | if (!max_msgs) |
279 | shutdown(exit_val: 2, err_cause: "Failed to open msg_max" , __LINE__); |
280 | if (!max_msgsize) |
281 | shutdown(exit_val: 2, err_cause: "Failed to open msgsize_max" , __LINE__); |
282 | if (def_msgs || def_msgsize) |
283 | default_settings = 1; |
284 | |
285 | /* Load up the current system values for everything we can */ |
286 | getr(type: RLIMIT_MSGQUEUE, rlim: &saved_limits); |
287 | cur_limits = saved_limits; |
288 | if (default_settings) { |
289 | saved_def_msgs = cur_def_msgs = get(def_msgs); |
290 | saved_def_msgsize = cur_def_msgsize = get(def_msgsize); |
291 | } |
292 | saved_max_msgs = cur_max_msgs = get(max_msgs); |
293 | saved_max_msgsize = cur_max_msgsize = get(max_msgsize); |
294 | |
295 | /* Tell the user our initial state */ |
296 | printf("\nInitial system state:\n" ); |
297 | printf("\tUsing queue path:\t\t%s\n" , queue_path); |
298 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n" , |
299 | (long) saved_limits.rlim_cur); |
300 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n" , |
301 | (long) saved_limits.rlim_max); |
302 | printf("\tMaximum Message Size:\t\t%d\n" , saved_max_msgsize); |
303 | printf("\tMaximum Queue Size:\t\t%d\n" , saved_max_msgs); |
304 | if (default_settings) { |
305 | printf("\tDefault Message Size:\t\t%d\n" , saved_def_msgsize); |
306 | printf("\tDefault Queue Size:\t\t%d\n" , saved_def_msgs); |
307 | } else { |
308 | printf("\tDefault Message Size:\t\tNot Supported\n" ); |
309 | printf("\tDefault Queue Size:\t\tNot Supported\n" ); |
310 | } |
311 | printf("\n" ); |
312 | |
313 | validate_current_settings(); |
314 | |
315 | printf("Adjusted system state for testing:\n" ); |
316 | printf("\tRLIMIT_MSGQUEUE(soft):\t\t%ld\n" , (long) cur_limits.rlim_cur); |
317 | printf("\tRLIMIT_MSGQUEUE(hard):\t\t%ld\n" , (long) cur_limits.rlim_max); |
318 | printf("\tMaximum Message Size:\t\t%d\n" , cur_max_msgsize); |
319 | printf("\tMaximum Queue Size:\t\t%d\n" , cur_max_msgs); |
320 | if (default_settings) { |
321 | printf("\tDefault Message Size:\t\t%d\n" , cur_def_msgsize); |
322 | printf("\tDefault Queue Size:\t\t%d\n" , cur_def_msgs); |
323 | } |
324 | |
325 | printf("\n\nTest series 1, behavior when no attr struct " |
326 | "passed to mq_open:\n" ); |
327 | if (!default_settings) { |
328 | test_queue(attr: NULL, result: &result); |
329 | printf("Given sane system settings, mq_open without an attr " |
330 | "struct succeeds:\tPASS\n" ); |
331 | if (result.mq_maxmsg != cur_max_msgs || |
332 | result.mq_msgsize != cur_max_msgsize) { |
333 | printf("Kernel does not support setting the default " |
334 | "mq attributes,\nbut also doesn't tie the " |
335 | "defaults to the maximums:\t\t\tPASS\n" ); |
336 | } else { |
337 | set(max_msgs, ++cur_max_msgs); |
338 | set(max_msgsize, ++cur_max_msgsize); |
339 | test_queue(NULL, &result); |
340 | if (result.mq_maxmsg == cur_max_msgs && |
341 | result.mq_msgsize == cur_max_msgsize) |
342 | printf("Kernel does not support setting the " |
343 | "default mq attributes and\n" |
344 | "also ties system wide defaults to " |
345 | "the system wide maximums:\t\t" |
346 | "FAIL\n" ); |
347 | else |
348 | printf("Kernel does not support setting the " |
349 | "default mq attributes,\n" |
350 | "but also doesn't tie the defaults to " |
351 | "the maximums:\t\t\tPASS\n" ); |
352 | } |
353 | } else { |
354 | printf("Kernel supports setting defaults separately from " |
355 | "maximums:\t\tPASS\n" ); |
356 | /* |
357 | * While we are here, go ahead and test that the kernel |
358 | * properly follows the default settings |
359 | */ |
360 | test_queue(NULL, &result); |
361 | printf("Given sane values, mq_open without an attr struct " |
362 | "succeeds:\t\tPASS\n" ); |
363 | if (result.mq_maxmsg != cur_def_msgs || |
364 | result.mq_msgsize != cur_def_msgsize) |
365 | printf("Kernel supports setting defaults, but does " |
366 | "not actually honor them:\tFAIL\n\n" ); |
367 | else { |
368 | set(def_msgs, ++cur_def_msgs); |
369 | set(def_msgsize, ++cur_def_msgsize); |
370 | /* In case max was the same as the default */ |
371 | set(max_msgs, ++cur_max_msgs); |
372 | set(max_msgsize, ++cur_max_msgsize); |
373 | test_queue(NULL, &result); |
374 | if (result.mq_maxmsg != cur_def_msgs || |
375 | result.mq_msgsize != cur_def_msgsize) |
376 | printf("Kernel supports setting defaults, but " |
377 | "does not actually honor them:\t" |
378 | "FAIL\n" ); |
379 | else |
380 | printf("Kernel properly honors default setting " |
381 | "knobs:\t\t\t\tPASS\n" ); |
382 | } |
383 | set(def_msgs, cur_max_msgs + 1); |
384 | cur_def_msgs = cur_max_msgs + 1; |
385 | set(def_msgsize, cur_max_msgsize + 1); |
386 | cur_def_msgsize = cur_max_msgsize + 1; |
387 | if (cur_def_msgs * (cur_def_msgsize + 2 * sizeof(void *)) >= |
388 | cur_limits.rlim_cur) { |
389 | cur_limits.rlim_cur = (cur_def_msgs + 2) * |
390 | (cur_def_msgsize + 2 * sizeof(void *)); |
391 | cur_limits.rlim_max = 2 * cur_limits.rlim_cur; |
392 | setr(RLIMIT_MSGQUEUE, &cur_limits); |
393 | } |
394 | if (test_queue_fail(NULL, &result)) { |
395 | if (result.mq_maxmsg == cur_max_msgs && |
396 | result.mq_msgsize == cur_max_msgsize) |
397 | printf("Kernel properly limits default values " |
398 | "to lesser of default/max:\t\tPASS\n" ); |
399 | else |
400 | printf("Kernel does not properly set default " |
401 | "queue parameters when\ndefaults > " |
402 | "max:\t\t\t\t\t\t\t\tFAIL\n" ); |
403 | } else |
404 | printf("Kernel fails to open mq because defaults are " |
405 | "greater than maximums:\tFAIL\n" ); |
406 | set(def_msgs, --cur_def_msgs); |
407 | set(def_msgsize, --cur_def_msgsize); |
408 | cur_limits.rlim_cur = cur_limits.rlim_max = cur_def_msgs * |
409 | cur_def_msgsize; |
410 | setr(RLIMIT_MSGQUEUE, &cur_limits); |
411 | if (test_queue_fail(NULL, &result)) |
412 | printf("Kernel creates queue even though defaults " |
413 | "would exceed\nrlimit setting:" |
414 | "\t\t\t\t\t\t\t\tFAIL\n" ); |
415 | else |
416 | printf("Kernel properly fails to create queue when " |
417 | "defaults would\nexceed rlimit:" |
418 | "\t\t\t\t\t\t\t\tPASS\n" ); |
419 | } |
420 | |
421 | /* |
422 | * Test #2 - open with an attr struct that exceeds rlimit |
423 | */ |
424 | printf("\n\nTest series 2, behavior when attr struct is " |
425 | "passed to mq_open:\n" ); |
426 | cur_max_msgs = 32; |
427 | cur_max_msgsize = cur_limits.rlim_max >> 4; |
428 | set(max_msgs, cur_max_msgs); |
429 | set(max_msgsize, cur_max_msgsize); |
430 | attr.mq_maxmsg = cur_max_msgs; |
431 | attr.mq_msgsize = cur_max_msgsize; |
432 | if (test_queue_fail(attr: &attr, result: &result)) |
433 | printf("Queue open in excess of rlimit max when euid = 0 " |
434 | "succeeded:\t\tFAIL\n" ); |
435 | else |
436 | printf("Queue open in excess of rlimit max when euid = 0 " |
437 | "failed:\t\tPASS\n" ); |
438 | attr.mq_maxmsg = cur_max_msgs + 1; |
439 | attr.mq_msgsize = 10; |
440 | if (test_queue_fail(attr: &attr, result: &result)) |
441 | printf("Queue open with mq_maxmsg > limit when euid = 0 " |
442 | "succeeded:\t\tPASS\n" ); |
443 | else |
444 | printf("Queue open with mq_maxmsg > limit when euid = 0 " |
445 | "failed:\t\tFAIL\n" ); |
446 | attr.mq_maxmsg = 1; |
447 | attr.mq_msgsize = cur_max_msgsize + 1; |
448 | if (test_queue_fail(attr: &attr, result: &result)) |
449 | printf("Queue open with mq_msgsize > limit when euid = 0 " |
450 | "succeeded:\t\tPASS\n" ); |
451 | else |
452 | printf("Queue open with mq_msgsize > limit when euid = 0 " |
453 | "failed:\t\tFAIL\n" ); |
454 | attr.mq_maxmsg = 65536; |
455 | attr.mq_msgsize = 65536; |
456 | if (test_queue_fail(attr: &attr, result: &result)) |
457 | printf("Queue open with total size > 2GB when euid = 0 " |
458 | "succeeded:\t\tFAIL\n" ); |
459 | else |
460 | printf("Queue open with total size > 2GB when euid = 0 " |
461 | "failed:\t\t\tPASS\n" ); |
462 | |
463 | if (seteuid(99) == -1) { |
464 | perror("seteuid() failed" ); |
465 | exit(1); |
466 | } |
467 | |
468 | attr.mq_maxmsg = cur_max_msgs; |
469 | attr.mq_msgsize = cur_max_msgsize; |
470 | if (test_queue_fail(attr: &attr, result: &result)) |
471 | printf("Queue open in excess of rlimit max when euid = 99 " |
472 | "succeeded:\t\tFAIL\n" ); |
473 | else |
474 | printf("Queue open in excess of rlimit max when euid = 99 " |
475 | "failed:\t\tPASS\n" ); |
476 | attr.mq_maxmsg = cur_max_msgs + 1; |
477 | attr.mq_msgsize = 10; |
478 | if (test_queue_fail(attr: &attr, result: &result)) |
479 | printf("Queue open with mq_maxmsg > limit when euid = 99 " |
480 | "succeeded:\t\tFAIL\n" ); |
481 | else |
482 | printf("Queue open with mq_maxmsg > limit when euid = 99 " |
483 | "failed:\t\tPASS\n" ); |
484 | attr.mq_maxmsg = 1; |
485 | attr.mq_msgsize = cur_max_msgsize + 1; |
486 | if (test_queue_fail(attr: &attr, result: &result)) |
487 | printf("Queue open with mq_msgsize > limit when euid = 99 " |
488 | "succeeded:\t\tFAIL\n" ); |
489 | else |
490 | printf("Queue open with mq_msgsize > limit when euid = 99 " |
491 | "failed:\t\tPASS\n" ); |
492 | attr.mq_maxmsg = 65536; |
493 | attr.mq_msgsize = 65536; |
494 | if (test_queue_fail(attr: &attr, result: &result)) |
495 | printf("Queue open with total size > 2GB when euid = 99 " |
496 | "succeeded:\t\tFAIL\n" ); |
497 | else |
498 | printf("Queue open with total size > 2GB when euid = 99 " |
499 | "failed:\t\t\tPASS\n" ); |
500 | |
501 | shutdown(exit_val: 0,err_cause: "" ,line_no: 0); |
502 | } |
503 | |