xf.li | bdd93d5 | 2023-05-12 07:10:14 -0700 | [diff] [blame] | 1 | /* Copyright (C) 1991-2016 Free Software Foundation, Inc. |
| 2 | This file is part of the GNU C Library. |
| 3 | |
| 4 | The GNU C Library is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU Lesser General Public |
| 6 | License as published by the Free Software Foundation; either |
| 7 | version 2.1 of the License, or (at your option) any later version. |
| 8 | |
| 9 | The GNU C Library 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 GNU |
| 12 | Lesser General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU Lesser General Public |
| 15 | License along with the GNU C Library; if not, see |
| 16 | <http://www.gnu.org/licenses/>. */ |
| 17 | |
| 18 | #include <errno.h> |
| 19 | #include <unistd.h> |
| 20 | #include <fcntl.h> |
| 21 | #include <limits.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
| 24 | #include <hurd.h> |
| 25 | #include <hurd/fd.h> |
| 26 | #include <hurd/signal.h> |
| 27 | #include <hurd/id.h> |
| 28 | #include <assert.h> |
| 29 | #include <argz.h> |
| 30 | |
| 31 | /* Overlay TASK, executing FILE with arguments ARGV and environment ENVP. |
| 32 | If TASK == mach_task_self (), some ports are dealloc'd by the exec server. |
| 33 | ARGV and ENVP are terminated by NULL pointers. */ |
| 34 | error_t |
| 35 | _hurd_exec (task_t task, file_t file, |
| 36 | char *const argv[], char *const envp[]) |
| 37 | { |
| 38 | error_t err; |
| 39 | char *args, *env; |
| 40 | size_t argslen, envlen; |
| 41 | int ints[INIT_INT_MAX]; |
| 42 | mach_port_t ports[_hurd_nports]; |
| 43 | struct hurd_userlink ulink_ports[_hurd_nports]; |
| 44 | inline void free_port (unsigned int i) |
| 45 | { |
| 46 | _hurd_port_free (&_hurd_ports[i], &ulink_ports[i], ports[i]); |
| 47 | } |
| 48 | file_t *dtable; |
| 49 | unsigned int dtablesize, i; |
| 50 | struct hurd_port **dtable_cells; |
| 51 | struct hurd_userlink *ulink_dtable; |
| 52 | struct hurd_sigstate *ss; |
| 53 | mach_port_t *please_dealloc, *pdp; |
| 54 | int reauth = 0; |
| 55 | |
| 56 | /* XXX needs to be hurdmalloc XXX */ |
| 57 | if (argv == NULL) |
| 58 | args = NULL, argslen = 0; |
| 59 | else if (err = __argz_create (argv, &args, &argslen)) |
| 60 | return err; |
| 61 | if (envp == NULL) |
| 62 | env = NULL, envlen = 0; |
| 63 | else if (err = __argz_create (envp, &env, &envlen)) |
| 64 | goto outargs; |
| 65 | |
| 66 | /* Load up the ports to give to the new program. */ |
| 67 | for (i = 0; i < _hurd_nports; ++i) |
| 68 | if (i == INIT_PORT_PROC && task != __mach_task_self ()) |
| 69 | { |
| 70 | /* This is another task, so we need to ask the proc server |
| 71 | for the right proc server port for it. */ |
| 72 | if (err = __USEPORT (PROC, __proc_task2proc (port, task, &ports[i]))) |
| 73 | { |
| 74 | while (--i > 0) |
| 75 | free_port (i); |
| 76 | goto outenv; |
| 77 | } |
| 78 | } |
| 79 | else |
| 80 | ports[i] = _hurd_port_get (&_hurd_ports[i], &ulink_ports[i]); |
| 81 | |
| 82 | |
| 83 | /* Load up the ints to give the new program. */ |
| 84 | for (i = 0; i < INIT_INT_MAX; ++i) |
| 85 | switch (i) |
| 86 | { |
| 87 | case INIT_UMASK: |
| 88 | ints[i] = _hurd_umask; |
| 89 | break; |
| 90 | |
| 91 | case INIT_SIGMASK: |
| 92 | case INIT_SIGIGN: |
| 93 | case INIT_SIGPENDING: |
| 94 | /* We will set these all below. */ |
| 95 | break; |
| 96 | |
| 97 | case INIT_TRACEMASK: |
| 98 | ints[i] = _hurdsig_traced; |
| 99 | break; |
| 100 | |
| 101 | default: |
| 102 | ints[i] = 0; |
| 103 | } |
| 104 | |
| 105 | ss = _hurd_self_sigstate (); |
| 106 | |
| 107 | assert (! __spin_lock_locked (&ss->critical_section_lock)); |
| 108 | __spin_lock (&ss->critical_section_lock); |
| 109 | |
| 110 | __spin_lock (&ss->lock); |
| 111 | ints[INIT_SIGMASK] = ss->blocked; |
| 112 | ints[INIT_SIGPENDING] = ss->pending; |
| 113 | ints[INIT_SIGIGN] = 0; |
| 114 | for (i = 1; i < NSIG; ++i) |
| 115 | if (ss->actions[i].sa_handler == SIG_IGN) |
| 116 | ints[INIT_SIGIGN] |= __sigmask (i); |
| 117 | |
| 118 | /* We hold the sigstate lock until the exec has failed so that no signal |
| 119 | can arrive between when we pack the blocked and ignored signals, and |
| 120 | when the exec actually happens. A signal handler could change what |
| 121 | signals are blocked and ignored. Either the change will be reflected |
| 122 | in the exec, or the signal will never be delivered. Setting the |
| 123 | critical section flag avoids anything we call trying to acquire the |
| 124 | sigstate lock. */ |
| 125 | |
| 126 | __spin_unlock (&ss->lock); |
| 127 | |
| 128 | /* Pack up the descriptor table to give the new program. */ |
| 129 | __mutex_lock (&_hurd_dtable_lock); |
| 130 | |
| 131 | dtablesize = _hurd_dtable ? _hurd_dtablesize : _hurd_init_dtablesize; |
| 132 | |
| 133 | if (task == __mach_task_self ()) |
| 134 | /* Request the exec server to deallocate some ports from us if the exec |
| 135 | succeeds. The init ports and descriptor ports will arrive in the |
| 136 | new program's exec_startup message. If we failed to deallocate |
| 137 | them, the new program would have duplicate user references for them. |
| 138 | But we cannot deallocate them ourselves, because we must still have |
| 139 | them after a failed exec call. */ |
| 140 | please_dealloc = __alloca ((_hurd_nports + 3 + (3 * dtablesize)) |
| 141 | * sizeof (mach_port_t)); |
| 142 | else |
| 143 | please_dealloc = NULL; |
| 144 | pdp = please_dealloc; |
| 145 | |
| 146 | if (_hurd_dtable != NULL) |
| 147 | { |
| 148 | dtable = __alloca (dtablesize * sizeof (dtable[0])); |
| 149 | ulink_dtable = __alloca (dtablesize * sizeof (ulink_dtable[0])); |
| 150 | dtable_cells = __alloca (dtablesize * sizeof (dtable_cells[0])); |
| 151 | for (i = 0; i < dtablesize; ++i) |
| 152 | { |
| 153 | struct hurd_fd *const d = _hurd_dtable[i]; |
| 154 | if (d == NULL) |
| 155 | { |
| 156 | dtable[i] = MACH_PORT_NULL; |
| 157 | continue; |
| 158 | } |
| 159 | __spin_lock (&d->port.lock); |
| 160 | if (d->flags & FD_CLOEXEC) |
| 161 | { |
| 162 | /* This descriptor is marked to be closed on exec. |
| 163 | So don't pass it to the new program. */ |
| 164 | dtable[i] = MACH_PORT_NULL; |
| 165 | if (pdp && d->port.port != MACH_PORT_NULL) |
| 166 | { |
| 167 | /* We still need to deallocate the ports. */ |
| 168 | *pdp++ = d->port.port; |
| 169 | if (d->ctty.port != MACH_PORT_NULL) |
| 170 | *pdp++ = d->ctty.port; |
| 171 | } |
| 172 | __spin_unlock (&d->port.lock); |
| 173 | } |
| 174 | else |
| 175 | { |
| 176 | if (pdp && d->ctty.port != MACH_PORT_NULL) |
| 177 | /* All the elements of DTABLE are added to PLEASE_DEALLOC |
| 178 | below, so we needn't add the port itself. |
| 179 | But we must deallocate the ctty port as well as |
| 180 | the normal port that got installed in DTABLE[I]. */ |
| 181 | *pdp++ = d->ctty.port; |
| 182 | dtable[i] = _hurd_port_locked_get (&d->port, &ulink_dtable[i]); |
| 183 | dtable_cells[i] = &d->port; |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | else |
| 188 | { |
| 189 | dtable = _hurd_init_dtable; |
| 190 | ulink_dtable = NULL; |
| 191 | dtable_cells = NULL; |
| 192 | } |
| 193 | |
| 194 | /* Prune trailing null ports from the descriptor table. */ |
| 195 | while (dtablesize > 0 && dtable[dtablesize - 1] == MACH_PORT_NULL) |
| 196 | --dtablesize; |
| 197 | |
| 198 | /* See if we need to diddle the auth port of the new program. |
| 199 | The purpose of this is to get the effect setting the saved-set UID and |
| 200 | GID to the respective effective IDs after the exec, as POSIX.1 requires. |
| 201 | Note that we don't reauthenticate with the proc server; that would be a |
| 202 | no-op since it only keeps track of the effective UIDs, and if it did |
| 203 | keep track of the available IDs we would have the problem that we'd be |
| 204 | changing the IDs before the exec and have to change them back after a |
| 205 | failure. Arguably we could skip all the reauthentications because the |
| 206 | available IDs have no bearing on any filesystem. But the conservative |
| 207 | approach is to reauthenticate all the io ports so that no state anywhere |
| 208 | reflects that our whole ID set differs from what we've set it to. */ |
| 209 | __mutex_lock (&_hurd_id.lock); |
| 210 | err = _hurd_check_ids (); |
| 211 | if (err == 0 && ((_hurd_id.aux.nuids >= 2 && _hurd_id.gen.nuids >= 1 |
| 212 | && _hurd_id.aux.uids[1] != _hurd_id.gen.uids[0]) |
| 213 | || (_hurd_id.aux.ngids >= 2 && _hurd_id.gen.ngids >= 1 |
| 214 | && _hurd_id.aux.gids[1] != _hurd_id.gen.gids[0]))) |
| 215 | { |
| 216 | /* We have euid != svuid or egid != svgid. POSIX.1 says that exec |
| 217 | sets svuid = euid and svgid = egid. So we must get a new auth |
| 218 | port and reauthenticate everything with it. We'll pass the new |
| 219 | ports in file_exec instead of our own ports. */ |
| 220 | |
| 221 | auth_t newauth; |
| 222 | |
| 223 | _hurd_id.aux.uids[1] = _hurd_id.gen.uids[0]; |
| 224 | _hurd_id.aux.gids[1] = _hurd_id.gen.gids[0]; |
| 225 | _hurd_id.valid = 0; |
| 226 | if (_hurd_id.rid_auth != MACH_PORT_NULL) |
| 227 | { |
| 228 | __mach_port_deallocate (__mach_task_self (), _hurd_id.rid_auth); |
| 229 | _hurd_id.rid_auth = MACH_PORT_NULL; |
| 230 | } |
| 231 | |
| 232 | err = __auth_makeauth (ports[INIT_PORT_AUTH], |
| 233 | NULL, MACH_MSG_TYPE_COPY_SEND, 0, |
| 234 | _hurd_id.gen.uids, _hurd_id.gen.nuids, |
| 235 | _hurd_id.aux.uids, _hurd_id.aux.nuids, |
| 236 | _hurd_id.gen.gids, _hurd_id.gen.ngids, |
| 237 | _hurd_id.aux.gids, _hurd_id.aux.ngids, |
| 238 | &newauth); |
| 239 | if (err == 0) |
| 240 | { |
| 241 | /* Now we have to reauthenticate the ports with this new ID. |
| 242 | */ |
| 243 | |
| 244 | inline error_t reauth_io (io_t port, io_t *newport) |
| 245 | { |
| 246 | mach_port_t ref = __mach_reply_port (); |
| 247 | *newport = MACH_PORT_NULL; |
| 248 | error_t err = __io_reauthenticate (port, |
| 249 | ref, MACH_MSG_TYPE_MAKE_SEND); |
| 250 | if (!err) |
| 251 | err = __auth_user_authenticate (newauth, |
| 252 | ref, MACH_MSG_TYPE_MAKE_SEND, |
| 253 | newport); |
| 254 | __mach_port_destroy (__mach_task_self (), ref); |
| 255 | return err; |
| 256 | } |
| 257 | inline void reauth_port (unsigned int idx) |
| 258 | { |
| 259 | io_t newport; |
| 260 | err = reauth_io (ports[idx], &newport) ?: err; |
| 261 | if (pdp) |
| 262 | *pdp++ = ports[idx]; /* XXX presumed still in _hurd_ports */ |
| 263 | free_port (idx); |
| 264 | ports[idx] = newport; |
| 265 | } |
| 266 | |
| 267 | if (pdp) |
| 268 | *pdp++ = ports[INIT_PORT_AUTH]; |
| 269 | free_port (INIT_PORT_AUTH); |
| 270 | ports[INIT_PORT_AUTH] = newauth; |
| 271 | |
| 272 | reauth_port (INIT_PORT_CRDIR); |
| 273 | reauth_port (INIT_PORT_CWDIR); |
| 274 | |
| 275 | if (!err) |
| 276 | { |
| 277 | /* Now we'll reauthenticate each file descriptor. */ |
| 278 | if (ulink_dtable == NULL) |
| 279 | { |
| 280 | assert (dtable == _hurd_init_dtable); |
| 281 | dtable = __alloca (dtablesize * sizeof (dtable[0])); |
| 282 | for (i = 0; i < dtablesize; ++i) |
| 283 | if (_hurd_init_dtable[i] != MACH_PORT_NULL) |
| 284 | { |
| 285 | if (pdp) |
| 286 | *pdp++ = _hurd_init_dtable[i]; |
| 287 | err = reauth_io (_hurd_init_dtable[i], &dtable[i]); |
| 288 | if (err) |
| 289 | { |
| 290 | while (++i < dtablesize) |
| 291 | dtable[i] = MACH_PORT_NULL; |
| 292 | break; |
| 293 | } |
| 294 | } |
| 295 | else |
| 296 | dtable[i] = MACH_PORT_NULL; |
| 297 | } |
| 298 | else |
| 299 | { |
| 300 | if (pdp) |
| 301 | { |
| 302 | /* Ask to deallocate all the old fd ports, |
| 303 | since we will have new ones in DTABLE. */ |
| 304 | memcpy (pdp, dtable, dtablesize * sizeof pdp[0]); |
| 305 | pdp += dtablesize; |
| 306 | } |
| 307 | for (i = 0; i < dtablesize; ++i) |
| 308 | if (dtable[i] != MACH_PORT_NULL) |
| 309 | { |
| 310 | io_t newport; |
| 311 | err = reauth_io (dtable[i], &newport); |
| 312 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], |
| 313 | dtable[i]); |
| 314 | dtable[i] = newport; |
| 315 | if (err) |
| 316 | { |
| 317 | while (++i < dtablesize) |
| 318 | _hurd_port_free (dtable_cells[i], |
| 319 | &ulink_dtable[i], dtable[i]); |
| 320 | break; |
| 321 | } |
| 322 | } |
| 323 | ulink_dtable = NULL; |
| 324 | dtable_cells = NULL; |
| 325 | } |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | reauth = 1; |
| 330 | } |
| 331 | __mutex_unlock (&_hurd_id.lock); |
| 332 | |
| 333 | /* The information is all set up now. Try to exec the file. */ |
| 334 | if (!err) |
| 335 | { |
| 336 | int flags; |
| 337 | |
| 338 | if (pdp) |
| 339 | { |
| 340 | /* Request the exec server to deallocate some ports from us if |
| 341 | the exec succeeds. The init ports and descriptor ports will |
| 342 | arrive in the new program's exec_startup message. If we |
| 343 | failed to deallocate them, the new program would have |
| 344 | duplicate user references for them. But we cannot deallocate |
| 345 | them ourselves, because we must still have them after a failed |
| 346 | exec call. */ |
| 347 | |
| 348 | for (i = 0; i < _hurd_nports; ++i) |
| 349 | *pdp++ = ports[i]; |
| 350 | for (i = 0; i < dtablesize; ++i) |
| 351 | *pdp++ = dtable[i]; |
| 352 | } |
| 353 | |
| 354 | flags = 0; |
| 355 | #ifdef EXEC_SIGTRAP |
| 356 | /* PTRACE_TRACEME sets all bits in _hurdsig_traced, which is |
| 357 | propagated through exec by INIT_TRACEMASK, so this checks if |
| 358 | PTRACE_TRACEME has been called in this process in any of its |
| 359 | current or prior lives. */ |
| 360 | if (__sigismember (&_hurdsig_traced, SIGKILL)) |
| 361 | flags |= EXEC_SIGTRAP; |
| 362 | #endif |
| 363 | err = __file_exec (file, task, flags, |
| 364 | args, argslen, env, envlen, |
| 365 | dtable, MACH_MSG_TYPE_COPY_SEND, dtablesize, |
| 366 | ports, MACH_MSG_TYPE_COPY_SEND, _hurd_nports, |
| 367 | ints, INIT_INT_MAX, |
| 368 | please_dealloc, pdp - please_dealloc, |
| 369 | &_hurd_msgport, task == __mach_task_self () ? 1 : 0); |
| 370 | } |
| 371 | |
| 372 | /* Release references to the standard ports. */ |
| 373 | for (i = 0; i < _hurd_nports; ++i) |
| 374 | if ((i == INIT_PORT_PROC && task != __mach_task_self ()) |
| 375 | || (reauth && (i == INIT_PORT_AUTH |
| 376 | || i == INIT_PORT_CRDIR || i == INIT_PORT_CWDIR))) |
| 377 | __mach_port_deallocate (__mach_task_self (), ports[i]); |
| 378 | else |
| 379 | free_port (i); |
| 380 | |
| 381 | /* Release references to the file descriptor ports. */ |
| 382 | if (ulink_dtable != NULL) |
| 383 | { |
| 384 | for (i = 0; i < dtablesize; ++i) |
| 385 | if (dtable[i] != MACH_PORT_NULL) |
| 386 | _hurd_port_free (dtable_cells[i], &ulink_dtable[i], dtable[i]); |
| 387 | } |
| 388 | else if (dtable && dtable != _hurd_init_dtable) |
| 389 | for (i = 0; i < dtablesize; ++i) |
| 390 | __mach_port_deallocate (__mach_task_self (), dtable[i]); |
| 391 | |
| 392 | /* Release lock on the file descriptor table. */ |
| 393 | __mutex_unlock (&_hurd_dtable_lock); |
| 394 | |
| 395 | /* Safe to let signals happen now. */ |
| 396 | _hurd_critical_section_unlock (ss); |
| 397 | |
| 398 | outargs: |
| 399 | free (args); |
| 400 | outenv: |
| 401 | free (env); |
| 402 | return err; |
| 403 | } |