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