GNU Linux-libre 4.19.264-gnu1
[releases.git] / tools / testing / selftests / mqueue / mq_open_tests.c
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(4, "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(5, "Failed writing to /proc file",
134                                 __LINE__ - 1);
135         new_value = get(stream);
136         if (new_value != value)
137                 return shutdown(5, "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(6, "getrlimit()", __LINE__ - 1);
145 }
146
147 static inline void setr(int type, struct rlimit *rlim)
148 {
149         if (setrlimit(type, rlim))
150                 shutdown(7, "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(RLIMIT_MSGQUEUE, &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(1, "mq_open()", __LINE__);
207         if (mq_getattr(queue, result))
208                 shutdown(1, "mq_getattr()", __LINE__);
209         if (mq_close(queue))
210                 shutdown(1, "mq_close()", __LINE__);
211         queue = -1;
212         if (mq_unlink(queue_path))
213                 shutdown(1, "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(1, "mq_getattr()", __LINE__);
231         if (mq_close(queue))
232                 shutdown(1, "mq_close()", __LINE__);
233         queue = -1;
234         if (mq_unlink(queue_path))
235                 shutdown(1, "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("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(2, "Failed to open msg_max", __LINE__);
280         if (!max_msgsize)
281                 shutdown(2, "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(RLIMIT_MSGQUEUE, &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(NULL, &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, &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, &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, &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, &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, &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, &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, &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, &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(0,"",0);
502 }