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

Reply via email to