1/*
2 * Check decoding of mq_open, mq_timedsend, mq_notify, mq_timedreceive and
3 * mq_unlink syscalls.
4 *
5 * Copyright (c) 2016 Eugene Syromyatnikov <evgsyr@gmail.com>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "tests.h"
32
33#include <asm/unistd.h>
34
35#if defined __NR_mq_open && __NR_mq_timedsend && __NR_mq_timedreceive && \
36	__NR_mq_notify && __NR_mq_unlink
37
38# include <assert.h>
39# include <errno.h>
40# include <fcntl.h>
41# include <inttypes.h>
42# include <signal.h>
43# include <stdbool.h>
44# include <stdio.h>
45# include <stdlib.h>
46# include <string.h>
47# include <time.h>
48# include <unistd.h>
49
50# include "sigevent.h"
51
52# ifndef MQ_NAME
53#  define MQ_NAME "mq_sendrecv.sample"
54# endif
55
56# ifndef DUMPIO_READ
57#  define DUMPIO_READ 0
58# endif
59
60# ifndef DUMPIO_WRITE
61#  define DUMPIO_WRITE 0
62# endif
63
64
65enum {
66	NUM_ATTRS = 8,
67	MSG_CUT = 8,
68	MSG_MAX_UNCUT = 32,
69	MSG_SIZE = 64,
70	MSG_START = 0x80,
71};
72
73
74static void
75printstr(unsigned char start, unsigned int count)
76{
77	unsigned int i;
78
79	printf("\"");
80	for (i = 0; i < count; i++) {
81		printf("\\%hho", (unsigned char) (start + i));
82	}
83	printf("\"");
84}
85
86#if DUMPIO_READ || DUMPIO_WRITE
87static void
88dumpstr(unsigned char start, unsigned int count)
89{
90	unsigned int i;
91	unsigned int j;
92
93	for (i = 0; i < count; i++) {
94		if (i < count) {
95			if (!(i % 16))
96				printf(" | %05x ", i);
97			if (!(i % 8))
98				printf(" ");
99
100			printf("%02hhx ", (unsigned char) (start + i));
101		}
102
103		if ((i % 16 == 15) || (i == (count - 1))) {
104			if (i % 16 != 15)
105				printf("%*s", 3 * (15 - i % 16) +
106				       ((i + 8) % 16) / 8, " ");
107
108			printf(" ");
109
110			for (j = 0; j <= (i % 16); j++)
111				printf(".");
112			for (j = i % 16; j < 15; j++)
113				printf(" ");
114
115			printf(" |\n");
116
117		}
118	}
119}
120#endif /* DUMPIO_READ || DUMPIO_WRITE */
121
122static void
123cleanup(void)
124{
125	long rc;
126
127	rc = syscall(__NR_mq_unlink, MQ_NAME);
128	printf("mq_unlink(\"" MQ_NAME "\") = %s\n", sprintrc(rc));
129
130	puts("+++ exited with 0 +++");
131}
132
133static void
134do_send(int fd, char *msg, unsigned int msg_size, struct timespec *tmout,
135	bool cropped)
136{
137	long rc;
138	long saved_errno;
139
140	do {
141		rc = syscall(__NR_mq_timedsend, fd, msg, msg_size, 42,
142			     tmout);
143		saved_errno = errno;
144		printf("mq_timedsend(%d, ", fd);
145		printstr(MSG_START, msg_size > MSG_MAX_UNCUT ? MSG_MAX_UNCUT :
146			 msg_size);
147		if (cropped)
148			printf("...");
149		errno = saved_errno;
150		printf(", %u, 42, {tv_sec=%jd, tv_nsec=%jd}) = %s\n", msg_size,
151		       (intmax_t) tmout->tv_sec, (intmax_t) tmout->tv_nsec,
152		       sprintrc(rc));
153		errno = saved_errno;
154
155		if (rc == -1) {
156			if (errno == EINTR)
157				continue;
158			perror_msg_and_skip("mq_timedsend");
159		}
160# if DUMPIO_WRITE
161		dumpstr(MSG_START, msg_size);
162# endif
163	} while (rc);
164}
165
166static void
167do_recv(int fd, char *msg, unsigned int msg_size, struct timespec *tmout,
168	bool cropped)
169{
170	long rc;
171	long saved_errno;
172	unsigned prio;
173
174	do {
175		rc = syscall(__NR_mq_timedreceive, fd, msg, MSG_SIZE, &prio,
176			     tmout);
177		saved_errno = errno;
178		printf("mq_timedreceive(%d, ", fd);
179		if (rc >= 0) {
180			printstr(MSG_START, rc > MSG_MAX_UNCUT ? MSG_MAX_UNCUT :
181				 rc);
182			if (cropped)
183				printf("...");
184		} else {
185			printf("%p", msg);
186		}
187		errno = saved_errno;
188		printf(", %u, [42], {tv_sec=%jd, tv_nsec=%jd}) = %s\n", MSG_SIZE,
189		       (intmax_t) tmout->tv_sec,
190		       (intmax_t) tmout->tv_nsec, sprintrc(rc));
191		errno = saved_errno;
192
193		if (rc == -1) {
194			if (errno == EINTR)
195				continue;
196			perror_msg_and_skip("mq_timedreceive");
197		}
198		if ((rc >= 0) && ((unsigned long) rc != msg_size))
199			error_msg_and_skip("mq_timedreceive size mismatch"
200					   ": expected %u, got %ld",
201					   msg_size, rc);
202# if DUMPIO_READ
203		dumpstr(MSG_START, rc);
204# endif
205	} while (rc < 0);
206}
207
208int
209main(void)
210{
211	static const kernel_ulong_t bogus_zero =
212		(kernel_ulong_t) 0x8765432100000000ULL;
213	static const kernel_ulong_t bogus_oflags =
214		(kernel_ulong_t) 0xdefaced100000003ULL;
215	static const kernel_ulong_t bogus_mode =
216		(kernel_ulong_t) 0xdec0deadfacefeedULL;
217	static const kernel_ulong_t bogus_fd =
218		(kernel_ulong_t) 0xfeedfacedeadba5eULL;
219	static const kernel_ulong_t bogus_zero_size =
220		(sizeof(kernel_ulong_t) > sizeof(int)) ? (kernel_ulong_t) 0 :
221			(kernel_ulong_t) 0xface1e5500000000ULL;
222	static const kernel_ulong_t bogus_size =
223		(kernel_ulong_t) 0xbadc0dedda7a1057ULL;
224	static const kernel_ulong_t bogus_prio =
225		(kernel_ulong_t) 0xdec0ded1defaced3ULL;
226	static const struct timespec bogus_tmout_data = {
227		.tv_sec = (time_t) 0xdeadfacebeeff00dLL,
228		.tv_nsec = (long) 0xfacefee1deadfeedLL,
229	};
230	static const struct timespec future_tmout_data = {
231		.tv_sec = (time_t) 0x7ea1fade7e57faceLL,
232		.tv_nsec = 999999999,
233	};;
234	struct_sigevent bogus_sev_data = {
235		.sigev_notify = 0xdefaced,
236		.sigev_signo = 0xfacefeed,
237		.sigev_value.sival_ptr = (unsigned long) 0xdeadbeefbadc0dedULL
238	};
239
240	const char *errstr;
241	long rc;
242	kernel_long_t *bogus_attrs = tail_alloc(sizeof(*bogus_attrs) *
243		NUM_ATTRS);
244	char *msg = tail_alloc(MSG_SIZE);
245	unsigned *bogus_prio_ptr = tail_alloc(sizeof(*bogus_prio_ptr));
246	struct timespec *bogus_tmout = tail_memdup(&bogus_tmout_data,
247		sizeof(*bogus_tmout));
248	struct timespec *future_tmout = tail_memdup(&future_tmout_data,
249		sizeof(*future_tmout));
250	struct_sigevent *bogus_sev = tail_memdup(&bogus_sev_data,
251		sizeof(*bogus_sev));
252	int fd = -1;
253
254
255	fill_memory_ex(msg, MSG_SIZE, MSG_START, MSG_SIZE);
256	fill_memory_ex(bogus_attrs, sizeof(*bogus_attrs) * NUM_ATTRS,
257		       0xbb, 0x70);
258
259
260	/* mq_open */
261
262	/* Zero values, non-O_CREAT mode */
263	rc = syscall(__NR_mq_open, NULL, bogus_zero, bogus_mode, NULL);
264	printf("mq_open(NULL, O_RDONLY) = %s\n", sprintrc(rc));
265
266	/* O_CREAT parsing, other flags, bogs values */
267	rc = syscall(__NR_mq_open, msg, O_CREAT | bogus_oflags, bogus_mode,
268		     NULL);
269	printf("mq_open(%p, O_ACCMODE|O_CREAT, %#o, NULL) = %s\n",
270	       msg, (unsigned short) bogus_mode, sprintrc(rc));
271
272	/* Partially invalid attributes structure */
273	rc = syscall(__NR_mq_open, msg, O_CREAT | bogus_oflags, bogus_mode,
274		     bogus_attrs + 1);
275	printf("mq_open(%p, O_ACCMODE|O_CREAT, %#o, %p) = %s\n",
276	       msg, (unsigned short) bogus_mode, bogus_attrs + 1, sprintrc(rc));
277
278	/* Valid attributes structure */
279	rc = syscall(__NR_mq_open, msg, O_CREAT | bogus_oflags, bogus_mode,
280		     bogus_attrs);
281	printf("mq_open(%p, O_ACCMODE|O_CREAT, %#o, {mq_flags=%#llx, "
282	       "mq_maxmsg=%lld, mq_msgsize=%lld, mq_curmsgs=%lld}) = %s\n",
283	       msg, (unsigned short) bogus_mode,
284	       (unsigned long long) (kernel_ulong_t) bogus_attrs[0],
285	       (long long) bogus_attrs[1],
286	       (long long) bogus_attrs[2],
287	       (long long) bogus_attrs[3], sprintrc(rc));
288
289
290	/* mq_timedsend */
291
292	/* Zero values*/
293	rc = syscall(__NR_mq_timedsend, bogus_zero, NULL, bogus_zero_size,
294		     bogus_zero, NULL);
295	printf("mq_timedsend(0, NULL, 0, 0, NULL) = %s\n", sprintrc(rc));
296
297	/* Invalid pointers */
298	rc = syscall(__NR_mq_timedsend, bogus_fd, msg + MSG_SIZE, bogus_size,
299		     bogus_prio, bogus_tmout + 1);
300	printf("mq_timedsend(%d, %p, %llu, %u, %p) = %s\n",
301	       (int) bogus_fd, msg + MSG_SIZE, (unsigned long long) bogus_size,
302	       (unsigned) bogus_prio, bogus_tmout + 1, sprintrc(rc));
303
304	/* Partially invalid message (memory only partially available) */
305	rc = syscall(__NR_mq_timedsend, bogus_fd, msg + MSG_SIZE - MSG_CUT,
306		     MSG_SIZE, bogus_prio, bogus_tmout);
307	printf("mq_timedsend(%d, %p, %llu, %u, {tv_sec=%jd, tv_nsec=%jd}) = "
308	       "%s\n",
309	       (int) bogus_fd, msg + MSG_SIZE - MSG_CUT,
310	       (unsigned long long) MSG_SIZE, (unsigned) bogus_prio,
311	       (intmax_t) bogus_tmout->tv_sec, (intmax_t) bogus_tmout->tv_nsec,
312	       sprintrc(rc));
313
314	/* Fully valid message, uncut */
315	rc = syscall(__NR_mq_timedsend, bogus_fd, msg + MSG_SIZE - MSG_CUT,
316		     MSG_CUT, bogus_prio, bogus_tmout);
317	errstr = sprintrc(rc);
318	printf("mq_timedsend(%d, ", (int) bogus_fd);
319	printstr(MSG_START + MSG_SIZE - MSG_CUT, MSG_CUT);
320	printf(", %llu, %u, {tv_sec=%jd, tv_nsec=%jd}) = %s\n",
321	       (unsigned long long) MSG_CUT, (unsigned) bogus_prio,
322	       (intmax_t) bogus_tmout->tv_sec, (intmax_t) bogus_tmout->tv_nsec,
323	       errstr);
324
325	/* Partially invalid message, cut at maxstrlen */
326	rc = syscall(__NR_mq_timedsend, bogus_fd, msg + MSG_CUT, MSG_SIZE,
327		     bogus_prio, bogus_tmout);
328	errstr = sprintrc(rc);
329	printf("mq_timedsend(%d, ", (int) bogus_fd);
330	printstr(MSG_START + MSG_CUT, MSG_MAX_UNCUT);
331	printf("..., %llu, %u, {tv_sec=%jd, tv_nsec=%jd}) = %s\n",
332	       (unsigned long long) MSG_SIZE, (unsigned) bogus_prio,
333	       (intmax_t) bogus_tmout->tv_sec, (intmax_t) bogus_tmout->tv_nsec,
334	       errstr);
335
336
337	/* mq_timedreceive */
338
339	/* Zero values */
340	rc = syscall(__NR_mq_timedreceive, bogus_zero, NULL, bogus_zero_size,
341		     NULL, NULL);
342	printf("mq_timedreceive(0, NULL, 0, NULL, NULL) = %s\n", sprintrc(rc));
343
344	/* Invalid addresses */
345	rc = syscall(__NR_mq_timedreceive, bogus_fd, msg + MSG_SIZE, bogus_size,
346		     bogus_prio_ptr + 1, bogus_tmout + 1);
347	printf("mq_timedreceive(%d, %p, %llu, %p, %p) = %s\n",
348	       (int) bogus_fd, msg + MSG_SIZE, (unsigned long long) bogus_size,
349	       bogus_prio_ptr + 1, bogus_tmout + 1, sprintrc(rc));
350
351	/* Invalid fd, valid msg pointer */
352	rc = syscall(__NR_mq_timedreceive, bogus_fd, msg, bogus_size,
353		     bogus_prio_ptr, bogus_tmout);
354	printf("mq_timedreceive(%d, %p, %llu, %p, {tv_sec=%jd, tv_nsec=%jd}) = "
355	       "%s\n",
356	       (int) bogus_fd, msg, (unsigned long long) bogus_size,
357	       bogus_prio_ptr, (intmax_t) bogus_tmout->tv_sec,
358	       (intmax_t) bogus_tmout->tv_nsec, sprintrc(rc));
359
360
361	/* mq_notify */
362
363	/* Zero values */
364	rc = syscall(__NR_mq_notify, bogus_zero, NULL);
365	printf("mq_notify(0, NULL) = %s\n", sprintrc(rc));
366
367	/* Invalid pointer */
368	rc = syscall(__NR_mq_notify, bogus_fd, bogus_sev + 1);
369	printf("mq_notify(%d, %p) = %s\n",
370	       (int) bogus_fd, bogus_sev + 1, sprintrc(rc));
371
372	/* Invalid SIGEV_* */
373	rc = syscall(__NR_mq_notify, bogus_fd, bogus_sev);
374	printf("mq_notify(%d, {sigev_value={sival_int=%d, sival_ptr=%#lx}"
375	       ", sigev_signo=%u, sigev_notify=%#x /* SIGEV_??? */}) = %s\n",
376	       (int) bogus_fd, bogus_sev->sigev_value.sival_int,
377	       bogus_sev->sigev_value.sival_ptr,
378	       bogus_sev->sigev_signo, bogus_sev->sigev_notify,
379	       sprintrc(rc));
380
381	/* SIGEV_NONE */
382	bogus_sev->sigev_notify = SIGEV_NONE;
383	rc = syscall(__NR_mq_notify, bogus_fd, bogus_sev);
384	printf("mq_notify(%d, {sigev_value={sival_int=%d, sival_ptr=%#lx}, "
385	       "sigev_signo=%u, sigev_notify=SIGEV_NONE}) = %s\n",
386	       (int) bogus_fd, bogus_sev->sigev_value.sival_int,
387	       bogus_sev->sigev_value.sival_ptr,
388	       bogus_sev->sigev_signo, sprintrc(rc));
389
390	/* SIGEV_SIGNAL */
391	bogus_sev->sigev_notify = SIGEV_SIGNAL;
392	bogus_sev->sigev_signo = SIGALRM;
393	rc = syscall(__NR_mq_notify, bogus_fd, bogus_sev);
394	printf("mq_notify(%d, {sigev_value={sival_int=%d, sival_ptr=%#lx}, "
395	       "sigev_signo=SIGALRM, sigev_notify=SIGEV_SIGNAL}) = %s\n",
396	       (int) bogus_fd, bogus_sev->sigev_value.sival_int,
397	       bogus_sev->sigev_value.sival_ptr, sprintrc(rc));
398
399	/* SIGEV_THREAD */
400	bogus_sev->sigev_notify = SIGEV_THREAD;
401	bogus_sev->sigev_un.sigev_thread.function =
402		(unsigned long) 0xdeadbeefbadc0dedULL;
403	bogus_sev->sigev_un.sigev_thread.attribute =
404		(unsigned long) 0xcafef00dfacefeedULL;
405	rc = syscall(__NR_mq_notify, bogus_fd, bogus_sev);
406	printf("mq_notify(%d, {sigev_value={sival_int=%d, sival_ptr=%#lx}, "
407	       "sigev_signo=SIGALRM, sigev_notify=SIGEV_THREAD, "
408	       "sigev_notify_function=%#lx, sigev_notify_attributes=%#lx}) = "
409	       "%s\n",
410	       (int) bogus_fd, bogus_sev->sigev_value.sival_int,
411	       bogus_sev->sigev_value.sival_ptr,
412	       bogus_sev->sigev_un.sigev_thread.function,
413	       bogus_sev->sigev_un.sigev_thread.attribute, sprintrc(rc));
414
415	/* mq_unlink */
416
417	/* Zero values */
418	rc = syscall(__NR_mq_unlink, NULL);
419	printf("mq_unlink(NULL) = %s\n", sprintrc(rc));
420
421	/* Invalid ptr */
422	rc = syscall(__NR_mq_unlink, msg + MSG_SIZE);
423	printf("mq_unlink(%p) = %s\n", msg + MSG_SIZE, sprintrc(rc));
424
425	/* Long unterminated string */
426	rc = syscall(__NR_mq_unlink, msg);
427	errstr = sprintrc(rc);
428	printf("mq_unlink(%p) = %s\n", msg, errstr);
429
430
431	/* Sending and receiving test */
432
433# if DUMPIO_READ || DUMPIO_WRITE
434	close(0);
435# endif
436	bogus_attrs[1] = 2;
437	bogus_attrs[2] = MSG_SIZE;
438	fd = rc = syscall(__NR_mq_open, MQ_NAME,
439		          O_CREAT|O_RDWR|O_NONBLOCK, S_IRWXU, bogus_attrs);
440	errstr = sprintrc(rc);
441	if (rc < 0)
442		perror_msg_and_skip("mq_open");
443	else
444		atexit(cleanup);
445# if DUMPIO_READ || DUMPIO_WRITE
446	if (fd != 0)
447		error_msg_and_skip("mq_open returned fd other than 0");
448# endif
449	fill_memory_ex(bogus_attrs, sizeof(*bogus_attrs) * NUM_ATTRS,
450		       0xbb, 0x70);
451	printf("mq_open(\"" MQ_NAME "\", O_RDWR|O_CREAT|O_NONBLOCK, "
452	       "0700, {mq_flags=%#llx, mq_maxmsg=2, mq_msgsize=%u, "
453	       "mq_curmsgs=%lld}) = %s\n",
454	       (unsigned long long) (kernel_ulong_t) bogus_attrs[0], MSG_SIZE,
455	       (long long) bogus_attrs[3], errstr);
456
457	rc = syscall(__NR_mq_getsetattr, fd, NULL, bogus_attrs);
458	if (rc < 0)
459		perror_msg_and_skip("mq_getsetattr");
460	if ((bogus_attrs[1] < 2) || (bogus_attrs[2] < MSG_SIZE))
461		error_msg_and_skip("mq too small");
462
463	do_send(fd, msg, MSG_CUT, future_tmout, false);
464	do_send(fd, msg, MSG_SIZE, future_tmout, true);
465
466	memset(msg, '\0', MSG_SIZE);
467	do_recv(fd, msg, MSG_CUT, future_tmout, false);
468
469	memset(msg, '\0', MSG_SIZE);
470	do_recv(fd, msg, MSG_SIZE, future_tmout, true);
471
472	return 0;
473}
474
475#else
476
477SKIP_MAIN_UNDEFINED("__NR_mq_open && __NR_mq_timedsend && "
478	"__NR_mq_timedreceive && __NR_mq_notify && __NR_mq_unlink");
479
480#endif
481