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
38static 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
47char *DEF_MSGS = "/proc/sys/fs/mqueue/msg_default";
48char *DEF_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_default";
49char *MAX_MSGS = "/proc/sys/fs/mqueue/msg_max";
50char *MAX_MSGSIZE = "/proc/sys/fs/mqueue/msgsize_max";
51
52int default_settings;
53struct rlimit saved_limits, cur_limits;
54int saved_def_msgs, saved_def_msgsize, saved_max_msgs, saved_max_msgsize;
55int cur_def_msgs, cur_def_msgsize, cur_max_msgs, cur_max_msgsize;
56FILE *def_msgs, *def_msgsize, *max_msgs, *max_msgsize;
57char *queue_path;
58char *default_queue_path = "/test1";
59mqd_t queue = -1;
60
61static inline void __set(FILE *stream, int value, char *err_msg);
62void shutdown(int exit_val, char *err_cause, int line_no);
63static inline int get(FILE *stream);
64static inline void set(FILE *stream, int value);
65static inline void getr(int type, struct rlimit *rlim);
66static inline void setr(int type, struct rlimit *rlim);
67void validate_current_settings();
68static inline void test_queue(struct mq_attr *attr, struct mq_attr *result);
69static inline int test_queue_fail(struct mq_attr *attr, struct mq_attr *result);
70
71static 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
79void 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
118static 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
127static 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
141static inline void getr(int type, struct rlimit *rlim)
142{
143	if (getrlimit(type, rlim))
144		shutdown(6, "getrlimit()", __LINE__ - 1);
145}
146
147static inline void setr(int type, struct rlimit *rlim)
148{
149	if (setrlimit(type, rlim))
150		shutdown(7, "setrlimit()", __LINE__ - 1);
151}
152
153void 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 */
200static 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 */
222static 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
239int 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