xref: /openbmc/linux/tools/testing/selftests/mqueue/mq_open_tests.c (revision 3eb66e91a25497065c5322b1268cbc3953642227)
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  
__set(FILE * stream,int value,char * err_msg)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  
shutdown(int exit_val,char * err_cause,int line_no)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  
get(FILE * stream)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  
set(FILE * stream,int value)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  
getr(int type,struct rlimit * rlim)141  static inline void getr(int type, struct rlimit *rlim)
142  {
143  	if (getrlimit(type, rlim))
144  		shutdown(6, "getrlimit()", __LINE__ - 1);
145  }
146  
setr(int type,struct rlimit * rlim)147  static inline void setr(int type, struct rlimit *rlim)
148  {
149  	if (setrlimit(type, rlim))
150  		shutdown(7, "setrlimit()", __LINE__ - 1);
151  }
152  
validate_current_settings()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   */
test_queue(struct mq_attr * attr,struct mq_attr * result)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   */
test_queue_fail(struct mq_attr * attr,struct mq_attr * result)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  
main(int argc,char * argv[])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  }
503