1 /*
2 * Copyright (c) 2015 Elvira Khabirova <lineprinter0@gmail.com>
3 * Copyright (c) 2015-2016 Dmitry V. Levin <ldv@strace.io>
4 * Copyright (c) 2015-2021 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11 #include <errno.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <sys/msg.h>
15
16 #include "xlat.h"
17 #include "xlat/resource_flags.h"
18
19 #ifndef MSG_STAT_ANY
20 # define MSG_STAT_ANY 13
21 #endif
22
23 #undef TEST_MSGCTL_BOGUS_ADDR
24 #undef TEST_MSGCTL_BOGUS_CMD
25
26 /*
27 * Starting with commit glibc-2.32~83, on every 32-bit architecture
28 * where 32-bit time_t support is enabled, glibc tries to retrieve
29 * the data provided in the third argument of msgctl call.
30 */
31 #if GLIBC_PREREQ_GE(2, 32) && defined __TIMESIZE && __TIMESIZE != 64
32 # define TEST_MSGCTL_BOGUS_ADDR 0
33 #endif
34 /*
35 * Starting with commit glibc-2.31~358, on every architecture where
36 * __ASSUME_SYSVIPC_BROKEN_MODE_T is defined, glibc tries to modify
37 * the data provided in the third argument of msgctl call.
38 */
39 #if GLIBC_PREREQ_GE(2, 31) && \
40 (defined __m68k__ || defined __s390__ || \
41 (defined WORDS_BIGENDIAN && \
42 (defined __arm__ || defined __microblaze__ || defined __sh__)))
43 # define TEST_MSGCTL_BOGUS_ADDR 0
44 #endif
45 /*
46 * Before glibc-2.22-122-gbe48165, ppc64 code tried to retrieve data
47 * provided in third argument of msgctl call (in case of IPC_SET cmd)
48 * which led to segmentation fault.
49 */
50 #if GLIBC_PREREQ_LT(2, 23) && (defined POWERPC64 || defined POWERPC64LE)
51 # define TEST_MSGCTL_BOGUS_ADDR 0
52 #endif
53
54 /*
55 * Starting with commit glibc-2.32.9000-149-gbe9b0b9a012780a403a2,
56 * glibc skips msgctl syscall invocations and returns EINVAL
57 * for invalid msgctl commands.
58 *
59 * Apparently, this change was later backported to vendor packages, e.g.:
60 * Thu Mar 18 2021 Carlos O'Donell <carlos@redhat.com> - 2.28-153
61 * - Support SEM_STAT_ANY via semctl. Return EINVAL for unknown commands
62 * to semctl, msgctl, and shmctl. (#1912670)
63 */
64 #if GLIBC_PREREQ_GE(2, 28)
65 # define TEST_MSGCTL_BOGUS_CMD 0
66 #endif
67
68 #ifndef TEST_MSGCTL_BOGUS_ADDR
69 # define TEST_MSGCTL_BOGUS_ADDR 1
70 #endif
71 #ifndef TEST_MSGCTL_BOGUS_CMD
72 # define TEST_MSGCTL_BOGUS_CMD 1
73 #endif
74
75 #if XLAT_RAW
76 # define str_ipc_excl_nowait "0xface1c00"
77 # define str_ipc_private "0"
78 # define str_ipc_rmid "0"
79 # define str_ipc_set "0x1"
80 # define str_ipc_stat "0x2"
81 # define str_ipc_info "0x3"
82 # define str_msg_stat "0xb"
83 # define str_msg_info "0xc"
84 # define str_msg_stat_any "0xd"
85 # define str_ipc_64 "0x100"
86 # define str_bogus_cmd "0xdeadbeef"
87 #elif XLAT_VERBOSE
88 # define str_ipc_excl_nowait \
89 "0xface1c00 /\\* IPC_EXCL\\|IPC_NOWAIT\\|0xface1000 \\*/"
90 # define str_ipc_private "0 /\\* IPC_PRIVATE \\*/"
91 # define str_ipc_rmid "0 /\\* IPC_RMID \\*/"
92 # define str_ipc_set "0x1 /\\* IPC_SET \\*/"
93 # define str_ipc_stat "0x2 /\\* IPC_STAT \\*/"
94 # define str_ipc_info "0x3 /\\* IPC_INFO \\*/"
95 # define str_msg_stat "0xb /\\* MSG_STAT \\*/"
96 # define str_msg_info "0xc /\\* MSG_INFO \\*/"
97 # define str_msg_stat_any "0xd /\\* MSG_STAT_ANY \\*/"
98 # define str_ipc_64 "0x100 /\\* IPC_64 \\*/"
99 # define str_bogus_cmd "0xdeadbeef /\\* MSG_\\?\\?\\? \\*/"
100 #else
101 # define str_ipc_excl_nowait "IPC_EXCL\\|IPC_NOWAIT\\|0xface1000"
102 # define str_ipc_private "IPC_PRIVATE"
103 # define str_ipc_rmid "IPC_RMID"
104 # define str_ipc_set "IPC_SET"
105 # define str_ipc_stat "IPC_STAT"
106 # define str_ipc_info "IPC_INFO"
107 # define str_msg_stat "MSG_STAT"
108 # define str_msg_info "MSG_INFO"
109 # define str_msg_stat_any "MSG_STAT_ANY"
110 # define str_ipc_64 "IPC_64"
111 # define str_bogus_cmd "0xdeadbeef /\\* MSG_\\?\\?\\? \\*/"
112 #endif
113
114 static int id = -1;
115
116 static void
117 cleanup(void)
118 {
119 msgctl(id, IPC_RMID, NULL);
120 printf("msgctl\\(%d, (%s\\|)?%s, NULL\\) += 0\n",
121 id, str_ipc_64, str_ipc_rmid);
122 id = -1;
123 }
124
125 static void
126 print_msginfo(const char *const str_ipc_cmd,
127 const struct msginfo *const info,
128 const int rc)
129 {
130 if (rc < 0) {
131 printf("msgctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
132 id, str_ipc_64, str_ipc_cmd, info, sprintrc_grep(rc));
133 return;
134 }
135
136 printf("msgctl\\(%d, (%s\\|)?%s, \\{msgpool=%d, msgmap=%d, msgmax=%d"
137 ", msgmnb=%d, msgmni=%d, msgssz=%d, msgtql=%d, msgseg=%u\\}\\)"
138 " = %d\n",
139 id,
140 str_ipc_64,
141 str_ipc_cmd,
142 info->msgpool,
143 info->msgmap,
144 info->msgmax,
145 info->msgmnb,
146 info->msgmni,
147 info->msgssz,
148 info->msgtql,
149 (unsigned) info->msgseg,
150 rc);
151 }
152
153 static void
154 print_msqid_ds(const char *const str_ipc_cmd,
155 const struct msqid_ds *const ds,
156 const int rc)
157 {
158 if (rc < 0) {
159 printf("msgctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
160 id, str_ipc_64, str_ipc_cmd, ds, sprintrc_grep(rc));
161 return;
162 }
163
164 printf("msgctl\\(%d, (%s\\|)?%s, \\{msg_perm=\\{uid=%u"
165 ", gid=%u, mode=%#o, key=%u, cuid=%u, cgid=%u\\}"
166 ", msg_stime=%u, msg_rtime=%u, msg_ctime=%u, msg_qnum=%u"
167 ", msg_qbytes=%u, msg_lspid=%d, msg_lrpid=%d\\}\\) = %d\n",
168 id,
169 str_ipc_64,
170 str_ipc_cmd,
171 (unsigned) ds->msg_perm.uid,
172 (unsigned) ds->msg_perm.gid,
173 (unsigned) ds->msg_perm.mode,
174 (unsigned) ds->msg_perm.__key,
175 (unsigned) ds->msg_perm.cuid,
176 (unsigned) ds->msg_perm.cgid,
177 (unsigned) ds->msg_stime,
178 (unsigned) ds->msg_rtime,
179 (unsigned) ds->msg_ctime,
180 (unsigned) ds->msg_qnum,
181 (unsigned) ds->msg_qbytes,
182 (int) ds->msg_lspid,
183 (int) ds->msg_lrpid,
184 rc);
185 }
186
187 int
188 main(void)
189 {
190 static const key_t private_key =
191 (key_t) (0xffffffff00000000ULL | IPC_PRIVATE);
192 static const key_t bogus_key = (key_t) 0xeca86420fdb9f531ULL;
193 static const int bogus_flags = 0xface1e55 & ~IPC_CREAT;
194 #if TEST_MSGCTL_BOGUS_CMD || TEST_MSGCTL_BOGUS_ADDR
195 static const int bogus_msgid = 0xfdb97531;
196 #endif
197 #if TEST_MSGCTL_BOGUS_CMD
198 static const int bogus_cmd = 0xdeadbeef;
199 #endif
200 #if TEST_MSGCTL_BOGUS_ADDR
201 static void * const bogus_addr = (void *) -1L;
202 #endif
203
204 int rc;
205 union {
206 struct msqid_ds ds;
207 struct msginfo info;
208 } buf;
209
210 rc = msgget(bogus_key, bogus_flags);
211 printf("msgget\\(%#llx, %s\\|%#04o\\) = %s\n",
212 zero_extend_signed_to_ull(bogus_key),
213 str_ipc_excl_nowait,
214 bogus_flags & 0777, sprintrc_grep(rc));
215
216 id = msgget(private_key, 0600);
217 if (id < 0)
218 perror_msg_and_skip("msgget");
219 printf("msgget\\(%s, 0600\\) = %d\n", str_ipc_private, id);
220 atexit(cleanup);
221
222 #if TEST_MSGCTL_BOGUS_CMD
223 rc = msgctl(bogus_msgid, bogus_cmd, NULL);
224 printf("msgctl\\(%d, (%s\\|)?%s, NULL\\) = %s\n",
225 bogus_msgid, str_ipc_64, str_bogus_cmd, sprintrc_grep(rc));
226 #endif
227
228 #if TEST_MSGCTL_BOGUS_ADDR
229 rc = msgctl(bogus_msgid, IPC_SET, bogus_addr);
230 printf("msgctl\\(%d, (%s\\|)?%s, %p\\) = %s\n",
231 bogus_msgid, str_ipc_64, str_ipc_set, bogus_addr,
232 sprintrc_grep(rc));
233 #endif
234
235 rc = msgctl(id, IPC_STAT, &buf.ds);
236 if (rc < 0)
237 perror_msg_and_skip("msgctl IPC_STAT");
238 print_msqid_ds(str_ipc_stat, &buf.ds, rc);
239
240 if (msgctl(id, IPC_SET, &buf.ds))
241 perror_msg_and_skip("msgctl IPC_SET");
242 printf("msgctl\\(%d, (%s\\|)?%s, \\{msg_perm=\\{uid=%u"
243 ", gid=%u, mode=%#o\\}, msg_qbytes=%u\\}\\) = 0\n",
244 id, str_ipc_64, str_ipc_set,
245 (unsigned) buf.ds.msg_perm.uid,
246 (unsigned) buf.ds.msg_perm.gid,
247 (unsigned) buf.ds.msg_perm.mode,
248 (unsigned) buf.ds.msg_qbytes);
249
250 rc = msgctl(id, IPC_INFO, &buf.ds);
251 print_msginfo(str_ipc_info, &buf.info, rc);
252
253 rc = msgctl(id, MSG_INFO, &buf.ds);
254 print_msginfo(str_msg_info, &buf.info, rc);
255
256 rc = msgctl(id, MSG_STAT, &buf.ds);
257 print_msqid_ds(str_msg_stat, &buf.ds, rc);
258
259 rc = msgctl(id, MSG_STAT_ANY, &buf.ds);
260 print_msqid_ds(str_msg_stat_any, &buf.ds, rc);
261
262 return 0;
263 }