nbd_connect_command (h, (char **) { NULL }) triggers SIGABRT when preparing to exec a NULL command name (during enter_STATE_CONNECT_COMMAND_START in v1.0).
nbd_connect_command (h, NULL) in newer releases triggers SIGSEGV by trying to dereference NULL (with LIBNBD_DEBUG=1, during nbd_internal_printable_string_list in v1.4; otherwise, during nbd_internal_set_argv in v1.6). In older releases, it behaves the same as (char **) { NULL } and triggers SIGABRT. Both crashes are corner cases that have existed for a long time, and unlikely to hit in real code. Still, we shouldn't crash in a library. Forbid a NULL StringList in general (similar to how we already forbid a NULL String); which also means a StringList parameter implies may_set_error=true. Refactor nbd_internal_set_argv() to split out the vector population into a new helper function that can only fail with ENOMEM (this function will also be useful in later patches that want to copy a StringList, but can tolerate 0 elements), as well as to set errors itself (all 2 callers updated) since detecting NULL for argv[0] is unique to argv. Tests are added in the next patch, to make it easier to review by temporarily swapping patch order. Changes from: $ nbdsh -c 'h.connect_command([])' nbdsh: generator/states-connect.c:247: enter_STATE_CONNECT_COMMAND_START: Assertion `h->argv.ptr[0]' failed. Aborted (core dumped) to: nbdsh: command line script failed: nbd_connect_command: missing command name in argv list: Invalid argument Fixes: 0b7bdf62 ("generator: Improve trace messages.", v1.3.2) Fixes: 6f3eee2e ("lib: Replace a few uses of realloc with nbdkit vector library.", v1.5.5) Fixes: 8b164376 ("api: Get rid of nbd_connection.", v0.1) --- lib/internal.h | 3 ++- generator/API.ml | 3 ++- generator/C.ml | 2 +- lib/connect.c | 10 +++------- lib/utils.c | 45 ++++++++++++++++++++++++++++++++++----------- 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/lib/internal.h b/lib/internal.h index e4c08ca3..04bd8134 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -476,7 +476,8 @@ extern int nbd_internal_aio_get_direction (enum state state); /* utils.c */ extern void nbd_internal_hexdump (const void *data, size_t len, FILE *fp); -extern int nbd_internal_set_argv (string_vector *v, char **argv); +extern int nbd_internal_copy_string_list (string_vector *v, char **in); +extern int nbd_internal_set_argv (struct nbd_handle *h, char **argv); extern const char *nbd_internal_fork_safe_itoa (long v, char *buf, size_t len); extern void nbd_internal_fork_safe_perror (const char *s); extern char *nbd_internal_printable_buffer (const void *buf, size_t count); diff --git a/generator/API.ml b/generator/API.ml index abf77972..7be870a4 100644 --- a/generator/API.ml +++ b/generator/API.ml @@ -3458,7 +3458,8 @@ let () = | name, { args; may_set_error = false } when List.exists (function - | Closure _ | Enum _ | Flags _ | String _ -> true + | Closure _ | Enum _ | Flags _ + | String _ | StringList _ -> true | _ -> false) args -> failwithf "%s: if args contains Closure/Enum/Flags/String parameter, may_set_error must be false" name diff --git a/generator/C.ml b/generator/C.ml index b2d46f98..73ecffa0 100644 --- a/generator/C.ml +++ b/generator/C.ml @@ -570,7 +570,7 @@ let need_out_label := true | Flags (n, flags) -> print_flags_check n flags None - | String n -> + | String n | StringList n -> let value = match errcode with | Some value -> value | None -> assert false in diff --git a/lib/connect.c b/lib/connect.c index 50080630..ffa50d5b 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -1,5 +1,5 @@ /* NBD client library in userspace - * Copyright (C) 2013-2020 Red Hat Inc. + * Copyright (C) 2013-2022 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -251,10 +251,8 @@ nbd_unlocked_aio_connect_socket (struct nbd_handle *h, int sock) int nbd_unlocked_aio_connect_command (struct nbd_handle *h, char **argv) { - if (nbd_internal_set_argv (&h->argv, argv) == -1) { - set_error (errno, "realloc"); + if (nbd_internal_set_argv (h, argv) == -1) return -1; - } return nbd_internal_run (h, cmd_connect_command); } @@ -263,10 +261,8 @@ int nbd_unlocked_aio_connect_systemd_socket_activation (struct nbd_handle *h, char **argv) { - if (nbd_internal_set_argv (&h->argv, argv) == -1) { - set_error (errno, "realloc"); + if (nbd_internal_set_argv (h, argv) == -1) return -1; - } return nbd_internal_run (h, cmd_connect_sa); } diff --git a/lib/utils.c b/lib/utils.c index 3d3b7f45..da3cee72 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -1,5 +1,5 @@ /* NBD client library in userspace - * Copyright (C) 2013-2020 Red Hat Inc. + * Copyright (C) 2013-2022 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -53,16 +53,17 @@ nbd_internal_hexdump (const void *data, size_t len, FILE *fp) } } -/* Replace the string_vector with a deep copy of argv (including final NULL). */ +/* Replace a string_vector with a deep copy of in (including final NULL). */ int -nbd_internal_set_argv (string_vector *v, char **argv) +nbd_internal_copy_string_list (string_vector *v, char **in) { size_t i; + assert (in); string_vector_reset (v); - for (i = 0; argv[i] != NULL; ++i) { - char *copy = strdup (argv[i]); + for (i = 0; in[i] != NULL; ++i) { + char *copy = strdup (in[i]); if (copy == NULL) return -1; if (string_vector_append (v, copy) == -1) { @@ -74,6 +75,24 @@ nbd_internal_set_argv (string_vector *v, char **argv) return string_vector_append (v, NULL); } +/* Store argv into h, or diagnose an error on failure. */ +int +nbd_internal_set_argv (struct nbd_handle *h, char **argv) +{ + assert (argv); + + if (argv[0] == NULL) { + set_error (EINVAL, "missing command name in argv list"); + return -1; + } + if (nbd_internal_copy_string_list (&h->argv, argv) == -1) { + set_error (errno, "realloc"); + return -1; + } + + return 0; +} + /* Like sprintf ("%ld", v), but safe to use between fork and exec. Do * not use this function in any other context. * @@ -247,13 +266,17 @@ nbd_internal_printable_string_list (char **list) if (fp == NULL) return NULL; - fprintf (fp, "["); - for (i = 0; list[i] != NULL; ++i) { - if (i > 0) - fprintf (fp, ", "); - printable_string (list[i], fp); + if (list == NULL) + fprintf (fp, "NULL"); + else { + fprintf (fp, "["); + for (i = 0; list[i] != NULL; ++i) { + if (i > 0) + fprintf (fp, ", "); + printable_string (list[i], fp); + } + fprintf (fp, "]"); } - fprintf (fp, "]"); fclose (fp); return s; -- 2.37.3 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs