]> sourceware.org Git - glibc.git/blame - hurd/intr-msg.c
syslog: Fix integer overflow in __vsyslog_internal (CVE-2023-6780)
[glibc.git] / hurd / intr-msg.c
CommitLineData
54da5be3 1/* Replacement for mach_msg used in interruptible Hurd RPCs.
dff8da6b 2 Copyright (C) 1995-2024 Free Software Foundation, Inc.
c84142e8 3 This file is part of the GNU C Library.
54da5be3 4
c84142e8 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
54da5be3 9
c84142e8
UD
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
41bdb6e2 13 Lesser General Public License for more details.
54da5be3 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
54da5be3
RM
18
19#include <mach.h>
84b4a81a 20#include <mach_rpc.h>
54da5be3 21#include <mach/mig_errors.h>
fb8e70d6 22#include <mach/mig_support.h>
54da5be3 23#include <hurd/signal.h>
38e30b39 24#include <assert.h>
54da5be3 25
3fe9de0d
RM
26#include "intr-msg.h"
27
54da5be3
RM
28error_t
29_hurd_intr_rpc_mach_msg (mach_msg_header_t *msg,
30 mach_msg_option_t option,
31 mach_msg_size_t send_size,
32 mach_msg_size_t rcv_size,
33 mach_port_t rcv_name,
34 mach_msg_timeout_t timeout,
35 mach_port_t notify)
36{
54da5be3 37 error_t err;
37eb58de 38 struct hurd_sigstate *ss;
38e30b39
RM
39 const mach_msg_option_t user_option = option;
40 const mach_msg_timeout_t user_timeout = timeout;
41
21297437
RM
42 struct clobber
43 {
21297437 44 mach_msg_type_t type;
21297437
RM
45 error_t err;
46 };
38e30b39
RM
47 union msg
48 {
49 mach_msg_header_t header;
50 mig_reply_header_t reply;
51 struct
52 {
53 mach_msg_header_t header;
4571fb8f 54 mach_msg_type_t type;
38e30b39
RM
55 int code;
56 } check;
57 struct
37eb58de 58 {
38e30b39
RM
59 mach_msg_header_t header;
60 struct clobber data;
61 } request;
62 };
63 union msg *const m = (void *) msg;
64 mach_msg_bits_t msgh_bits;
65 mach_port_t remote_port;
66 mach_msg_id_t msgid;
67 struct clobber save_data;
68
69 if ((option & (MACH_SEND_MSG|MACH_RCV_MSG)) != (MACH_SEND_MSG|MACH_RCV_MSG)
70 || _hurd_msgport_thread == MACH_PORT_NULL)
71 {
72 /* Either this is not an RPC (i.e., only a send or only a receive),
73 so it can't be interruptible; or, the signal thread is not set up
74 yet, so we cannot do the normal signal magic. Do a normal,
75 uninterruptible mach_msg call instead. */
76 return __mach_msg (&m->header, option, send_size, rcv_size, rcv_name,
37eb58de
RM
77 timeout, notify);
78 }
79
80 ss = _hurd_self_sigstate ();
81
38e30b39
RM
82 /* Save state that gets clobbered by an EINTR reply message.
83 We will need to restore it if we want to retry the RPC. */
84 msgh_bits = m->header.msgh_bits;
85 remote_port = m->header.msgh_remote_port;
86 msgid = m->header.msgh_id;
87 assert (rcv_size >= sizeof m->request);
88 save_data = m->request.data;
54da5be3
RM
89
90 /* Tell the signal thread that we are doing an interruptible RPC on
91 this port. If we get a signal and should return EINTR, the signal
92 thread will set this variable to MACH_PORT_NULL. The RPC might
93 return EINTR when some other thread gets a signal, in which case we
94 want to restart our call. */
38e30b39 95 ss->intr_port = m->header.msgh_remote_port;
fb8e70d6 96
38e30b39
RM
97 /* A signal may arrive here, after intr_port is set, but before the
98 mach_msg system call. The signal handler might do an interruptible
99 RPC, and clobber intr_port; then it would not be set properly when we
100 actually did send the RPC, and a later signal wouldn't interrupt that
101 RPC. So, _hurd_setup_sighandler saves intr_port in the sigcontext,
102 and sigreturn restores it. */
54da5be3
RM
103
104 message:
105
38e30b39 106 /* Note that the signal trampoline code might modify our OPTION! */
fb8e70d6 107 err = INTR_MSG_TRAP (msg, option, send_size,
5c81be53
ST
108 rcv_size, rcv_name, timeout, notify,
109 &ss->cancel, &ss->intr_port);
54da5be3
RM
110
111 switch (err)
112 {
a993273c 113 case MACH_RCV_TIMED_OUT:
38e30b39 114 if (user_option & MACH_RCV_TIMEOUT)
a993273c
RM
115 /* The real user RPC timed out. */
116 break;
117 else
118 /* The operation was supposedly interrupted, but still has
119 not returned. Declare it interrupted. */
2d0d1d38 120 goto dead;
a993273c 121
54da5be3 122 case MACH_SEND_INTERRUPTED: /* RPC didn't get out. */
38e30b39
RM
123 if (!(option & MACH_SEND_MSG))
124 {
125 /* Oh yes, it did! Since we were not doing a message send,
126 this return code cannot have come from the kernel!
127 Instead, it was the signal thread mutating our state to tell
128 us not to enter this RPC. However, we are already in the receive!
129 Since the signal thread thought we weren't in the RPC yet,
130 it didn't do an interrupt_operation.
131 XXX */
132 goto retry_receive;
133 }
8f22e362
ST
134 if (!(option & MACH_SEND_INTERRUPT))
135 {
136 option = user_option;
137 timeout = user_timeout;
138 goto message;
139 }
38e30b39
RM
140 /* FALLTHROUGH */
141
024c4ccb
RM
142 /* These are the other codes that mean a pseudo-receive modified
143 the message buffer and we might need to clean up the port rights. */
144 case MACH_SEND_TIMED_OUT:
145 case MACH_SEND_INVALID_NOTIFY:
21297437 146#ifdef MACH_SEND_NO_NOTIFY
024c4ccb 147 case MACH_SEND_NO_NOTIFY:
21297437
RM
148#endif
149#ifdef MACH_SEND_NOTIFY_IN_PROGRESS
024c4ccb 150 case MACH_SEND_NOTIFY_IN_PROGRESS:
21297437 151#endif
024c4ccb
RM
152 if (MACH_MSGH_BITS_REMOTE (msg->msgh_bits) == MACH_MSG_TYPE_MOVE_SEND)
153 {
154 __mach_port_deallocate (__mach_task_self (), msg->msgh_remote_port);
155 msg->msgh_bits
156 = (MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
157 MACH_MSGH_BITS_LOCAL (msg->msgh_bits))
158 | MACH_MSGH_BITS_OTHER (msg->msgh_bits));
159 }
160 if (msg->msgh_bits & MACH_MSGH_BITS_COMPLEX)
161 {
162 /* Check for MOVE_SEND rights in the message. These hold refs
163 that we need to release in case the message is in fact never
164 re-sent later. Since it might in fact be re-sent, we turn
165 these into COPY_SEND's after deallocating the extra user ref;
166 the caller is responsible for still holding a ref to go with
167 the original COPY_SEND right, so the resend copies it again. */
168
169 mach_msg_type_long_t *ty = (void *) (msg + 1);
170 while ((void *) ty < (void *) msg + msg->msgh_size)
171 {
172 mach_msg_type_name_t name;
173 mach_msg_type_size_t size;
174 mach_msg_type_number_t number;
175
f11a9299
FC
176 inline void clean_ports_and_memory (char *data, const vm_size_t length,
177 int dealloc)
024c4ccb
RM
178 {
179 mach_msg_type_number_t i;
180 switch (name)
181 {
182 case MACH_MSG_TYPE_MOVE_SEND:
f11a9299 183 mach_port_t *ports = (mach_port_t *) data;
024c4ccb
RM
184 for (i = 0; i < number; i++)
185 __mach_port_deallocate (__mach_task_self (), *ports++);
4e4c417b
RM
186 if (ty->msgtl_header.msgt_longform)
187 ty->msgtl_name = MACH_MSG_TYPE_COPY_SEND;
188 else
189 ty->msgtl_header.msgt_name = MACH_MSG_TYPE_COPY_SEND;
024c4ccb
RM
190 break;
191 case MACH_MSG_TYPE_COPY_SEND:
192 case MACH_MSG_TYPE_MOVE_RECEIVE:
193 break;
194 default:
195 if (MACH_MSG_TYPE_PORT_ANY (name))
196 assert (! "unexpected port type in interruptible RPC");
197 }
198 if (dealloc)
f11a9299 199 __vm_deallocate (__mach_task_self (), (vm_address_t) data, length);
024c4ccb
RM
200 }
201
ad26c251
FC
202 inline void clean_inlined_ports (mach_port_name_inlined_t *ports)
203 {
204 mach_msg_type_number_t i;
205 switch (name)
206 {
207 case MACH_MSG_TYPE_MOVE_SEND:
208 for (i = 0; i < number; i++)
209 __mach_port_deallocate (__mach_task_self (), ports[i].name);
210 if (ty->msgtl_header.msgt_longform)
211 ty->msgtl_name = MACH_MSG_TYPE_COPY_SEND;
212 else
213 ty->msgtl_header.msgt_name = MACH_MSG_TYPE_COPY_SEND;
214 break;
215 case MACH_MSG_TYPE_COPY_SEND:
216 case MACH_MSG_TYPE_MOVE_RECEIVE:
217 break;
218 default:
219 if (MACH_MSG_TYPE_PORT_ANY (name))
220 assert (! "unexpected port type in interruptible RPC");
221 }
222 }
223
f11a9299 224 char *data;
024c4ccb
RM
225 if (ty->msgtl_header.msgt_longform)
226 {
227 name = ty->msgtl_name;
228 size = ty->msgtl_size;
229 number = ty->msgtl_number;
f11a9299 230 data = (char *) ty + sizeof (mach_msg_type_long_t);
024c4ccb
RM
231 }
232 else
233 {
234 name = ty->msgtl_header.msgt_name;
235 size = ty->msgtl_header.msgt_size;
236 number = ty->msgtl_header.msgt_number;
f11a9299 237 data = (char *) ty + sizeof (mach_msg_type_t);
024c4ccb
RM
238 }
239
ad26c251 240 /* Calculate length of data in bytes. */
f11a9299 241 const vm_size_t length = ((number * size) + 7) >> 3;
024c4ccb
RM
242 if (ty->msgtl_header.msgt_inline)
243 {
ad26c251 244 clean_inlined_ports ((mach_port_name_inlined_t *) data);
4571fb8f 245 /* Move to the next argument. */
f11a9299 246 ty = (void *) PTR_ALIGN_UP (data + length, __alignof__ (uintptr_t));
024c4ccb
RM
247 }
248 else
ca18306b 249 {
f11a9299 250 clean_ports_and_memory (*(void **) data, length,
ca18306b 251 ty->msgtl_header.msgt_deallocate);
f11a9299 252 ty = (void *) data + sizeof (void *);
ca18306b 253 }
024c4ccb
RM
254 }
255 }
256 break;
257
38e30b39
RM
258 case EINTR:
259 /* Either the process was stopped and continued,
260 or the server doesn't support interrupt_operation. */
54da5be3
RM
261 if (ss->intr_port != MACH_PORT_NULL)
262 /* If this signal was for us and it should interrupt calls, the
263 signal thread will have cleared SS->intr_port.
264 Since it's not cleared, the signal was for another thread,
265 or SA_RESTART is set. Restart the interrupted call. */
fb8e70d6 266 {
38e30b39
RM
267 /* Make sure we have a valid reply port. The one we were using
268 may have been destroyed by interruption. */
4e506f67 269 __mig_dealloc_reply_port (rcv_name);
38e30b39
RM
270 m->header.msgh_local_port = rcv_name = __mig_get_reply_port ();
271 m->header.msgh_bits = msgh_bits;
272 option = user_option;
273 timeout = user_timeout;
fb8e70d6
RM
274 goto message;
275 }
2d0d1d38
ST
276 err = EINTR;
277
278 /* The EINTR return indicates cancellation, so clear the flag. */
279 ss->cancel = 0;
280 break;
54da5be3
RM
281
282 case MACH_RCV_PORT_DIED:
283 /* Server didn't respond to interrupt_operation,
284 so the signal thread destroyed the reply port. */
285 /* FALLTHROUGH */
286
2d0d1d38 287 dead:
b6e7c449 288 err = EIEIO;
54da5be3 289
b6e7c449 290 /* The EIEIO return indicates cancellation, so clear the flag. */
54da5be3
RM
291 ss->cancel = 0;
292 break;
293
294 case MACH_RCV_INTERRUPTED: /* RPC sent; no reply. */
295 option &= ~MACH_SEND_MSG; /* Don't send again. */
38e30b39 296 retry_receive:
54da5be3
RM
297 if (ss->intr_port == MACH_PORT_NULL)
298 {
299 /* This signal or cancellation was for us. We need to wait for
300 the reply, but not hang forever. */
301 option |= MACH_RCV_TIMEOUT;
a993273c 302 /* Never decrease the user's timeout. */
38e30b39
RM
303 if (!(user_option & MACH_RCV_TIMEOUT)
304 || timeout > _hurd_interrupted_rpc_timeout)
a993273c 305 timeout = _hurd_interrupted_rpc_timeout;
54da5be3 306 }
38e30b39
RM
307 else
308 {
309 option = user_option;
310 timeout = user_timeout;
311 }
54da5be3
RM
312 goto message; /* Retry the receive. */
313
314 case MACH_MSG_SUCCESS:
38e30b39
RM
315 {
316 /* We got a reply. Was it EINTR? */
21297437 317#ifdef MACH_MSG_TYPE_BIT
4571fb8f
FC
318 static const mach_msg_type_t type_check = {
319 .msgt_name = MACH_MSG_TYPE_INTEGER_T,
320 .msgt_size = sizeof (integer_t) * 8,
321 .msgt_number = 1,
322 .msgt_inline = TRUE,
323 .msgt_longform = FALSE,
324 .msgt_deallocate = FALSE,
325 .msgt_unused = 0
326 };
21297437 327#endif
38e30b39 328
34a5a146
JM
329 if (m->reply.RetCode == EINTR
330 && m->header.msgh_size == sizeof m->reply
21297437 331#ifdef MACH_MSG_TYPE_BIT
4571fb8f 332 && !BAD_TYPECHECK(&m->check.type, &type_check)
21297437 333#endif
34a5a146 334 && !(m->header.msgh_bits & MACH_MSGH_BITS_COMPLEX))
38e30b39
RM
335 {
336 /* It is indeed EINTR. Is the interrupt for us? */
337 if (ss->intr_port != MACH_PORT_NULL)
338 {
54da5be3
RM
339 /* Nope; repeat the RPC.
340 XXX Resources moved? */
38e30b39
RM
341
342 assert (m->header.msgh_id == msgid + 100);
343
344 /* We know we have a valid reply port, because we just
345 received the EINTR reply on it. Restore it and the
346 other fields in the message header needed for send,
347 since the header now reflects receipt of the reply. */
348 m->header.msgh_local_port = rcv_name;
349 m->header.msgh_remote_port = remote_port;
350 m->header.msgh_id = msgid;
351 m->header.msgh_bits = msgh_bits;
352 /* Restore the two words clobbered by the reply data. */
353 m->request.data = save_data;
354
355 /* Restore the original mach_msg options.
356 OPTION may have had MACH_RCV_TIMEOUT added,
357 and/or MACH_SEND_MSG removed. */
358 option = user_option;
359 timeout = user_timeout;
360
361 /* Now we are ready to repeat the original message send. */
362 goto message;
363 }
364 else
365 /* The EINTR return indicates cancellation,
366 so clear the flag. */
367 ss->cancel = 0;
368 }
369 }
54da5be3
RM
370 break;
371
372 default: /* Quiet -Wswitch-enum. */
ca18306b 373 break;
54da5be3
RM
374 }
375
376 ss->intr_port = MACH_PORT_NULL;
377
378 return err;
379}
7a8f45e3 380libc_hidden_def (_hurd_intr_rpc_mach_msg)
This page took 0.631215 seconds and 5 git commands to generate.