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 <peter.bigot@nordicsemi.no>
This commit is contained in:
Peter Bigot 2020-12-11 13:51:22 -06:00 committed by Anas Nashif
parent 5be8afcf73
commit 53762239c1
2 changed files with 21 additions and 17 deletions

View file

@ -19,6 +19,11 @@
#include <sys/util.h>
#include <sys/cbprintf.h>
/* 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;

View file

@ -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)) {