]> git.ipfire.org Git - thirdparty/glibc.git/blob - sysdeps/unix/sysv/linux/msgctl.c
sysvipc: Return EINVAL for invalid msgctl commands
[thirdparty/glibc.git] / sysdeps / unix / sysv / linux / msgctl.c
1 /* Copyright (C) 1995-2020 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 #include <sys/msg.h>
20 #include <ipc_priv.h>
21 #include <sysdep.h>
22 #include <shlib-compat.h>
23 #include <errno.h>
24 #include <linux/posix_types.h> /* For __kernel_mode_t. */
25
26 /* POSIX states ipc_perm mode should have type of mode_t. */
27 _Static_assert (sizeof ((struct msqid_ds){0}.msg_perm.mode)
28 == sizeof (mode_t),
29 "sizeof (msqid_ds.msg_perm.mode) != sizeof (mode_t)");
30
31 #if __IPC_TIME64 == 0
32 typedef struct msqid_ds msgctl_arg_t;
33 #else
34 # include <struct_kernel_msqid64_ds.h>
35
36 static void
37 msqid64_to_kmsqid64 (const struct __msqid64_ds *msqid64,
38 struct kernel_msqid64_ds *kmsqid)
39 {
40 kmsqid->msg_perm = msqid64->msg_perm;
41 kmsqid->msg_stime = msqid64->msg_stime;
42 kmsqid->msg_stime_high = msqid64->msg_stime >> 32;
43 kmsqid->msg_rtime = msqid64->msg_rtime;
44 kmsqid->msg_rtime_high = msqid64->msg_rtime >> 32;
45 kmsqid->msg_ctime = msqid64->msg_ctime;
46 kmsqid->msg_ctime_high = msqid64->msg_ctime >> 32;
47 kmsqid->msg_cbytes = msqid64->msg_cbytes;
48 kmsqid->msg_qnum = msqid64->msg_qnum;
49 kmsqid->msg_qbytes = msqid64->msg_qbytes;
50 kmsqid->msg_lspid = msqid64->msg_lspid;
51 kmsqid->msg_lrpid = msqid64->msg_lrpid;
52 }
53
54 static void
55 kmsqid64_to_msqid64 (const struct kernel_msqid64_ds *kmsqid,
56 struct __msqid64_ds *msqid64)
57 {
58 msqid64->msg_perm = kmsqid->msg_perm;
59 msqid64->msg_stime = kmsqid->msg_stime
60 | ((__time64_t) kmsqid->msg_stime_high << 32);
61 msqid64->msg_rtime = kmsqid->msg_rtime
62 | ((__time64_t) kmsqid->msg_rtime_high << 32);
63 msqid64->msg_ctime = kmsqid->msg_ctime
64 | ((__time64_t) kmsqid->msg_ctime_high << 32);
65 msqid64->msg_cbytes = kmsqid->msg_cbytes;
66 msqid64->msg_qnum = kmsqid->msg_qnum;
67 msqid64->msg_qbytes = kmsqid->msg_qbytes;
68 msqid64->msg_lspid = kmsqid->msg_lspid;
69 msqid64->msg_lrpid = kmsqid->msg_lrpid;
70 }
71
72 typedef struct kernel_msqid64_ds msgctl_arg_t;
73 #endif
74
75 static int
76 msgctl_syscall (int msqid, int cmd, msgctl_arg_t *buf)
77 {
78 #ifdef __ASSUME_DIRECT_SYSVIPC_SYSCALLS
79 return INLINE_SYSCALL_CALL (msgctl, msqid, cmd | __IPC_64, buf);
80 #else
81 return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd | __IPC_64, 0,
82 buf);
83 #endif
84 }
85
86 int
87 __msgctl64 (int msqid, int cmd, struct __msqid64_ds *buf)
88 {
89 #if __IPC_TIME64
90 struct kernel_msqid64_ds ksemid, *arg = NULL;
91 #else
92 msgctl_arg_t *arg;
93 #endif
94
95 switch (cmd)
96 {
97 case IPC_RMID:
98 arg = NULL;
99 break;
100
101 case IPC_SET:
102 case IPC_STAT:
103 case MSG_STAT:
104 case MSG_STAT_ANY:
105 #if __IPC_TIME64
106 if (buf != NULL)
107 {
108 msqid64_to_kmsqid64 (buf, &ksemid);
109 arg = &ksemid;
110 }
111 # ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
112 if (cmd == IPC_SET)
113 arg->msg_perm.mode *= 0x10000U;
114 # endif
115 #else
116 arg = buf;
117 #endif
118 break;
119
120 case IPC_INFO:
121 case MSG_INFO:
122 /* This is a Linux extension where kernel returns a 'struct msginfo'
123 instead. */
124 arg = (__typeof__ (arg)) buf;
125 break;
126
127 default:
128 __set_errno (EINVAL);
129 return -1;
130 }
131
132 int ret = msgctl_syscall (msqid, cmd, arg);
133 if (ret < 0)
134 return ret;
135
136 switch (cmd)
137 {
138 case IPC_STAT:
139 case MSG_STAT:
140 case MSG_STAT_ANY:
141 #ifdef __ASSUME_SYSVIPC_BROKEN_MODE_T
142 arg->msg_perm.mode >>= 16;
143 #else
144 /* Old Linux kernel versions might not clear the mode padding. */
145 if (sizeof ((struct msqid_ds){0}.msg_perm.mode)
146 != sizeof (__kernel_mode_t))
147 arg->msg_perm.mode &= 0xFFFF;
148 #endif
149
150 #if __IPC_TIME64
151 kmsqid64_to_msqid64 (arg, buf);
152 #endif
153 }
154
155 return ret;
156 }
157 #if __TIMESIZE != 64
158 libc_hidden_def (__msgctl64)
159
160 static void
161 msqid_to_msqid64 (struct __msqid64_ds *mq64, const struct msqid_ds *mq)
162 {
163 mq64->msg_perm = mq->msg_perm;
164 mq64->msg_stime = mq->msg_stime
165 | ((__time64_t) mq->__msg_stime_high << 32);
166 mq64->msg_rtime = mq->msg_rtime
167 | ((__time64_t) mq->__msg_rtime_high << 32);
168 mq64->msg_ctime = mq->msg_ctime
169 | ((__time64_t) mq->__msg_ctime_high << 32);
170 mq64->msg_cbytes = mq->msg_cbytes;
171 mq64->msg_qnum = mq->msg_qnum;
172 mq64->msg_qbytes = mq->msg_qbytes;
173 mq64->msg_lspid = mq->msg_lspid;
174 mq64->msg_lrpid = mq->msg_lrpid;
175 }
176
177 static void
178 msqid64_to_msqid (struct msqid_ds *mq, const struct __msqid64_ds *mq64)
179 {
180 mq->msg_perm = mq64->msg_perm;
181 mq->msg_stime = mq64->msg_stime;
182 mq->__msg_stime_high = 0;
183 mq->msg_rtime = mq64->msg_rtime;
184 mq->__msg_rtime_high = 0;
185 mq->msg_ctime = mq64->msg_ctime;
186 mq->__msg_ctime_high = 0;
187 mq->msg_cbytes = mq64->msg_cbytes;
188 mq->msg_qnum = mq64->msg_qnum;
189 mq->msg_qbytes = mq64->msg_qbytes;
190 mq->msg_lspid = mq64->msg_lspid;
191 mq->msg_lrpid = mq64->msg_lrpid;
192 }
193
194 int
195 __msgctl (int msqid, int cmd, struct msqid_ds *buf)
196 {
197 struct __msqid64_ds msqid64, *buf64 = NULL;
198 if (buf != NULL)
199 {
200 /* This is a Linux extension where kernel returns a 'struct msginfo'
201 instead. */
202 if (cmd == IPC_INFO || cmd == MSG_INFO)
203 buf64 = (struct __msqid64_ds *) buf;
204 else
205 {
206 msqid_to_msqid64 (&msqid64, buf);
207 buf64 = &msqid64;
208 }
209 }
210
211 int ret = __msgctl64 (msqid, cmd, buf64);
212 if (ret < 0)
213 return ret;
214
215 switch (cmd)
216 {
217 case IPC_STAT:
218 case MSG_STAT:
219 case MSG_STAT_ANY:
220 msqid64_to_msqid (buf, buf64);
221 }
222
223 return ret;
224 }
225 #endif
226
227 #ifndef DEFAULT_VERSION
228 # ifndef __ASSUME_SYSVIPC_BROKEN_MODE_T
229 # define DEFAULT_VERSION GLIBC_2_2
230 # else
231 # define DEFAULT_VERSION GLIBC_2_31
232 # endif
233 #endif
234 versioned_symbol (libc, __msgctl, msgctl, DEFAULT_VERSION);
235
236 #if defined __ASSUME_SYSVIPC_BROKEN_MODE_T \
237 && SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_31)
238 int
239 attribute_compat_text_section
240 __msgctl_mode16 (int msqid, int cmd, struct msqid_ds *buf)
241 {
242 return msgctl_syscall (msqid, cmd, (msgctl_arg_t *) buf);
243 }
244 compat_symbol (libc, __msgctl_mode16, msgctl, GLIBC_2_2);
245 #endif
246
247 #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
248 struct __old_msqid_ds
249 {
250 struct __old_ipc_perm msg_perm; /* structure describing operation permission */
251 struct msg *__msg_first; /* pointer to first message on queue */
252 struct msg *__msg_last; /* pointer to last message on queue */
253 __time_t msg_stime; /* time of last msgsnd command */
254 __time_t msg_rtime; /* time of last msgrcv command */
255 __time_t msg_ctime; /* time of last change */
256 struct wait_queue *__wwait; /* ??? */
257 struct wait_queue *__rwait; /* ??? */
258 unsigned short int __msg_cbytes; /* current number of bytes on queue */
259 unsigned short int msg_qnum; /* number of messages currently on queue */
260 unsigned short int msg_qbytes; /* max number of bytes allowed on queue */
261 __ipc_pid_t msg_lspid; /* pid of last msgsnd() */
262 __ipc_pid_t msg_lrpid; /* pid of last msgrcv() */
263 };
264
265 int
266 attribute_compat_text_section
267 __old_msgctl (int msqid, int cmd, struct __old_msqid_ds *buf)
268 {
269 #if defined __ASSUME_DIRECT_SYSVIPC_SYSCALLS \
270 && !defined __ASSUME_SYSVIPC_DEFAULT_IPC_64
271 /* For architecture that have wire-up msgctl but also have __IPC_64 to a
272 value different than default (0x0) it means the compat symbol used the
273 __NR_ipc syscall. */
274 return INLINE_SYSCALL_CALL (msgctl, msqid, cmd, buf);
275 #else
276 return INLINE_SYSCALL_CALL (ipc, IPCOP_msgctl, msqid, cmd, 0, buf);
277 #endif
278 }
279 compat_symbol (libc, __old_msgctl, msgctl, GLIBC_2_0);
280 #endif