Skip to content

Commit 3a105ad

Browse files
committed
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 <[email protected]>
1 parent 4f35535 commit 3a105ad

File tree

2 files changed

+21
-17
lines changed

2 files changed

+21
-17
lines changed

lib/os/cbprintf_complete.c

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
#include <sys/util.h>
2020
#include <sys/cbprintf.h>
2121

22+
/* newlib doesn't declare this function unless __POSIX_VISIBLE >= 200809. No
23+
* idea how to make that happen, so lets put it right here.
24+
*/
25+
size_t strnlen(const char *, size_t);
26+
2227
/* Provide typedefs used for signed and unsigned integral types
2328
* capable of holding all convertable integral values.
2429
*/
@@ -342,8 +347,9 @@ static inline const char *extract_flags(struct conversion *conv,
342347
static inline const char *extract_width(struct conversion *conv,
343348
const char *sp)
344349
{
350+
conv->width_present = true;
351+
345352
if (*sp == '*') {
346-
conv->width_present = true;
347353
conv->width_star = true;
348354
return ++sp;
349355
}
@@ -375,27 +381,24 @@ static inline const char *extract_width(struct conversion *conv,
375381
static inline const char *extract_prec(struct conversion *conv,
376382
const char *sp)
377383
{
378-
if (*sp != '.') {
384+
conv->prec_present = (*sp == '.');
385+
386+
if (!conv->prec_present) {
379387
return sp;
380388
}
381389
++sp;
382390

383391
if (*sp == '*') {
384-
conv->prec_present = true;
385392
conv->prec_star = true;
386393
return ++sp;
387394
}
388395

389-
const char *wp = sp;
390396
size_t prec = extract_decimal(&sp);
391397

392-
if (sp != wp) {
393-
conv->prec_present = true;
394-
conv->prec_value = prec;
395-
if (prec != conv->prec_value) {
396-
/* Lost precision data */
397-
conv->unsupported = true;
398-
}
398+
conv->prec_value = prec;
399+
if (prec != conv->prec_value) {
400+
/* Lost precision data */
401+
conv->unsupported = true;
399402
}
400403

401404
return sp;
@@ -1579,11 +1582,12 @@ int cbvprintf(cbprintf_cb out, void *ctx, const char *fp, va_list ap)
15791582
case 's': {
15801583
bps = (const char *)value->ptr;
15811584

1582-
size_t len = strlen(bps);
1585+
size_t len;
15831586

1584-
if ((precision >= 0)
1585-
&& ((size_t)precision < len)) {
1586-
len = (size_t)precision;
1587+
if (precision >= 0) {
1588+
len = strnlen(bps, precision);
1589+
} else {
1590+
len = strlen(bps);
15871591
}
15881592

15891593
bpe = bps + len;

tests/unit/cbprintf/main.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,8 @@ static void test_s(void)
341341
return;
342342
}
343343

344-
rc = TEST_PRF("/%.6s/%.2s/", s, s);
345-
PRF_CHECK("/123/12/", rc);
344+
rc = TEST_PRF("/%.6s/%.2s/%.s/", s, s, s);
345+
PRF_CHECK("/123/12//", rc);
346346

347347
rc = TEST_PRF("%ls", ws);
348348
if (IS_ENABLED(USE_LIBC)) {

0 commit comments

Comments
 (0)