From 3a105adc4c16025977ff78e3f26f4033ac12f567 Mon Sep 17 00:00:00 2001 From: Peter Bigot Date: Fri, 11 Dec 2020 13:51:22 -0600 Subject: [PATCH] lib: cbprintf: fix mishandling of precision string output If a precision flag is included for s formatting that bounds the maximum output length, so we need to use strnlen rather than strlen to get the amount of data to emit. With that flag we can't expect there to be a terminating NUL following the text to print. Also fix handling of an empty precision, which should behave as if a precision of zero was provided. Signed-off-by: Peter Bigot --- lib/os/cbprintf_complete.c | 34 +++++++++++++++++++--------------- tests/unit/cbprintf/main.c | 4 ++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/lib/os/cbprintf_complete.c b/lib/os/cbprintf_complete.c index 461589a08b03..fb431f57c425 100644 --- a/lib/os/cbprintf_complete.c +++ b/lib/os/cbprintf_complete.c @@ -19,6 +19,11 @@ #include #include +/* newlib doesn't declare this function unless __POSIX_VISIBLE >= 200809. No + * idea how to make that happen, so lets put it right here. + */ +size_t strnlen(const char *, size_t); + /* Provide typedefs used for signed and unsigned integral types * capable of holding all convertable integral values. */ @@ -342,8 +347,9 @@ static inline const char *extract_flags(struct conversion *conv, static inline const char *extract_width(struct conversion *conv, const char *sp) { + conv->width_present = true; + if (*sp == '*') { - conv->width_present = true; conv->width_star = true; return ++sp; } @@ -375,27 +381,24 @@ static inline const char *extract_width(struct conversion *conv, static inline const char *extract_prec(struct conversion *conv, const char *sp) { - if (*sp != '.') { + conv->prec_present = (*sp == '.'); + + if (!conv->prec_present) { return sp; } ++sp; if (*sp == '*') { - conv->prec_present = true; conv->prec_star = true; return ++sp; } - const char *wp = sp; size_t prec = extract_decimal(&sp); - if (sp != wp) { - conv->prec_present = true; - conv->prec_value = prec; - if (prec != conv->prec_value) { - /* Lost precision data */ - conv->unsupported = true; - } + conv->prec_value = prec; + if (prec != conv->prec_value) { + /* Lost precision data */ + conv->unsupported = true; } return sp; @@ -1579,11 +1582,12 @@ int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap) case 's': { bps = (const char *)value->ptr; - size_t len = strlen(bps); + size_t len; - if ((precision >= 0) - && ((size_t)precision < len)) { - len = (size_t)precision; + if (precision >= 0) { + len = strnlen(bps, precision); + } else { + len = strlen(bps); } bpe = bps + len; diff --git a/tests/unit/cbprintf/main.c b/tests/unit/cbprintf/main.c index 663306d94916..1e4f0b6c7684 100644 --- a/tests/unit/cbprintf/main.c +++ b/tests/unit/cbprintf/main.c @@ -341,8 +341,8 @@ static void test_s(void) return; } - rc = TEST_PRF("/%.6s/%.2s/", s, s); - PRF_CHECK("/123/12/", rc); + rc = TEST_PRF("/%.6s/%.2s/%.s/", s, s, s); + PRF_CHECK("/123/12//", rc); rc = TEST_PRF("%ls", ws); if (IS_ENABLED(USE_LIBC)) {