The patch is as follows: On Fri, Sep 18, 2026 at 5:39 PM Alperen Erkan <[email protected]> wrote:
> Hello everyone, > > I am submitting a patch set for the Hurd `boot` server. Due to the scope > of these changes, I have already initiated the FSF Copyright Assignment > process with Craig Topham and am currently waiting for the final paperwork. > > Here is a brief summary of the key refactoring and fixes included: > - **Thread-safe `poll` implementation:** Replaced legacy blocking I/O > calls to improve concurrency in event handling. > - **Memory Leak Fixes:** Cleaned up allocated state and port references > across long-running bootstrap routines. > - **Signal Handling:** Improved asynchronous signal safety and context > recovery within the boot server. > > Because explaining every internal detail would be quite lengthy, the code > changes are kept modular and self-explanatory. > > I maintain and test these patches in my repository before submitting them. > You can review the complete diff and repository history here: > https://github.com/erkanalperen54-boop/HURD > > Looking forward to your feedback and code review! > > Kind regards, > Alperen ERKAN >
From 6a9ac9915e800ddc9fe597d17f84bae441d13b30 Mon Sep 17 00:00:00 2001 From: Alperen ERKAN <[email protected]> Date: Sat, 16 Sep 2026 12:50:33 +0300 Subject: [PATCH] Boot Fix --- hurd/boot/boot.c | 1175 ++++++++++++++++++++++++++++++--------------- hurd/boot/boot_script.c | 18 +- hurd/boot/userland-boot.c | 261 +++++++--- 3 files changed, 1008 insertions(+), 446 deletions(-) diff --git a/hurd/boot/boot.c b/hurd/boot/boot.c index 54feb33..f9f630c 100644 --- a/hurd/boot/boot.c +++ b/hurd/boot/boot.c @@ -2,6 +2,7 @@ as if we were the kernel. Copyright (C) 1993,94,95,96,97,98,99,2000,01,02,2006,14,16 Free Software Foundation, Inc. + Copyright (C) 2026 Alperen ERKAN This file is part of the GNU Hurd. @@ -59,8 +60,12 @@ #include <hurd/auth.h> #include <unistd.h> -#include <fcntl.h> #include <signal.h> +#include <poll.h> +#include <stdatomic.h> +#include <time.h> +#include <limits.h> +#include <stdint.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <termios.h> @@ -79,9 +84,50 @@ static int privileged; static int want_privileged; static struct termios orig_tty_state; +static int termstate_initialized; static int isig; static char *kernel_command_line; +static void +restore_termstate (void) +{ + if (! termstate_initialized) + return; + tcsetattr (0, 0, &orig_tty_state); + termstate_initialized = 0; +} + +static void +sig_handler (int sig) +{ + switch (sig) + { + case SIGCONT: + /* Re-enter raw mode after being stopped. */ + if (termstate_initialized) + { + struct termios tty_state = orig_tty_state; + cfmakeraw (&tty_state); + if (isig) + tty_state.c_lflag |= ISIG; + tcsetattr (0, 0, &tty_state); + } + break; + + case SIGTSTP: + restore_termstate (); + signal (SIGTSTP, SIG_DFL); + raise (SIGTSTP); + break; + + default: + restore_termstate (); + signal (sig, SIG_DFL); + raise (sig); + break; + } +} + static void init_termstate (void) { @@ -97,12 +143,14 @@ init_termstate (void) if (tcsetattr (0, 0, &tty_state) < 0) error (11, errno, "tcsetattr"); -} -static void -restore_termstate (void) -{ - tcsetattr (0, 0, &orig_tty_state); + termstate_initialized = 1; + + atexit (restore_termstate); + signal (SIGINT, sig_handler); + signal (SIGTERM, sig_handler); + signal (SIGTSTP, sig_handler); + signal (SIGCONT, sig_handler); } #define host_fstat fstat @@ -115,6 +163,16 @@ host_exit (int status) exit (status); } +/* Best-effort write of a diagnostic message to stderr. */ +static void +write_diag (const char *msg, size_t len) +{ + ssize_t err; + do + err = write (2, msg, len); + while (err < 0 && errno == EINTR); +} + int verbose; mach_port_t privileged_host_port, master_device_port; @@ -137,17 +195,7 @@ struct store *root_store; pthread_spinlock_t queuelock = PTHREAD_SPINLOCK_INITIALIZER; pthread_spinlock_t readlock = PTHREAD_SPINLOCK_INITIALIZER; -mach_port_t php_child_name, psmdp_child_name, taskname; - -task_t child_task; -mach_port_t bootport; - -int console_mscount; - -vm_address_t fs_stack_base; -vm_size_t fs_stack_size; - -char *fsname; +mach_port_mscount_t console_mscount; char bootstrap_args[100] = "-"; char *bootdevice = 0; @@ -267,6 +315,17 @@ boot_demuxer (mach_msg_header_t *inp, static void read_reply (void); static void * msg_thread (void *); +static void * select_thread (void *); + +/* Console input event handling. The main thread polls the host stdin + only while console read requests are queued; the message threads + wake it via WAKE_PIPE. */ +static int wake_pipe[2]; +static int select_pipe[2]; +static _Atomic int stdin_eof; + +/* Maximum size of a single console read request (out-of-line). */ +#define CONSOLE_READ_MAX (16 * 1024 * 1024) const char *argp_program_version = STANDARD_HURD_VERSION (boot); @@ -329,6 +388,13 @@ add_dev_map (const char *dev_name, const char *dev_file) map->device_name = strdup (dev_name); map->file_name = strdup (dev_file); + if (! map->device_name || ! map->file_name) + { + free (map->device_name); + free (map->file_name); + free (map); + return NULL; + } map->next = dev_map_head; dev_map_head = map; return map; @@ -378,7 +444,8 @@ parse_opt (int key, char *arg, struct argp_state *state) if (dev_file == NULL) return ARGP_ERR_UNKNOWN; *dev_file = 0; - add_dev_map (arg, dev_file+1); + if (! add_dev_map (arg, dev_file + 1)) + argp_error (state, "Not enough memory"); break; case OPT_PRIVILEGED: @@ -404,47 +471,70 @@ parse_opt (int key, char *arg, struct argp_state *state) static error_t allocate_pseudo_ports (void) { + error_t err; mach_port_t old; /* Allocate a port that we hand out as the privileged host port. */ - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_privileged_host_port); - mach_port_insert_right (mach_task_self (), - pseudo_privileged_host_port, - pseudo_privileged_host_port, - MACH_MSG_TYPE_MAKE_SEND); - mach_port_move_member (mach_task_self (), pseudo_privileged_host_port, - receive_set); - mach_port_request_notification (mach_task_self (), - pseudo_privileged_host_port, - MACH_NOTIFY_NO_SENDERS, 1, - pseudo_privileged_host_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_privileged_host_port); + if (err) + return err; + err = mach_port_insert_right (mach_task_self (), + pseudo_privileged_host_port, + pseudo_privileged_host_port, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + return err; + err = mach_port_move_member (mach_task_self (), pseudo_privileged_host_port, + receive_set); + if (err) + return err; + err = mach_port_request_notification (mach_task_self (), + pseudo_privileged_host_port, + MACH_NOTIFY_NO_SENDERS, 1, + pseudo_privileged_host_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); + if (err) + return err; assert_backtrace (old == MACH_PORT_NULL); /* Allocate a port that we hand out as the privileged processor set port. */ - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_pset); - mach_port_move_member (mach_task_self (), pseudo_pset, - receive_set); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_pset); + if (err) + return err; + err = mach_port_move_member (mach_task_self (), pseudo_pset, + receive_set); + if (err) + return err; /* Make one send right that we copy when handing it out. */ - mach_port_insert_right (mach_task_self (), - pseudo_pset, - pseudo_pset, - MACH_MSG_TYPE_MAKE_SEND); + err = mach_port_insert_right (mach_task_self (), + pseudo_pset, + pseudo_pset, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + return err; /* We will receive new task notifications on this port. */ - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &task_notification_port); - mach_port_move_member (mach_task_self (), task_notification_port, - receive_set); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &task_notification_port); + if (err) + return err; + err = mach_port_move_member (mach_task_self (), task_notification_port, + receive_set); + if (err) + return err; /* And information about dying tasks here. */ - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &dead_task_notification_port); - mach_port_move_member (mach_task_self (), dead_task_notification_port, - receive_set); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &dead_task_notification_port); + if (err) + return err; + err = mach_port_move_member (mach_task_self (), dead_task_notification_port, + receive_set); + if (err) + return err; return 0; } @@ -457,20 +547,17 @@ read_boot_script (char **buffer, size_t *length) static const char memmsg[] = "Not enough memory\n"; int i, fd; size_t amt, len; - ssize_t err; fd = open (bootscript, O_RDONLY, 0); if (fd < 0) { - err = write (2, filemsg, sizeof (filemsg)); - assert_backtrace (err == (sizeof (filemsg))); + write_diag (filemsg, sizeof filemsg - 1); host_exit (1); } p = buf = malloc (500); if (!buf) { - err = write (2, memmsg, sizeof (memmsg)); - assert_backtrace (err == (sizeof (memmsg))); + write_diag (memmsg, sizeof memmsg - 1); host_exit (1); } len = 500; @@ -478,20 +565,27 @@ read_boot_script (char **buffer, size_t *length) while (1) { i = read (fd, p, len - (p - buf)); - if (i <= 0) + if (i == 0) break; + if (i < 0) + { + if (errno == EINTR) + continue; + error (1, errno, "%s", bootscript); + } p += i; amt += i; if (p == buf + len) { char *newbuf; - size_t newlen = len + 500; + size_t newlen = len * 2; + if (newlen < len) + error (1, ENOMEM, "%s", bootscript); newbuf = realloc (buf, newlen); if (!newbuf) { - err = write (2, memmsg, sizeof (memmsg)); - assert_backtrace (err == (sizeof (memmsg))); + write_diag (memmsg, sizeof memmsg - 1); host_exit (1); } p = newbuf + len; @@ -522,12 +616,9 @@ const char *default_boot_script = " -T device ${root-device} $(task-create) $(task-resume)" "\n" - /* Now the exec server; to load the dynamically-linked exec server - program, we have the boot loader in fact load and run ld.so, - which in turn loads and runs /hurd/exec. This task is created, - and its task port saved in ${exec-task} to be passed to the fs - above, but it is left suspended; the fs will resume the exec task - once it is ready. */ + /* Now the exec server. It is created suspended; the bootstrap + filesystem resumes it once it is ready. Its task port is saved + in ${exec-task} to be passed to the fs above. */ "/hurd/exec.static $(exec-task=task-create)" "\n"; @@ -567,8 +658,14 @@ main (int argc, char **argv, char **envp) if (privileged) strcat (bootstrap_args, "f"); - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET, - &receive_set); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_PORT_SET, + &receive_set); + if (err) + error (12, err, "mach_port_allocate"); + + if (pipe2 (wake_pipe, O_NONBLOCK | O_CLOEXEC) < 0 + || pipe2 (select_pipe, O_NONBLOCK | O_CLOEXEC) < 0) + error (13, errno, "pipe2"); if (root_store->class == &store_device_class && root_store->name && (root_store->flags & STORE_ENFORCED) @@ -581,41 +678,66 @@ main (int argc, char **argv, char **envp) /* Pass a magic value that we can use to do I/O to ROOT_STORE. */ { bootdevice = "pseudo-root"; - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_root); - mach_port_move_member (mach_task_self (), pseudo_root, receive_set); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_root); + if (err) + error (14, err, "mach_port_allocate"); + err = mach_port_move_member (mach_task_self (), pseudo_root, receive_set); + if (err) + error (14, err, "mach_port_move_member"); } - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_master_device_port); - mach_port_insert_right (mach_task_self (), - pseudo_master_device_port, - pseudo_master_device_port, - MACH_MSG_TYPE_MAKE_SEND); - mach_port_move_member (mach_task_self (), pseudo_master_device_port, - receive_set); - mach_port_request_notification (mach_task_self (), pseudo_master_device_port, - MACH_NOTIFY_NO_SENDERS, 1, - pseudo_master_device_port, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_master_device_port); + if (err) + error (15, err, "mach_port_allocate"); + err = mach_port_insert_right (mach_task_self (), + pseudo_master_device_port, + pseudo_master_device_port, + MACH_MSG_TYPE_MAKE_SEND); + if (err) + error (15, err, "mach_port_insert_right"); + err = mach_port_move_member (mach_task_self (), pseudo_master_device_port, + receive_set); + if (err) + error (15, err, "mach_port_move_member"); + err = mach_port_request_notification (mach_task_self (), + pseudo_master_device_port, + MACH_NOTIFY_NO_SENDERS, 1, + pseudo_master_device_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + if (err) + error (15, err, "mach_port_request_notification"); if (foo != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), foo); - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_console); - mach_port_move_member (mach_task_self (), pseudo_console, receive_set); - mach_port_request_notification (mach_task_self (), pseudo_console, - MACH_NOTIFY_NO_SENDERS, 1, pseudo_console, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_console); + if (err) + error (16, err, "mach_port_allocate"); + err = mach_port_move_member (mach_task_self (), pseudo_console, receive_set); + if (err) + error (16, err, "mach_port_move_member"); + err = mach_port_request_notification (mach_task_self (), pseudo_console, + MACH_NOTIFY_NO_SENDERS, 1, pseudo_console, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + if (err) + error (16, err, "mach_port_request_notification"); if (foo != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), foo); - mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, - &pseudo_time); - mach_port_move_member (mach_task_self (), pseudo_time, receive_set); - mach_port_request_notification (mach_task_self (), pseudo_time, - MACH_NOTIFY_NO_SENDERS, 1, pseudo_time, - MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, + &pseudo_time); + if (err) + error (17, err, "mach_port_allocate"); + err = mach_port_move_member (mach_task_self (), pseudo_time, receive_set); + if (err) + error (17, err, "mach_port_move_member"); + err = mach_port_request_notification (mach_task_self (), pseudo_time, + MACH_NOTIFY_NO_SENDERS, 1, pseudo_time, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &foo); + if (err) + error (17, err, "mach_port_request_notification"); if (foo != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), foo); @@ -637,7 +759,9 @@ main (int argc, char **argv, char **envp) error (1, err, "task_create"); /* Give it a name so it's easy to spot it from the real kernel. */ - task_set_name (pseudo_kernel, "pseudo_kernel"); + err = task_set_name (pseudo_kernel, "pseudo_kernel"); + if (err) + error (1, err, "task_set_name"); } if (kernel_command_line == 0) @@ -664,49 +788,66 @@ main (int argc, char **argv, char **envp) VAL_STR, (intptr_t) bootstrap_args)) { static const char msg[] = "error setting variable"; - size_t len = strlen (msg); - ssize_t err2 = write (2, msg, len); - assert_backtrace (err2 == len); + write_diag (msg, sizeof msg - 1); host_exit (1); } /* Turn each `FOO=BAR' word in the command line into a boot script variable ${FOO} with value BAR. */ { - int len = strlen (kernel_command_line) + 1; - char *s = memcpy (alloca (len), kernel_command_line, len); + char *s = strdup (kernel_command_line); char *word; + if (! s) + error (1, ENOMEM, "strdup"); + while ((word = strsep (&s, " \t")) != 0) { char *eq = strchr (word, '='); if (eq == 0) continue; *eq++ = '\0'; + if (! strcmp (word, "host-port") + || ! strcmp (word, "device-port") + || ! strcmp (word, "kernel-task") + || ! strcmp (word, "kernel-command-line") + || ! strcmp (word, "root-device") + || ! strcmp (word, "boot-args")) + { + fprintf (stderr, "ignoring reserved boot variable %s\n", word); + continue; + } err = boot_script_set_variable (word, VAL_STR, (intptr_t) eq); if (err) { char *msg; - ssize_t err2 = asprintf (&msg, "cannot set boot-script variable %s: %s\n", - word, boot_script_error_string (err)); - assert_backtrace (err2 != -1); - len = strlen (msg); - err2 = write (2, msg, len); - assert_backtrace (err2 == len); - free (msg); + if (asprintf (&msg, "cannot set boot-script variable %s: %s\n", + word, boot_script_error_string (err)) >= 0) + { + write_diag (msg, strlen (msg)); + free (msg); + } host_exit (1); } } + free (s); } /* Parse the boot script. */ { char *p, *line; size_t amt; + int lineno = 1; + if (bootscript) read_boot_script (&buf, &amt); else - buf = strdup (default_boot_script), amt = strlen (default_boot_script); + { + buf = strdup (default_boot_script); + if (! buf) + error (1, ENOMEM, "strdup"); + amt = strlen (default_boot_script); + } line = p = buf; while (1) @@ -717,39 +858,33 @@ main (int argc, char **argv, char **envp) err = boot_script_parse_line (0, line); if (err) { - ssize_t err2; char *str; - int i; str = boot_script_error_string (err); - i = strlen (str); - err2 = write (2, str, i); - assert_backtrace (err2 == i); - err2 = write (2, " in `", 5); - assert_backtrace (err2 == 5); - i = strlen (line); - err2 = write (2, line, i); - assert_backtrace (err2 == i); - err2 = write (2, "'\n", 2); - assert_backtrace (err2 == 2); + fprintf (stderr, "line %d: ", lineno); + write_diag (str, strlen (str)); + write_diag (" in `", 5); + write_diag (line, strlen (line)); + write_diag ("'\n", 2); host_exit (1); } if (p == buf + amt) break; line = ++p; + lineno++; } } if (index (bootstrap_args, 'd')) { static const char msg[] = "Pausing. . ."; - size_t msg_len = sizeof (msg) - 1; char c; - ssize_t err2; - err2 = write (2, msg, msg_len); - assert_backtrace (err2 == msg_len); - err2 = read (0, &c, 1); - assert_backtrace (err2 == 1); + ssize_t r; + + write_diag (msg, sizeof msg - 1); + do + r = read (0, &c, 1); + while (r < 0 && errno == EINTR); } init_termstate (); @@ -760,14 +895,10 @@ main (int argc, char **argv, char **envp) err = boot_script_exec (); if (err) { - ssize_t err2; char *str = boot_script_error_string (err); - int i = strlen (str); - err2 = write (2, str, i); - assert_backtrace (err2 == i); - err2 = write (2, "\n", 1); - assert_backtrace (err2 == 1); + write_diag (str, strlen (str)); + write_diag ("\n", 1); host_exit (1); } free (buf); @@ -776,24 +907,63 @@ main (int argc, char **argv, char **envp) mach_port_deallocate (mach_task_self (), pseudo_master_device_port); err = pthread_create (&pthread_id, NULL, msg_thread, NULL); - if (!err) - pthread_detach (pthread_id); - else - { - errno = err; - perror ("pthread_create"); - } + if (err) + error (1, err, "pthread_create"); + pthread_detach (pthread_id); + + err = pthread_create (&pthread_id, NULL, select_thread, NULL); + if (err) + error (1, err, "pthread_create"); + pthread_detach (pthread_id); for (;;) { - fd_set rmask; - FD_ZERO (&rmask); - FD_SET (0, &rmask); - if (select (1, &rmask, 0, 0, 0) == 1) + int want_stdin; + struct pollfd pfd[2]; + int n; + + if (atomic_load_explicit (&stdin_eof, memory_order_relaxed)) + { + /* Satisfy remaining waiters with EOF replies. */ + pthread_spin_lock (&queuelock); + want_stdin = qrhead != NULL; + pthread_spin_unlock (&queuelock); + if (want_stdin) + { + read_reply (); + continue; + } + } + else + { + pthread_spin_lock (&queuelock); + want_stdin = qrhead != NULL; + pthread_spin_unlock (&queuelock); + } + + pfd[0].fd = wake_pipe[0]; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + pfd[1].fd = 0; + pfd[1].events = POLLIN; + pfd[1].revents = 0; + + n = poll (pfd, want_stdin ? 2 : 1, -1); + if (n < 0) + { + if (errno == EINTR) + continue; + error (5, errno, "poll"); + } + + if (pfd[0].revents & POLLIN) + { + char c[128]; + read (wake_pipe[0], c, sizeof c); + } + + if (want_stdin && (pfd[1].revents & (POLLIN | POLLHUP | POLLERR))) read_reply (); - else if (errno != EINTR) - /* We hosed */ - error (5, errno, "select"); } } @@ -817,136 +987,367 @@ struct qr enum read_type type; mach_port_t reply_port; mach_msg_type_name_t reply_type; - int amount; + vm_size_t amount; struct qr *next; }; struct qr *qrhead, *qrtail; -/* Queue a read for later reply. */ -kern_return_t -queue_read (enum read_type type, - mach_port_t reply_port, - mach_msg_type_name_t reply_type, - int amount) +struct selq +{ + mach_port_t reply_port; + mach_msg_type_name_t reply_type; + int is_timeout; /* Use io_select_timeout_reply. */ + int type; /* Requested SELECT_* mask. */ + struct timespec deadline; /* Valid iff IS_TIMEOUT. */ + struct selq *next; +}; +static struct selq *selq_head, *selq_tail; +static pthread_mutex_t selq_lock = PTHREAD_MUTEX_INITIALIZER; + +/* Send the reply for a queued console read QR. BUF/LEN are the data; + if ERR is nonzero, it is an errno-style error code and no data is + returned. */ +static void +send_read_reply (struct qr *qr, const void *buf, ssize_t len, int err) +{ + switch (qr->type) + { + case DEV_READ: + ds_device_read_reply (qr->reply_port, qr->reply_type, err, + (io_buf_ptr_t) (err ? 0 : buf), + err ? 0 : len); + break; + + case DEV_READI: + ds_device_read_reply_inband (qr->reply_port, qr->reply_type, err, + err ? (const void *) 0 : buf, + err ? 0 : len); + break; + + case IO_READ: + io_read_reply (qr->reply_port, qr->reply_type, err, + err ? (void *) 0 : buf, err ? 0 : len); + break; + } +} + +/* Queue a read for later reply. */ +static kern_return_t +queue_read (enum read_type type, mach_port_t reply_port, + mach_msg_type_name_t reply_type, vm_size_t amount) { struct qr *qr; - qr = malloc (sizeof (struct qr)); + /* Zero-length requests and EOF get an immediate answer. */ + if (amount == 0 || atomic_load_explicit (&stdin_eof, memory_order_relaxed)) + { + struct qr qr0 = { type, reply_port, reply_type, 0, NULL }; + send_read_reply (&qr0, NULL, 0, 0); + return D_SUCCESS; + } + + qr = malloc (sizeof *qr); if (!qr) return D_NO_MEMORY; - pthread_spin_lock (&queuelock); - qr->type = type; qr->reply_port = reply_port; qr->reply_type = reply_type; qr->amount = amount; qr->next = 0; + + pthread_spin_lock (&queuelock); if (qrtail) qrtail->next = qr; else - qrhead = qrtail = qr; - + qrhead = qr; + qrtail = qr; pthread_spin_unlock (&queuelock); + + /* Wake the main thread so it starts polling stdin. */ + if (write (wake_pipe[1], "", 1) < 0 && errno != EAGAIN && errno != EINTR) + /* ignore */; + return D_SUCCESS; } -/* TRUE if there's data available on stdin, which should be used to satisfy - console read requests. */ -static int should_read = 0; - -/* Reply to a queued read. */ +/* Reply to the oldest queued console read, if any, using input from + host stdin. Called by the main thread when stdin is readable (or + at EOF, where read returns 0). */ static void read_reply (void) { - int avail; struct qr *qr; - char * buf; - int amtread; - - /* By forcing SHOULD_READ to true before trying the lock, we ensure that - either we get the lock ourselves or that whoever currently holds the - lock will service this read when he unlocks it. */ - should_read = 1; - if (pthread_spin_trylock (&readlock)) - return; + ssize_t amtread = 0; + void *buf = NULL; + vm_size_t bufsize = 0; + char inband_buf[IO_INBAND_MAX]; - /* Since we're committed to servicing the read, no one else need do so. */ - should_read = 0; + pthread_spin_lock (&readlock); - ioctl (0, FIONREAD, &avail); - if (!avail) + pthread_spin_lock (&queuelock); + qr = qrhead; + if (qr) + { + qrhead = qr->next; + if (qrhead == NULL) + qrtail = NULL; + } + pthread_spin_unlock (&queuelock); + + if (! qr) { pthread_spin_unlock (&readlock); return; } - pthread_spin_lock (&queuelock); - - if (!qrhead) + if (qr->type == DEV_READI) { - pthread_spin_unlock (&queuelock); + /* Amounts for in-band reads were validated at enqueue time. */ + amtread = read (0, inband_buf, qr->amount); + if (amtread == 0) + atomic_store_explicit (&stdin_eof, 1, memory_order_relaxed); pthread_spin_unlock (&readlock); - return; + if (amtread < 0) + send_read_reply (qr, NULL, 0, errno ? errno : EIO); + else + send_read_reply (qr, inband_buf, amtread, 0); + } + else + { + bufsize = qr->amount; + buf = mmap (0, bufsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (buf == MAP_FAILED) + { + int e = errno ? errno : EIO; + pthread_spin_unlock (&readlock); + send_read_reply (qr, NULL, 0, e); + free (qr); + return; + } + amtread = read (0, buf, bufsize); + if (amtread == 0) + atomic_store_explicit (&stdin_eof, 1, memory_order_relaxed); + if (amtread > 0 && (vm_size_t) amtread < bufsize) + { + /* Shrink the mapping so the tail pages cannot leak. */ + void *nbuf = mmap (0, amtread, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + if (nbuf != MAP_FAILED) + { + memcpy (nbuf, buf, amtread); + munmap (buf, bufsize); + buf = nbuf; + bufsize = amtread; + } + } + if (amtread < 0) + { + int e = errno; + munmap (buf, bufsize); + pthread_spin_unlock (&readlock); + send_read_reply (qr, NULL, 0, e); + free (qr); + return; + } + pthread_spin_unlock (&readlock); + send_read_reply (qr, buf, amtread, 0); + munmap (buf, bufsize); } - qr = qrhead; - qrhead = qr->next; - if (qr == qrtail) - qrtail = 0; + free (qr); +} - pthread_spin_unlock (&queuelock); +/* Queue an io_select request; satisfied by SELECT_THREAD. */ +static kern_return_t +queue_select (mach_port_t reply_port, mach_msg_type_name_t reply_type, + int type, int is_timeout, const struct timespec *ts) +{ + struct selq *sq; - if (qr->type == DEV_READ) + if (type == 0) + return 0; + + sq = malloc (sizeof *sq); + if (! sq) + return ENOMEM; + + sq->reply_port = reply_port; + sq->reply_type = reply_type; + sq->is_timeout = is_timeout; + sq->type = type; + sq->next = NULL; + if (is_timeout) { - buf = mmap (0, qr->amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); - assert_backtrace (buf != MAP_FAILED); + if (clock_gettime (CLOCK_MONOTONIC, &sq->deadline) < 0) + { + free (sq); + return errno; + } + sq->deadline.tv_sec += ts->tv_sec; + sq->deadline.tv_nsec += ts->tv_nsec; + if (sq->deadline.tv_nsec >= 1000000000L) + { + sq->deadline.tv_nsec -= 1000000000L; + sq->deadline.tv_sec += 1; + } } + + pthread_mutex_lock (&selq_lock); + if (selq_tail) + selq_tail->next = sq; else - buf = alloca (qr->amount); - amtread = read (0, buf, qr->amount); + selq_head = sq; + selq_tail = sq; + pthread_mutex_unlock (&selq_lock); - pthread_spin_unlock (&readlock); + if (write (select_pipe[1], "", 1) < 0 && errno != EAGAIN && errno != EINTR) + /* ignore */; - switch (qr->type) + return MIG_NO_REPLY; +} + +static void * +select_thread (void *arg) +{ + pthread_setname_np (pthread_self (), "select"); + + for (;;) { - case DEV_READ: - if (amtread >= 0) - ds_device_read_reply (qr->reply_port, qr->reply_type, 0, - (io_buf_ptr_t) buf, amtread); - else - ds_device_read_reply (qr->reply_port, qr->reply_type, errno, 0, 0); - break; + int want_r = 0, want_w = 0, want_x = 0; + int n, i, npfd = 0; + int stdin_ready, stdout_ready, urg_ready; + struct selq *sq, **psq, *done = NULL, **pdone = &done; + struct pollfd pfd[3]; + int timeout_ms = -1; + struct timespec now; + + pthread_mutex_lock (&selq_lock); + for (sq = selq_head; sq; sq = sq->next) + { + if (sq->type & (SELECT_READ | SELECT_URG)) + want_r = 1; + if (sq->type & SELECT_WRITE) + want_w = 1; + if (sq->type & SELECT_URG) + want_x = 1; + } + if (selq_head) + clock_gettime (CLOCK_MONOTONIC, &now); + for (sq = selq_head; sq; sq = sq->next) + { + if (sq->is_timeout) + { + long long ms = (sq->deadline.tv_sec - now.tv_sec) * 1000LL + + (sq->deadline.tv_nsec - now.tv_nsec) / 1000000LL; + int m = ms <= 0 ? 0 : (ms > 0x7fffffffLL ? 0x7fffffff : (int) ms); + if (timeout_ms < 0 || m < timeout_ms) + timeout_ms = m; + } + } + pthread_mutex_unlock (&selq_lock); - case DEV_READI: - if (amtread >= 0) - ds_device_read_reply_inband (qr->reply_port, qr->reply_type, 0, - buf, amtread); - else - ds_device_read_reply_inband (qr->reply_port, qr->reply_type, errno, - 0, 0); - break; + pfd[npfd].fd = select_pipe[0]; + pfd[npfd].events = POLLIN; + npfd++; + if (want_r) + { + pfd[npfd].fd = 0; + pfd[npfd].events = POLLIN | (want_x ? POLLPRI : 0); + npfd++; + } + if (want_w) + { + pfd[npfd].fd = 1; + pfd[npfd].events = POLLOUT; + npfd++; + } - case IO_READ: - if (amtread >= 0) - io_read_reply (qr->reply_port, qr->reply_type, 0, - buf, amtread); - else - io_read_reply (qr->reply_port, qr->reply_type, errno, 0, 0); - break; - } + n = poll (pfd, npfd, timeout_ms); + if (n < 0) + { + if (errno == EINTR) + continue; + continue; + } - free (qr); -} + if (pfd[0].revents & POLLIN) + { + char c[128]; + read (select_pipe[0], c, sizeof c); + } -/* Unlock READLOCK, and also service any new read requests that it was - blocking. */ -static void -unlock_readlock (void) -{ - pthread_spin_unlock (&readlock); - while (should_read) - read_reply (); + stdin_ready = 0, stdout_ready = 0, urg_ready = 0; + for (i = 1; i < npfd; i++) + { + if ((pfd[i].revents & (POLLIN | POLLHUP | POLLERR)) + && (pfd[i].events & POLLIN)) + stdin_ready = 1; + if ((pfd[i].revents & POLLOUT) && (pfd[i].events & POLLOUT)) + stdout_ready = 1; + if ((pfd[i].revents & POLLPRI) && (pfd[i].events & POLLPRI)) + urg_ready = 1; + } + if (atomic_load_explicit (&stdin_eof, memory_order_relaxed)) + stdin_ready = 1; + + clock_gettime (CLOCK_MONOTONIC, &now); + + pthread_mutex_lock (&selq_lock); + for (psq = &selq_head; (sq = *psq); ) + { + int result = 0, expired = 0; + + if (sq->is_timeout + && (sq->deadline.tv_sec < now.tv_sec + || (sq->deadline.tv_sec == now.tv_sec + && sq->deadline.tv_nsec <= now.tv_nsec))) + expired = 1; + + if (! expired) + { + if (stdin_ready && (sq->type & SELECT_READ)) + result |= SELECT_READ; + if (stdout_ready && (sq->type & SELECT_WRITE)) + result |= SELECT_WRITE; + if (urg_ready && (sq->type & SELECT_URG)) + result |= SELECT_URG; + } + + if (result || expired) + { + *psq = sq->next; + if (selq_tail == sq) + selq_tail = NULL; /* recompute below if needed */ + sq->type = result; + *pdone = sq; + pdone = &sq->next; + sq->next = NULL; + } + else + psq = &sq->next; + } + /* Fix up tail after removals. */ + if (! selq_head) + selq_tail = NULL; + else + { + for (sq = selq_head; sq->next; sq = sq->next) + ; + selq_tail = sq; + } + pthread_mutex_unlock (&selq_lock); + + while ((sq = done)) + { + done = sq->next; + if (sq->is_timeout) + io_select_timeout_reply (sq->reply_port, sq->reply_type, 0, + sq->type); + else + io_select_reply (sq->reply_port, sq->reply_type, 0, sq->type); + free (sq); + } + } } @@ -971,11 +1372,6 @@ ds_device_open (mach_port_t master_port, if (!strcmp (name, "console")) { -#if 0 - mach_port_insert_right (mach_task_self (), pseudo_console, - pseudo_console, MACH_MSG_TYPE_MAKE_SEND); - console_send_rights++; -#endif console_mscount++; *device = pseudo_console; *devicetype = MACH_MSG_TYPE_MAKE_SEND; @@ -1034,7 +1430,7 @@ ds_device_open_new (mach_port_t master_port, kern_return_t ds_device_close (device_t device) { - if (device != pseudo_console && device != pseudo_root) + if (device != pseudo_console && device != pseudo_root && device != pseudo_time) return D_NO_SUCH_DEVICE; return 0; } @@ -1051,24 +1447,25 @@ ds_device_write (device_t device, { if (device == pseudo_console) { -#if 0 - if (console_send_rights) + *bytes_written = write (1, data, datalen); + if (*bytes_written == -1) { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; + if (verbose) + fprintf (stderr, "console write: %s\r\n", strerror (errno)); + return D_IO_ERROR; } -#endif - *bytes_written = write (1, data, datalen); - - return (*bytes_written == -1 ? D_IO_ERROR : D_SUCCESS); + return D_SUCCESS; } else if (device == pseudo_root) { size_t wrote; if (store_write (root_store, recnum, data, datalen, &wrote) != 0) - return D_IO_ERROR; + { + if (verbose) + fprintf (stderr, "store_write: %s\r\n", strerror (errno)); + return D_IO_ERROR; + } *bytes_written = wrote; return D_SUCCESS; } @@ -1088,24 +1485,25 @@ ds_device_write_inband (device_t device, { if (device == pseudo_console) { -#if 0 - if (console_send_rights) + *bytes_written = write (1, data, datalen); + if (*bytes_written == -1) { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; + if (verbose) + fprintf (stderr, "console write: %s\r\n", strerror (errno)); + return D_IO_ERROR; } -#endif - - *bytes_written = write (1, data, datalen); - return (*bytes_written == -1 ? D_IO_ERROR : D_SUCCESS); + return D_SUCCESS; } else if (device == pseudo_root) { size_t wrote; if (store_write (root_store, recnum, data, datalen, &wrote) != 0) - return D_IO_ERROR; + { + if (verbose) + fprintf (stderr, "store_write: %s\r\n", strerror (errno)); + return D_IO_ERROR; + } *bytes_written = wrote; return D_SUCCESS; } @@ -1124,42 +1522,52 @@ ds_device_read (device_t device, mach_msg_type_number_t *datalen) { error_t err; + + /* Zero-length requests get an immediate empty answer. */ + if (bytes_wanted == 0) + { + *data = 0; + *datalen = 0; + return D_SUCCESS; + } + if (device == pseudo_console) { int avail; -#if 0 - if (console_send_rights) - { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; - } -#endif + if (bytes_wanted < 0) + return D_INVALID_SIZE; + if (bytes_wanted > CONSOLE_READ_MAX) + bytes_wanted = CONSOLE_READ_MAX; pthread_spin_lock (&readlock); - ioctl (0, FIONREAD, &avail); + if (ioctl (0, FIONREAD, &avail) < 0) + { + pthread_spin_unlock (&readlock); + return errno; + } if (avail) { void *new_data = mmap (0, bytes_wanted, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); if (new_data == MAP_FAILED) { - unlock_readlock (); + pthread_spin_unlock (&readlock); return errno; } *data = new_data; *datalen = read (0, *data, bytes_wanted); - unlock_readlock (); + if (*datalen == 0) + atomic_store_explicit (&stdin_eof, 1, memory_order_relaxed); + pthread_spin_unlock (&readlock); return (*datalen == -1 ? D_IO_ERROR : D_SUCCESS); } else { - unlock_readlock (); - err = queue_read (DEV_READ, reply_port, reply_type, bytes_wanted); - if (err) - return err; - return MIG_NO_REPLY; + pthread_spin_unlock (&readlock); + err = queue_read (DEV_READ, reply_port, reply_type, + (vm_size_t) bytes_wanted); + return err == D_SUCCESS ? MIG_NO_REPLY : err; } } else if (device == pseudo_root) @@ -1167,7 +1575,11 @@ ds_device_read (device_t device, size_t data_size = 0; err = store_read (root_store, recnum, bytes_wanted, (void **)data, &data_size); if (err) - return D_IO_ERROR; + { + if (verbose) + fprintf (stderr, "store_read: %s\r\n", strerror (err)); + return D_IO_ERROR; + } *datalen = data_size; return D_SUCCESS; } @@ -1185,36 +1597,41 @@ ds_device_read_inband (device_t device, io_buf_ptr_inband_t data, mach_msg_type_number_t *datalen) { + /* The buffer is a fixed MIG in-band array; bound the request. */ + if (bytes_wanted < 0 || bytes_wanted > IO_INBAND_MAX) + return D_INVALID_SIZE; + if (bytes_wanted == 0) + { + *datalen = 0; + return D_SUCCESS; + } + if (device == pseudo_console) { int avail; -#if 0 - if (console_send_rights) + pthread_spin_lock (&readlock); + if (ioctl (0, FIONREAD, &avail) < 0) { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; + pthread_spin_unlock (&readlock); + return errno; } -#endif - - pthread_spin_lock (&readlock); - ioctl (0, FIONREAD, &avail); if (avail) { *datalen = read (0, data, bytes_wanted); - unlock_readlock (); + if (*datalen == 0) + atomic_store_explicit (&stdin_eof, 1, memory_order_relaxed); + pthread_spin_unlock (&readlock); return (*datalen == -1 ? D_IO_ERROR : D_SUCCESS); } else { kern_return_t err; - unlock_readlock (); - err = queue_read (DEV_READI, reply_port, reply_type, bytes_wanted); - if (err) - return err; - return MIG_NO_REPLY; + pthread_spin_unlock (&readlock); + err = queue_read (DEV_READI, reply_port, reply_type, + (vm_size_t) bytes_wanted); + return err == D_SUCCESS ? MIG_NO_REPLY : err; } } else if (device == pseudo_root) @@ -1263,7 +1680,13 @@ ds_device_map (device_t device, return D_IO_ERROR; err = io_map (node, pager, &wr_memobj); - if (!err && MACH_PORT_VALID (wr_memobj)) + if (err) + { + mach_port_deallocate (mach_task_self (), node); + *pager = MACH_PORT_NULL; + return D_IO_ERROR; + } + if (MACH_PORT_VALID (wr_memobj)) mach_port_deallocate (mach_task_self (), wr_memobj); mach_port_deallocate (mach_task_self (), node); @@ -1298,6 +1721,9 @@ ds_device_get_status (device_t device, case DEV_GET_SIZE: if (*statuslen < DEV_GET_SIZE_COUNT) return D_INVALID_SIZE; + if (root_store->size > UINT32_MAX + || root_store->block_size > UINT32_MAX) + return D_INVALID_SIZE; status[DEV_GET_SIZE_DEVICE_SIZE] = root_store->size; status[DEV_GET_SIZE_RECORD_SIZE] = root_store->block_size; *statuslen = DEV_GET_SIZE_COUNT; @@ -1306,6 +1732,9 @@ ds_device_get_status (device_t device, case DEV_GET_RECORDS: if (*statuslen < DEV_GET_RECORDS_COUNT) return D_INVALID_SIZE; + if (root_store->blocks > UINT32_MAX + || root_store->block_size > UINT32_MAX) + return D_INVALID_SIZE; status[DEV_GET_RECORDS_DEVICE_RECORDS] = root_store->blocks; status[DEV_GET_RECORDS_RECORD_SIZE] = root_store->block_size; *statuslen = DEV_GET_RECORDS_COUNT; @@ -1373,7 +1802,6 @@ kern_return_t do_mach_notify_no_senders (mach_port_t notify, mach_port_mscount_t mscount) { - ssize_t err; static int no_console; mach_port_t foo; if (notify == pseudo_master_device_port) @@ -1390,8 +1818,7 @@ do_mach_notify_no_senders (mach_port_t notify, { bye: restore_termstate (); - err = write (2, "bye\n", 4); - assert_backtrace (err == 4); + write_diag ("bye\n", 4); host_exit (0); } else @@ -1408,6 +1835,7 @@ do_mach_notify_no_senders (mach_port_t notify, if (foo != MACH_PORT_NULL) mach_port_deallocate (mach_task_self (), foo); } + return 0; } return EOPNOTSUPP; @@ -1425,10 +1853,6 @@ kern_return_t do_mach_notify_dead_name (mach_port_t notify, mach_port_t name) { -#if 0 - if (name == child_task && notify == bootport) - host_exit (0); -#endif if (notify != dead_task_notification_port) return EOPNOTSUPP; task_died (name); @@ -1452,15 +1876,6 @@ S_io_write (mach_port_t object, if (object != pseudo_console) return EOPNOTSUPP; -#if 0 - if (console_send_rights) - { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; - } -#endif - *amtwritten = write (1, data, datalen); return *amtwritten == -1 ? errno : 0; } @@ -1479,17 +1894,20 @@ S_io_read (mach_port_t object, if (object != pseudo_console) return EOPNOTSUPP; -#if 0 - if (console_send_rights) + if (amount > CONSOLE_READ_MAX) + amount = CONSOLE_READ_MAX; + if (amount == 0) { - mach_port_mod_refs (mach_task_self (), pseudo_console, - MACH_PORT_TYPE_SEND, -console_send_rights); - console_send_rights = 0; + *datalen = 0; + return 0; } -#endif pthread_spin_lock (&readlock); - ioctl (0, FIONREAD, &avail); + if (ioctl (0, FIONREAD, &avail) < 0) + { + pthread_spin_unlock (&readlock); + return errno; + } if (avail) { data_t orig_data = *data; @@ -1499,26 +1917,26 @@ S_io_read (mach_port_t object, MAP_ANON, 0, 0); if (new_data == MAP_FAILED) { - unlock_readlock(); + pthread_spin_unlock (&readlock); return errno; } *data = new_data; } *datalen = read (0, *data, amount); + if (*datalen == 0) + atomic_store_explicit (&stdin_eof, 1, memory_order_relaxed); if (*datalen == -1 && *data != orig_data) munmap (*data, amount); - unlock_readlock (); + pthread_spin_unlock (&readlock); return *datalen == -1 ? errno : 0; } else { kern_return_t err; - unlock_readlock (); + pthread_spin_unlock (&readlock); err = queue_read (IO_READ, reply_port, reply_type, amount); - if (err) - return err; - return MIG_NO_REPLY; + return err == D_SUCCESS ? MIG_NO_REPLY : err; } } @@ -1539,9 +1957,13 @@ S_io_readable (mach_port_t object, mach_msg_type_name_t reply_type, vm_size_t *amt) { + int avail; + if (object != pseudo_console) return EOPNOTSUPP; - ioctl (0, FIONREAD, amt); + if (ioctl (0, FIONREAD, &avail) < 0) + return errno; + *amt = avail; return 0; } @@ -1621,60 +2043,16 @@ S_io_get_icky_async_id (mach_port_t object, return EOPNOTSUPP; } -static kern_return_t -io_select_common (mach_port_t object, - mach_port_t reply_port, - mach_msg_type_name_t reply_type, - struct timespec *tsp, int *type) -{ - struct timeval tv, *tvp; - fd_set r, w, x; - int n; - - if (object != pseudo_console) - return EOPNOTSUPP; - - FD_ZERO (&r); - FD_ZERO (&w); - FD_ZERO (&x); - FD_SET (0, &r); - FD_SET (0, &w); - FD_SET (0, &x); - - if (tsp == NULL) - tvp = NULL; - else - { - tv.tv_sec = tsp->tv_sec; - tv.tv_usec = tsp->tv_nsec / 1000; - tvp = &tv; - } - - n = select (1, - (*type & SELECT_READ) ? &r : 0, - (*type & SELECT_WRITE) ? &w : 0, - (*type & SELECT_URG) ? &x : 0, - tvp); - if (n < 0) - return errno; - - if (! FD_ISSET (0, &r)) - *type &= ~SELECT_READ; - if (! FD_ISSET (0, &w)) - *type &= ~SELECT_WRITE; - if (! FD_ISSET (0, &x)) - *type &= ~SELECT_URG; - - return 0; -} - kern_return_t S_io_select (mach_port_t object, mach_port_t reply_port, mach_msg_type_name_t reply_type, int *type) { - return io_select_common (object, reply_port, reply_type, NULL, type); + if (object != pseudo_console) + return EOPNOTSUPP; + + return queue_select (reply_port, reply_type, *type, 0, NULL); } kern_return_t @@ -1684,7 +2062,13 @@ S_io_select_timeout (mach_port_t object, struct timespec ts, int *type) { - return io_select_common (object, reply_port, reply_type, &ts, type); + if (object != pseudo_console) + return EOPNOTSUPP; + + if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000L) + return EINVAL; + + return queue_select (reply_port, reply_type, *type, 1, &ts); } kern_return_t @@ -1697,6 +2081,7 @@ S_io_stat (mach_port_t object, return EOPNOTSUPP; memset (st, 0, sizeof(struct stat)); + st->st_mode = S_IFCHR | 0666; st->st_blksize = 1024; return 0; } @@ -1712,11 +2097,18 @@ S_io_reauthenticate (mach_port_t object, mach_msg_type_number_t gulen = 0, aulen = 0, gglen = 0, aglen = 0; error_t err; - /* XXX: This cannot possibly work, authserver is 0. */ + if (object != pseudo_console) + return EOPNOTSUPP; + + /* Without an auth server there is nobody to reauthenticate + against. */ + if (authserver == MACH_PORT_NULL) + return EOPNOTSUPP; err = mach_port_insert_right (mach_task_self (), object, object, MACH_MSG_TYPE_MAKE_SEND); - assert_perror_backtrace (err); + if (err) + return err; do err = auth_server_authenticate (authserver, @@ -1728,17 +2120,17 @@ S_io_reauthenticate (mach_port_t object, &ag, &aglen); while (err == EINTR); - if (!err) + if (! err) { mig_deallocate ((vm_address_t) gu, gulen * sizeof *gu); - mig_deallocate ((vm_address_t) au, aulen * sizeof *gu); - mig_deallocate ((vm_address_t) gg, gglen * sizeof *gu); - mig_deallocate ((vm_address_t) au, aulen * sizeof *gu); + mig_deallocate ((vm_address_t) au, aulen * sizeof *au); + mig_deallocate ((vm_address_t) gg, gglen * sizeof *gg); + mig_deallocate ((vm_address_t) ag, aglen * sizeof *ag); } mach_port_deallocate (mach_task_self (), rend); mach_port_deallocate (mach_task_self (), object); - return 0; + return err; } kern_return_t @@ -2027,6 +2419,9 @@ kern_return_t S_host_reboot (mach_port_t host_priv, int flags) { + if (host_priv != pseudo_privileged_host_port) + return KERN_INVALID_HOST; + fprintf (stderr, "Would %s the system. Bye.\r\n", flags & RB_HALT? "halt": "reboot"); host_exit (0); @@ -2080,7 +2475,7 @@ static void task_died (mach_port_t name) { if (verbose > 1) - fprintf (stderr, "Task '%u' died.\r\n", name); + fprintf (stderr, "Task '%lu' died.\r\n", (unsigned long) name); hurd_ihash_remove (&task_ihash, (hurd_ihash_key_t) name); } @@ -2098,7 +2493,8 @@ S_mach_notify_new_task (mach_port_t notify, return EOPNOTSUPP; if (verbose > 1) - fprintf (stderr, "Task '%u' created by task '%u'.\r\n", task, parent); + fprintf (stderr, "Task '%lu' created by task '%lu'.\r\n", + (unsigned long) task, (unsigned long) parent); err = mach_port_request_notification (mach_task_self (), task, MACH_NOTIFY_DEAD_NAME, 0, @@ -2109,15 +2505,15 @@ S_mach_notify_new_task (mach_port_t notify, goto fail; assert_backtrace (! MACH_PORT_VALID (previous)); - mach_port_mod_refs (mach_task_self (), task, MACH_PORT_RIGHT_SEND, +1); + err = mach_port_mod_refs (mach_task_self (), task, MACH_PORT_RIGHT_SEND, + +1); + if (err) + goto fail; err = hurd_ihash_add (&task_ihash, (hurd_ihash_key_t) task, (hurd_ihash_value_t)(uintptr_t) task); if (err) - { - mach_port_deallocate (mach_task_self (), task); - goto fail; - } + goto fail; if (MACH_PORT_VALID (new_task_notification)) /* Relay the notification. This consumes task and parent. */ @@ -2129,6 +2525,8 @@ S_mach_notify_new_task (mach_port_t notify, fail: task_terminate (task); + mach_port_deallocate (mach_task_self (), task); + mach_port_deallocate (mach_task_self (), parent); return err; } @@ -2138,16 +2536,29 @@ S_processor_set_tasks(mach_port_t processor_set, mach_msg_type_number_t *task_listCnt) { error_t err; - size_t i; + size_t i, count; + int kernel_in_hash = 0; + hurd_ihash_value_t value; - if (!task_ihash.nr_items) + if (processor_set != pseudo_pset) + return KERN_INVALID_ARGUMENT; + + if (! MACH_PORT_VALID (pseudo_kernel)) { *task_listCnt = 0; return 0; } + HURD_IHASH_ITERATE (&task_ihash, value) + if ((task_t) (uintptr_t) value == pseudo_kernel) + kernel_in_hash = 1; + + count = task_ihash.nr_items + (kernel_in_hash ? 0 : 1); + if (count > SIZE_MAX / sizeof **task_list) + return KERN_RESOURCE_SHORTAGE; + err = vm_allocate (mach_task_self (), (vm_address_t *) task_list, - task_ihash.nr_items * sizeof **task_list, 1); + count * sizeof **task_list, 1); if (err) return err; @@ -2157,14 +2568,14 @@ S_processor_set_tasks(mach_port_t processor_set, i = 1; HURD_IHASH_ITERATE (&task_ihash, value) { - task_t task = (task_t)(uintptr_t) value; + task_t task = (task_t) (uintptr_t) value; if (task == pseudo_kernel) - continue; + continue; (*task_list)[i] = task; i += 1; } - *task_listCnt = task_ihash.nr_items; + *task_listCnt = i; return 0; } diff --git a/hurd/boot/boot_script.c b/hurd/boot/boot_script.c index 340ccc8..6c526df 100644 --- a/hurd/boot/boot_script.c +++ b/hurd/boot/boot_script.c @@ -1,6 +1,7 @@ /* Boot script parser for Mach. */ /* Written by Shantanu Goel ([email protected]). */ +/* Copyright (C) 2026 Alperen ERKAN */ #include <mach/mach_types.h> #if !KERNEL || OSKIT_MACH @@ -339,7 +340,10 @@ boot_script_parse_line (void *hook, char *cmdline) /* Only values are allowed in ${...} constructs. */ if (end_char == '}' && s->type == VAL_FUNC) - return BOOT_SCRIPT_INVALID_SYM; + { + error = BOOT_SCRIPT_INVALID_SYM; + goto bad; + } /* Check that assignment is valid. */ if (c == '=' && s->type == VAL_FUNC) @@ -555,9 +559,17 @@ boot_script_exec (void) { struct sym *sym = (struct sym *) arg->val; - /* Resolve symbol value. */ - while (sym->type == VAL_SYM) + /* Resolve symbol value. Guard against reference + cycles. */ + unsigned int depth = 0; + while (sym->type == VAL_SYM + && depth++ <= (unsigned int) symtab_index) sym = (struct sym *) sym->val; + if (sym->type == VAL_SYM) + { + error = BOOT_SCRIPT_SYNTAX_ERROR; + goto done; + } if (sym->type == VAL_NONE) { error = BOOT_SCRIPT_UNDEF_SYM; diff --git a/hurd/boot/userland-boot.c b/hurd/boot/userland-boot.c index 1739eb9..1ca6430 100644 --- a/hurd/boot/userland-boot.c +++ b/hurd/boot/userland-boot.c @@ -1,5 +1,6 @@ /* boot_script.c support functions for running in a Mach user task. Copyright (C) 2001 Free Software Foundation, Inc. + Copyright (C) 2026 Alperen ERKAN This file is part of the GNU Hurd. @@ -24,9 +25,11 @@ #include <mach/machine/vm_param.h> /* For VM_XXX_ADDRESS */ #include <mach/gnumach.h> /* For task_set_name */ #include <stdlib.h> +#include <stdint.h> #include <stdio.h> #include <string.h> #include <sys/mman.h> +#include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <error.h> @@ -36,6 +39,44 @@ #include "boot_script.h" #include "private.h" +/* Read exactly LEN bytes from FD, returning 0 on success. */ +static int +read_full (int fd, void *buf, size_t len) +{ + char *p = buf; + while (len > 0) + { + ssize_t n = read (fd, p, len); + if (n == 0) + return -1; + if (n < 0) + { + if (errno == EINTR) + continue; + return -1; + } + p += n; + len -= n; + } + return 0; +} + +static void __attribute__ ((__noreturn__)) +load_fail (task_t t, const char *file) +{ + char msg[] = ": truncated or unreadable bootstrap file\n"; + size_t len = strlen (file); + ssize_t err; + do + err = write (2, file, len); + while (err < 0 && errno == EINTR); + do + err = write (2, msg, sizeof msg - 1); + while (err < 0 && errno == EINTR); + task_terminate (t); + exit (1); +} + void * boot_script_malloc (unsigned int size) { @@ -122,9 +163,14 @@ boot_script_insert_right (struct cmd *cmd, mach_port_t port, mach_port_t *name) *name = MACH_PORT_NULL; do { + if (*name >= (1 << 20)) + { + error (0, ENOMEM, "%s: mach_port_insert_right", cmd->path); + return BOOT_SCRIPT_MACH_ERROR; + } *name += 1; err = mach_port_insert_right (cmd->task, - *name, port, MACH_MSG_TYPE_COPY_SEND); + *name, port, MACH_MSG_TYPE_COPY_SEND); } while (err == KERN_NAME_EXISTS); @@ -193,44 +239,105 @@ load_image (task_t t, exit (1); } - err = read (fd, &hdr, sizeof hdr); - assert_backtrace (err == (sizeof hdr)); + if (read_full (fd, &hdr, sizeof hdr) < 0) + { + close (fd); + load_fail (t, file); + } /* File must have magic ELF number. */ if (hdr.e.e_ident[0] == 0177 && hdr.e.e_ident[1] == 'E' && hdr.e.e_ident[2] == 'L' && hdr.e.e_ident[3] == 'F') { - ElfW(Phdr) phdrs[hdr.e.e_phnum], *ph; - lseek (fd, hdr.e.e_phoff, SEEK_SET); - err = read (fd, phdrs, sizeof phdrs); - assert_backtrace (err == (sizeof phdrs)); - for (ph = phdrs; ph < &phdrs[sizeof phdrs/sizeof phdrs[0]]; ++ph) - if (ph->p_type == PT_LOAD) + /* Refuse pathological or extended program-header counts; + we do not support PN_XNUM. */ + if (hdr.e.e_phnum == 0 || hdr.e.e_phnum >= PN_XNUM) + { + close (fd); + load_fail (t, file); + } + + { + struct stat st; + ElfW(Phdr) *phdrs, *ph; + + if (fstat (fd, &st) < 0 + || hdr.e.e_phoff > (uintmax_t) st.st_size + || (uintmax_t) hdr.e.e_phnum * sizeof (ElfW(Phdr)) + > (uintmax_t) st.st_size - hdr.e.e_phoff) + { + close (fd); + load_fail (t, file); + } + + phdrs = malloc (hdr.e.e_phnum * sizeof (ElfW(Phdr))); + if (! phdrs) + { + close (fd); + load_fail (t, file); + } + if (lseek (fd, hdr.e.e_phoff, SEEK_SET) < 0 + || read_full (fd, phdrs, + hdr.e.e_phnum * sizeof (ElfW(Phdr))) < 0) { - vm_address_t buf; - vm_size_t offs = ph->p_offset & (ph->p_align - 1); - vm_size_t bufsz = round_page (ph->p_filesz + offs); - - buf = (vm_address_t) mmap (0, bufsz, - PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); - assert_backtrace (buf != MAP_FAILED); - - lseek (fd, ph->p_offset, SEEK_SET); - err = read (fd, (void *)(buf + offs), ph->p_filesz); - assert_backtrace (err == (ph->p_filesz)); - - ph->p_memsz = ((ph->p_vaddr + ph->p_memsz + ph->p_align - 1) - & ~(ph->p_align - 1)); - ph->p_vaddr &= ~(ph->p_align - 1); - ph->p_memsz -= ph->p_vaddr; - - vm_allocate (t, (vm_address_t*)&ph->p_vaddr, ph->p_memsz, 0); - vm_write (t, ph->p_vaddr, buf, bufsz); - munmap ((caddr_t) buf, bufsz); - vm_protect (t, ph->p_vaddr, ph->p_memsz, 0, - ((ph->p_flags & PF_R) ? VM_PROT_READ : 0) | - ((ph->p_flags & PF_W) ? VM_PROT_WRITE : 0) | - ((ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0)); + free (phdrs); + close (fd); + load_fail (t, file); } + for (ph = phdrs; ph < &phdrs[hdr.e.e_phnum]; ++ph) + if (ph->p_type == PT_LOAD) + { + /* A segment must lie within the file and have a + valid, power-of-two alignment. */ + if (ph->p_align == 0 + || (ph->p_align & (ph->p_align - 1)) != 0 + || ph->p_offset > (uintmax_t) st.st_size + || (uintmax_t) ph->p_filesz + > (uintmax_t) st.st_size - ph->p_offset + || ph->p_memsz < ph->p_filesz) + continue; + + { + vm_address_t buf; + vm_size_t offs = ph->p_offset & (ph->p_align - 1); + vm_size_t bufsz = round_page (ph->p_filesz + offs); + + buf = (vm_address_t) mmap (0, bufsz, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + if (buf == MAP_FAILED + || lseek (fd, ph->p_offset, SEEK_SET) < 0 + || read_full (fd, (void *) (buf + offs), ph->p_filesz) < 0) + { + if (buf != MAP_FAILED) + munmap ((caddr_t) buf, bufsz); + free (phdrs); + close (fd); + load_fail (t, file); + } + + ph->p_memsz = ((ph->p_vaddr + ph->p_memsz + ph->p_align - 1) + & ~(ph->p_align - 1)); + ph->p_vaddr &= ~(ph->p_align - 1); + ph->p_memsz -= ph->p_vaddr; + + if (vm_allocate (t, (vm_address_t *) &ph->p_vaddr, ph->p_memsz, + 0) + || vm_write (t, ph->p_vaddr, buf, bufsz)) + { + munmap ((caddr_t) buf, bufsz); + free (phdrs); + close (fd); + load_fail (t, file); + } + munmap ((caddr_t) buf, bufsz); + vm_protect (t, ph->p_vaddr, ph->p_memsz, 0, + ((ph->p_flags & PF_R) ? VM_PROT_READ : 0) | + ((ph->p_flags & PF_W) ? VM_PROT_WRITE : 0) | + ((ph->p_flags & PF_X) ? VM_PROT_EXECUTE : 0)); + } + } + free (phdrs); + } + close (fd); return hdr.e.e_entry; } else @@ -250,8 +357,12 @@ load_image (task_t t, buf = mmap (0, rndamount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); assert_backtrace (buf != MAP_FAILED); lseek (fd, sizeof hdr.a - headercruft, SEEK_SET); - err = read (fd, buf, amount); - assert_backtrace (err == amount); + if (read_full (fd, buf, amount) < 0) + { + munmap ((caddr_t) buf, rndamount); + close (fd); + load_fail (t, file); + } vm_allocate (t, &base, rndamount, 0); vm_write (t, base, (vm_address_t) buf, rndamount); if (magic != OMAGIC) @@ -261,59 +372,69 @@ load_image (task_t t, bssstart = base + hdr.a.a_text + hdr.a.a_data + headercruft; bsspagestart = round_page (bssstart); - vm_allocate (t, &bsspagestart, - hdr.a.a_bss - (bsspagestart - bssstart), 0); + if (hdr.a.a_bss > bsspagestart - bssstart) + vm_allocate (t, &bsspagestart, + hdr.a.a_bss - (bsspagestart - bssstart), 0); + close (fd); return hdr.a.a_entry; } } +static void +write_str (const char *msg, size_t len) +{ + ssize_t err; + do + err = write (2, msg, len); + while (err < 0 && errno == EINTR); +} + int boot_script_exec_cmd (void *hook, mach_port_t task, char *path, int argc, char **argv, char *strings, int stringlen) { char *args, *p; - int arg_len, i; + int i; + size_t arg_len, len; mach_msg_type_number_t reg_size; void *arg_pos; vm_offset_t stack_start, stack_end; vm_address_t startpc, str_start; thread_t thread; - ssize_t err; - size_t len; + error_t err; len = strlen (path); - err = write (2, path, len); - assert_backtrace (err == len); + write_str (path, len); for (i = 1; i < argc; ++i) { int quote = !! index (argv[i], ' ') || !! index (argv[i], '\t'); - err = write (2, " ", 1); - assert_backtrace (err == 1); + write_str (" ", 1); if (quote) - { - err = write (2, "\"", 1); - assert_backtrace (err == 1); - } + write_str ("\"", 1); len = strlen (argv[i]); - err = write (2, argv[i], len); - assert_backtrace (err == len); + write_str (argv[i], len); if (quote) - { - err = write (2, "\"", 1); - assert_backtrace (err == 1); - } + write_str ("\"", 1); } - err = write (2, "\r\n", 2); - assert_backtrace (err == 2); + write_str ("\r\n", 2); startpc = load_image (task, path); arg_len = stringlen + (argc + 2) * sizeof (char *) + sizeof (intptr_t); arg_len += 5 * sizeof (intptr_t); + if (arg_len > 16 * 1024 * 1024) + { + error (0, ENOMEM, "%s: argument list too long", path); + return BOOT_SCRIPT_EXEC_ERROR; + } stack_end = VM_MAX_ADDRESS; stack_start = VM_MAX_ADDRESS - 16 * 1024 * 1024; - vm_allocate (task, &stack_start, stack_end - stack_start, FALSE); + if (vm_allocate (task, &stack_start, stack_end - stack_start, FALSE)) + { + error (0, ENOMEM, "%s: vm_allocate", path); + return BOOT_SCRIPT_EXEC_ERROR; + } arg_pos = (void *) ((stack_end - arg_len) & ~(sizeof (intptr_t) - 1)); args = mmap (0, stack_end - trunc_page ((vm_offset_t) arg_pos), PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); @@ -334,12 +455,24 @@ boot_script_exec_cmd (void *hook, p = (void *) p + sizeof (char *); memcpy (p, strings, stringlen); memset (args, 0, (vm_offset_t)arg_pos & (vm_page_size - 1)); - vm_write (task, trunc_page ((vm_offset_t) arg_pos), (vm_address_t) args, - stack_end - trunc_page ((vm_offset_t) arg_pos)); + if (vm_write (task, trunc_page ((vm_offset_t) arg_pos), + (vm_address_t) args, + stack_end - trunc_page ((vm_offset_t) arg_pos))) + { + error (0, ENOMEM, "%s: vm_write", path); + munmap ((caddr_t) args, + stack_end - trunc_page ((vm_offset_t) arg_pos)); + return BOOT_SCRIPT_EXEC_ERROR; + } munmap ((caddr_t) args, stack_end - trunc_page ((vm_offset_t) arg_pos)); - thread_create (task, &thread); + err = thread_create (task, &thread); + if (err) + { + error (0, err, "%s: thread_create", path); + return BOOT_SCRIPT_EXEC_ERROR; + } #ifdef i386_THREAD_STATE_COUNT { struct i386_thread_state regs; @@ -382,7 +515,13 @@ boot_script_exec_cmd (void *hook, # error needs to be ported #endif - thread_resume (thread); + err = thread_resume (thread); + if (err) + { + error (0, err, "%s: thread_resume", path); + mach_port_deallocate (mach_task_self (), thread); + return BOOT_SCRIPT_EXEC_ERROR; + } mach_port_deallocate (mach_task_self (), thread); return 0; } -- 2.43.0
