Hi everyone,

Here is the second patch of the series for hurd/boot.

This patch focuses on robustness and error hardening during server startup,
option parsing, and boot script processing:

   1.

   *Mach Port & Pseudo-Device Allocation Checks:*
   -

      Check return values for all mach_port_* and task_set_name calls in
      allocate_pseudo_ports(), main(), and pseudo-device setup routines to
      prevent silent failures.
      -

      Properly create and configure wake_pipe and select_pipe via pipe2()
      with O_NONBLOCK | O_CLOEXEC.
      2.

   *Boot Script Parsing & Memory Management:*
   -

      Handle EINTR signal interrupts gracefully in read_boot_script() and
      switch buffer allocation to exponential growth ($len \times 2$) with
      overflow checks.
      -

      Track and report line numbers (lineno) when reporting syntax or
      evaluation errors in boot scripts.
      -

      Ignore reserved boot script variables (host-port, device-port,
      kernel-task, kernel-command-line, root-device, boot-args) when passed
      from the kernel command line to avoid unwanted overrides.
      -

      Handle memory allocation failures in add_dev_map(), strdup(), and
      option parsers cleanly.
      3.

   *Safe Diagnostics:*
   -

      Replace unsafe/blocking write() calls and assert_backtrace() write
      checks with the signal-safe write_diag() helper introduced in the
      previous patch.
      ---


>From 6a9ac9915e800ddc9fe597d17f84bae441d13f30 Mon Sep 17 00:00:00 2001
From: Alperen ERKAN <[email protected]>
Date: Sat, 16 Sep 2026 12:50:33 +0300
Subject: [PATCH 2/5] boot: check errors in startup, option parsing and boot
script reading

Check the mach_port_* return values in allocate_pseudo_ports, main
and the pseudo device setup, create the wake/select pipes, reject
reserved boot script variables from the kernel command line, report
boot script line numbers, and make read_boot_script handle EINTR and
exponential buffer growth.

---
 hurd/boot/boot.c | 296
++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
 1 file changed, 184 insertions(+), 112 deletions(-)

diff --git a/hurd/boot/boot.c b/hurd/boot/boot.c
--- a/hurd/boot/boot.c
+++ b/hurd/boot/boot.c
@@ -388,6 +395,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;
@@ -444,7 +445,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:
@@ -471,47 +494,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;
 }
@@ -547,20 +544,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;
@@ -565,20 +572,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;
@@ -616,12 +613,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";

@@ -658,8 +664,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)
@@ -678,41 +703,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);

@@ -759,7 +761,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)
@@ -788,49 +805,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)
@@ -858,39 +852,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 ();
@@ -895,14 +891,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);
--
2.43.0

Reply via email to