From a3faa73f03a4d539de4c808d1a4f3b8ebc5ce537 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 10 Jun 2017 03:02:39 -0400 Subject: printf: Fix embedded nul bytes Fixes #26. --- dstring.c | 4 ++++ dstring.h | 11 +++++++++++ printf.c | 27 ++++++++++++++++----------- tests.sh | 6 ++++++ tests/test_printf_nul.out | Bin 0 -> 198 bytes 5 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 tests/test_printf_nul.out diff --git a/dstring.c b/dstring.c index 5a6d4b3..54815d6 100644 --- a/dstring.c +++ b/dstring.c @@ -95,6 +95,10 @@ int dstrncat(char **dest, const char *src, size_t n) { return dstrcat_impl(dest, src, strnlen(src, n)); } +int dstrapp(char **str, char c) { + return dstrcat_impl(str, &c, 1); +} + void dstrfree(char *dstr) { if (dstr) { free(dstrheader(dstr)); diff --git a/dstring.h b/dstring.h index cd867ad..318bb0b 100644 --- a/dstring.h +++ b/dstring.h @@ -77,6 +77,17 @@ int dstrcat(char **dest, const char *src); */ int dstrncat(char **dest, const char *src, size_t n); +/** + * Append a single character to a dynamic string. + * + * @param str + * The string to append to. + * @param c + * The character to append. + * @return 0 on success, -1 on failure. + */ +int dstrapp(char **str, char c); + /** * Free a dynamic string. * diff --git a/printf.c b/printf.c index 33753e3..888fecb 100644 --- a/printf.c +++ b/printf.c @@ -48,7 +48,12 @@ struct bfs_printf_directive { /** Print some text as-is. */ static int bfs_printf_literal(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) { - return fprintf(file, "%s", directive->str); + size_t len = dstrlen(directive->str); + if (fwrite(directive->str, 1, len, file) == len) { + return 0; + } else { + return -1; + } } /** \c: flush */ @@ -517,8 +522,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) if (!directive) { goto directive_error; } - if (dstrncat(&directive->str, &c, 1) != 0) { - perror("dstralloc()"); + if (dstrapp(&directive->str, c) != 0) { + perror("dstrapp()"); goto directive_error; } @@ -540,8 +545,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) cfprintf(cerr, "%{er}error: '%s': Duplicate flag '%c'.%{rs}\n", format, c); goto directive_error; } - if (dstrncat(&directive->str, &c, 1) != 0) { - perror("dstrncat()"); + if (dstrapp(&directive->str, c) != 0) { + perror("dstrapp()"); goto directive_error; } continue; @@ -552,8 +557,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) // Parse the field width while (c >= '0' && c <= '9') { - if (dstrncat(&directive->str, &c, 1) != 0) { - perror("dstrncat()"); + if (dstrapp(&directive->str, c) != 0) { + perror("dstrapp()"); goto directive_error; } c = *++i; @@ -562,8 +567,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) // Parse the precision if (c == '.') { do { - if (dstrncat(&directive->str, &c, 1) != 0) { - perror("dstrncat()"); + if (dstrapp(&directive->str, c) != 0) { + perror("dstrapp()"); goto directive_error; } c = *++i; @@ -774,8 +779,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) } one_char: - if (dstrncat(&literal->str, &c, 1) != 0) { - perror("dstrncat()"); + if (dstrapp(&literal->str, c) != 0) { + perror("dstrapp()"); goto error; } } diff --git a/tests.sh b/tests.sh index a07ae8a..ef4bbfc 100755 --- a/tests.sh +++ b/tests.sh @@ -302,6 +302,7 @@ gnu_tests=( test_printf_escapes test_printf_times test_printf_leak + test_printf_nul test_quit_after_print test_quit_before_print test_fstype @@ -1036,6 +1037,11 @@ function test_printf_leak() { bfs_diff basic -maxdepth 0 -printf '%p' } +function test_printf_nul() { + # NUL byte regression test + bfs_diff basic -printf '%h\0%f\n' +} + function test_fstype() { fstype="$($BFS -printf '%F\n' | head -n1)" bfs_diff basic -fstype "$fstype" diff --git a/tests/test_printf_nul.out b/tests/test_printf_nul.out new file mode 100644 index 0000000..3e30a4f Binary files /dev/null and b/tests/test_printf_nul.out differ -- cgit v1.2.3