Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 0/6] server: Add -D nbdkit.environ=1 to dump the environment
Add R-b tags. Split the last patch into 3 parts, fixing the error handling issues raised by Laszlo in the previous review. Rich.
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 1/6] server: Add -D nbdkit.environ=1 to dump the environment
This is not secure so should not be used routinely. Also we do not
attempt to filter environment variables, so even ones containing
multiple lines or special characters are all sent to nbdkit_debug.
The reason for adding this is to allow for debugging the new
nbd_set_socket_activation_name(3) API added in libnbd 1.16.
Reviewed-by: Laszlo Ersek <lersek at redhat.com>
---
docs/nbdkit.pod | 8 ++++++++
server/main.c | 22 ++++++++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/docs/nbdkit.pod b/docs/nbdkit.pod
index 83cf6a11c..634c97e3a 100644
--- a/docs/nbdkit.pod
+++ b/docs/nbdkit.pod
@@ -619,6 +619,14 @@ are numerous and not usually very interesting.
S<I<-D nbdkit.backend.controlpath=0>> suppresses the non-datapath
commands (config, open, close, can_write, etc.)
+=item B<-D nbdkit.environ=1>
+
+Print nbdkit's environment variables in the debug output at start up.
+This is insecure because environment variables may contain both
+sensitive and user-controlled information, so it should not be used
+routinely. But it is useful for tracking down problems related to
+environment variables.
+
=item B<-D nbdkit.tls.log=>N
Enable TLS logging. C<N> can be in the range 0 (no logging) to 99.
diff --git a/server/main.c b/server/main.c
index 1df5d69ac..528a2dfea 100644
--- a/server/main.c
+++ b/server/main.c
@@ -210,6 +210,22 @@ dump_config (void)
#endif
}
+/* -D nbdkit.environ=1 to dump the environment at start up. */
+NBDKIT_DLL_PUBLIC int nbdkit_debug_environ;
+
+#ifndef HAVE_ENVIRON_DECL
+extern char **environ;
+#endif
+
+static void
+dump_environment (void)
+{
+ size_t i;
+
+ for (i = 0; environ[i]; ++i)
+ nbdkit_debug ("%s", environ[i]);
+}
+
int
main (int argc, char *argv[])
{
@@ -662,6 +678,12 @@ main (int argc, char *argv[])
/* Check all debug flags were used, and free them. */
free_debug_flags ();
+ /* Dump the environment if asked. This is the earliest we can do it
+ * because it uses a debug flag.
+ */
+ if (nbdkit_debug_environ && verbose)
+ dump_environment ();
+
if (help) {
struct backend *b;
--
2.39.2
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 2/6] server: debug: Don't emit ANSI colour codes on Windows
At least under Wine they are not recognized and you just see literal
"[0m" etc appearing in the output.
Updates: commit 5a011e02375912f2ad88de1ebc6d609e29d38f71
Reviewed-by: Laszlo Ersek <lersek at redhat.com>
---
server/debug.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/server/debug.c b/server/debug.c
index 1b5ddaafe..2231d40a0 100644
--- a/server/debug.c
+++ b/server/debug.c
@@ -70,7 +70,9 @@ static void
debug_common (bool in_server, const char *fs, va_list args)
{
int err = errno;
+#ifndef WIN32
int tty;
+#endif
CLEANUP_FREE char *str = NULL;
size_t len = 0;
FILE *fp;
@@ -88,15 +90,19 @@ debug_common (bool in_server, const char *fs, va_list args)
return;
}
+#ifndef WIN32
tty = isatty (fileno (stderr));
if (!in_server && tty) ansi_force_colour (ANSI_FG_BOLD_BLACK, fp);
+#endif
prologue (fp);
errno = err;
vfprintf (fp, fs, args);
+#ifndef WIN32
if (!in_server && tty) ansi_force_restore (fp);
+#endif
fprintf (fp, "\n");
close_memstream (fp);
--
2.39.2
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 3/6] common/utils: Add C string quoting function
eg. { '\r', '\n' } -> { '\\', 'n',
'\\', 'r' }
---
common/utils/utils.h | 1 +
common/utils/quote.c | 58 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/common/utils/utils.h b/common/utils/utils.h
index 42288a5cd..1e6d8972b 100644
--- a/common/utils/utils.h
+++ b/common/utils/utils.h
@@ -35,6 +35,7 @@
extern void shell_quote (const char *str, FILE *fp);
extern void uri_quote (const char *str, FILE *fp);
+extern void c_string_quote (const char *str, FILE *fp);
extern int exit_status_to_nbd_error (int status, const char *cmd);
extern int set_cloexec (int fd);
extern int set_nonblock (int fd);
diff --git a/common/utils/quote.c b/common/utils/quote.c
index 863e08a8e..877035387 100644
--- a/common/utils/quote.c
+++ b/common/utils/quote.c
@@ -37,6 +37,9 @@
#include <string.h>
#include <unistd.h>
+#include "ascii-ctype.h"
+#include "hexdigit.h"
+
/* Print str to fp, shell quoting if necessary. This comes from
* libguestfs, but was written by me so I'm relicensing it to a BSD
* license for nbdkit.
@@ -98,3 +101,58 @@ uri_quote (const char *str, FILE *fp)
fprintf (fp, "%%%02X", str[i] & 0xff);
}
}
+
+/* Print str to fp, quoting in a way similar to C strings, for example
+ * '\n' -> "\n".
+ *
+ * Note that we do not emit quotes around the string, and double
+ * quotes within the string are not escaped.
+ */
+void
+c_string_quote (const char *str, FILE *fp)
+{
+ size_t i;
+ char c;
+
+ for (i = 0; c = str[i], c != '\0'; ++i) {
+ if (ascii_isprint (c))
+ fputc (c, fp);
+ else {
+ switch (c) {
+ case '\a':
+ fputc ('\\', fp);
+ fputc ('a', fp);
+ break;
+ case '\b':
+ fputc ('\\', fp);
+ fputc ('b', fp);
+ break;
+ case '\f':
+ fputc ('\\', fp);
+ fputc ('f', fp);
+ break;
+ case '\n':
+ fputc ('\\', fp);
+ fputc ('n', fp);
+ break;
+ case '\r':
+ fputc ('\\', fp);
+ fputc ('r', fp);
+ break;
+ case '\t':
+ fputc ('\\', fp);
+ fputc ('t', fp);
+ break;
+ case '\v':
+ fputc ('\\', fp);
+ fputc ('v', fp);
+ break;
+ default:
+ fputc ('\\', fp);
+ fputc ('x', fp);
+ fputc (hexchar (c >> 4), fp);
+ fputc (hexchar (c), fp);
+ }
+ }
+ }
+}
--
2.39.2
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 4/6] server: debug: Return earlier if not verbose
In particular return before we have allocated 'str' with
attribute((cleanup)) so that we don't waste time calling free (NULL)
on the hot-ish path out of the function.
---
server/debug.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/server/debug.c b/server/debug.c
index 2231d40a0..8afa0ae8b 100644
--- a/server/debug.c
+++ b/server/debug.c
@@ -69,6 +69,9 @@ prologue (FILE *fp)
static void
debug_common (bool in_server, const char *fs, va_list args)
{
+ if (!verbose)
+ return;
+
int err = errno;
#ifndef WIN32
int tty;
@@ -77,9 +80,6 @@ debug_common (bool in_server, const char *fs, va_list args)
size_t len = 0;
FILE *fp;
- if (!verbose)
- return;
-
fp = open_memstream (&str, &len);
if (fp == NULL) {
fail:
--
2.39.2
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 5/6] server: debug: Fix error handling
Preserve errno even if the function fails. We need to set
errno = err again on every exit path out of the function.
Check if close_memstream failed and go to the error path if so. This
is most important on Windows where close_memstream is what actually
allocates the memory. But even on Unix, close_memstream (which is an
alias for fclose) can return an error.
Move the error-handling code to the end of the function.
I added a comment above the 'fputs' explaining why exactly we're
using
memstream in the first place -- because it is more atomic than using
multiple fprintf.
See: https://listman.redhat.com/archives/libguestfs/2023-May/031456.html
Thanks: Laszlo Ersek
---
server/debug.c | 28 ++++++++++++++++------------
1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/server/debug.c b/server/debug.c
index 8afa0ae8b..177d9d5da 100644
--- a/server/debug.c
+++ b/server/debug.c
@@ -81,14 +81,8 @@ debug_common (bool in_server, const char *fs, va_list args)
FILE *fp;
fp = open_memstream (&str, &len);
- if (fp == NULL) {
- fail:
- /* Try to emit what we can. */
- errno = err;
- vfprintf (stderr, fs, args);
- fprintf (stderr, "\n");
- return;
- }
+ if (fp == NULL)
+ goto fail;
#ifndef WIN32
tty = isatty (fileno (stderr));
@@ -104,13 +98,23 @@ debug_common (bool in_server, const char *fs, va_list args)
if (!in_server && tty) ansi_force_restore (fp);
#endif
fprintf (fp, "\n");
- close_memstream (fp);
+ if (close_memstream (fp) == -1)
+ goto fail;
- if (str)
- fputs (str, stderr);
- else
+ if (!str)
goto fail;
+ /* Send it to stderr as atomically as possible. */
+ fputs (str, stderr);
+
+ errno = err;
+ return;
+
+ fail:
+ /* Try to emit what we can. */
+ errno = err;
+ vfprintf (stderr, fs, args);
+ fprintf (stderr, "\n");
errno = err;
}
--
2.39.2
Richard W.M. Jones
2023-May-09 14:51 UTC
[Libguestfs] [PATCH nbdkit v3 6/6] server: debug: Escape debug strings
Debug strings contain all kinds of information including some under
user control. Previously we simply sent everything to stderr, but
this is potentially insecure, as well as not dealing well with
non-printable characters. Escape these strings when printing.
---
server/debug.c | 38 +++++++++++++++++++++++---------------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/server/debug.c b/server/debug.c
index 177d9d5da..6cf1af316 100644
--- a/server/debug.c
+++ b/server/debug.c
@@ -42,6 +42,7 @@
#include "ansi-colours.h"
#include "open_memstream.h"
+#include "utils.h"
#include "internal.h"
@@ -76,36 +77,43 @@ debug_common (bool in_server, const char *fs, va_list args)
#ifndef WIN32
int tty;
#endif
- CLEANUP_FREE char *str = NULL;
- size_t len = 0;
- FILE *fp;
+ CLEANUP_FREE char *str_inner = NULL;
+ CLEANUP_FREE char *str_outer = NULL;
+ FILE *fp_inner, *fp_outer;
+ size_t len_inner = 0, len_outer = 0;
- fp = open_memstream (&str, &len);
- if (fp == NULL)
+ /* The "inner" string is the debug string before escaping. */
+ fp_inner = open_memstream (&str_inner, &len_inner);
+ if (fp_inner == NULL)
goto fail;
+ errno = err;
+ vfprintf (fp_inner, fs, args);
+ close_memstream (fp_inner);
+
+ /* The "outer" string contains the prologue, escaped debug string
and \n. */
+ fp_outer = open_memstream (&str_outer, &len_outer);
+ if (fp_outer == NULL) goto fail;
#ifndef WIN32
tty = isatty (fileno (stderr));
- if (!in_server && tty) ansi_force_colour (ANSI_FG_BOLD_BLACK, fp);
+ if (!in_server && tty) ansi_force_colour (ANSI_FG_BOLD_BLACK,
fp_outer);
#endif
- prologue (fp);
-
- errno = err;
- vfprintf (fp, fs, args);
+ prologue (fp_outer);
+ c_string_quote (str_inner, fp_outer);
#ifndef WIN32
- if (!in_server && tty) ansi_force_restore (fp);
+ if (!in_server && tty) ansi_force_restore (fp_outer);
#endif
- fprintf (fp, "\n");
- if (close_memstream (fp) == -1)
+ fprintf (fp_outer, "\n");
+ if (close_memstream (fp_outer) == -1)
goto fail;
- if (!str)
+ if (!str_outer)
goto fail;
/* Send it to stderr as atomically as possible. */
- fputs (str, stderr);
+ fputs (str_outer, stderr);
errno = err;
return;
--
2.39.2