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 | 52 ++++++++++++++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 21 deletions(-)
diff --git a/server/debug.c b/server/debug.c
index 2231d40a0..1fc56ec50 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"
@@ -69,19 +70,22 @@ prologue (FILE *fp)
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;
-
if (!verbose)
return;
- fp = open_memstream (&str, &len);
- if (fp == NULL) {
+ int err = errno;
+
+#ifndef WIN32
+ int tty;
+#endif
+ 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;
+
+ /* The "inner" string is the debug string before escaping. */
+ fp_inner = open_memstream (&str_inner, &len_inner);
+ if (fp_inner == NULL) {
fail:
/* Try to emit what we can. */
errno = err;
@@ -89,28 +93,34 @@ debug_common (bool in_server, const char *fs, va_list args)
fprintf (stderr, "\n");
return;
}
+ 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");
- close_memstream (fp);
+ fprintf (fp_outer, "\n");
+ close_memstream (fp_outer);
- if (str)
- fputs (str, stderr);
- else
+ if (!str_outer)
goto fail;
+ /* Send it to stderr as atomically as possible. */
+ fputs (str_outer, stderr);
+
errno = err;
}
--
2.39.2