diff --git a/include/asm/format.h b/include/asm/format.h index 15b2fb48..b9042844 100644 --- a/include/asm/format.h +++ b/include/asm/format.h @@ -28,9 +28,9 @@ struct FormatSpec { bool prefix; bool alignLeft; bool padZero; - uint8_t width; + size_t width; bool hasFrac; - uint8_t fracWidth; + size_t fracWidth; int type; bool valid; }; diff --git a/src/asm/format.c b/src/asm/format.c index 456cfc25..8feb5d03 100644 --- a/src/asm/format.c +++ b/src/asm/format.c @@ -6,6 +6,7 @@ * SPDX-License-Identifier: MIT */ +#include #include #include #include @@ -148,20 +149,25 @@ void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, cha size_t len = strlen(value); size_t totalLen = fmt->width > len ? fmt->width : len; - if (totalLen + 1 > bufLen) /* bufLen includes terminator */ + if (totalLen > bufLen - 1) { /* bufLen includes terminator */ error("Formatted string value too long\n"); + totalLen = bufLen - 1; + if (len > totalLen) + len = totalLen; + } + assert(len < bufLen && totalLen < bufLen && len <= totalLen); - size_t padLen = fmt->width > len ? fmt->width - len : 0; + size_t padLen = totalLen - len; if (fmt->alignLeft) { - strncpy(buf, value, len < bufLen ? len : bufLen); - for (size_t i = 0; i < totalLen && len + i < bufLen; i++) - buf[len + i] = ' '; - } else { - for (size_t i = 0; i < padLen && i < bufLen; i++) + memcpy(buf, value, len); + for (size_t i = len; i < totalLen; i++) buf[i] = ' '; - if (bufLen > padLen) - strncpy(buf + padLen, value, bufLen - padLen - 1); + } else { + for (size_t i = 0; i < padLen; i++) + buf[i] = ' '; + if (totalLen > padLen) + memcpy(buf + padLen, value, len); } buf[totalLen] = '\0'; @@ -221,12 +227,18 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin /* Special case for fixed-point */ /* Default fractional width (C's is 6 for "%f"; here 5 is enough) */ - uint8_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5; + size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5; if (fracWidth) { + if (fracWidth > 255) { + error("Fractional width %zu too long, limiting to 255\n", + fracWidth); + fracWidth = 255; + } + char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */ - snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%d.f", fracWidth); + snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%zu.f", fracWidth); snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16, (value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5); } else { @@ -253,46 +265,47 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin size_t totalLen = fmt->width > numLen ? fmt->width : numLen; - if (totalLen + 1 > bufLen) /* bufLen includes terminator */ + if (totalLen > bufLen - 1) { /* bufLen includes terminator */ error("Formatted numeric value too long\n"); + totalLen = bufLen - 1; + if (numLen > totalLen) { + len -= numLen - totalLen; + numLen = totalLen; + } + } + assert(numLen < bufLen && totalLen < bufLen && numLen <= totalLen && len <= numLen); - size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0; + size_t padLen = totalLen - numLen; + size_t pos = 0; if (fmt->alignLeft) { - size_t pos = 0; - - if (sign && pos < bufLen) + if (sign) buf[pos++] = sign; - if (prefix && pos < bufLen) + if (prefix) buf[pos++] = prefix; - - strcpy(buf + pos, valueBuf); - pos += len; - - for (size_t i = 0; i < totalLen && pos + i < bufLen; i++) - buf[pos + i] = ' '; + memcpy(buf + pos, valueBuf, len); + for (size_t i = pos + len; i < totalLen; i++) + buf[i] = ' '; } else { - size_t pos = 0; - if (fmt->padZero) { /* sign, then prefix, then zero padding */ - if (sign && pos < bufLen) + if (sign) buf[pos++] = sign; - if (prefix && pos < bufLen) + if (prefix) buf[pos++] = prefix; - for (size_t i = 0; i < padLen && pos < bufLen; i++) + for (size_t i = 0; i < padLen; i++) buf[pos++] = '0'; } else { /* space padding, then sign, then prefix */ - for (size_t i = 0; i < padLen && pos < bufLen; i++) + for (size_t i = 0; i < padLen; i++) buf[pos++] = ' '; - if (sign && pos < bufLen) + if (sign) buf[pos++] = sign; - if (prefix && pos < bufLen) + if (prefix) buf[pos++] = prefix; } - if (bufLen > pos) - strcpy(buf + pos, valueBuf); + if (totalLen > pos) + memcpy(buf + pos, valueBuf, len); } buf[totalLen] = '\0'; diff --git a/src/asm/rgbasm.5 b/src/asm/rgbasm.5 index 10e92028..2371e62f 100644 --- a/src/asm/rgbasm.5 +++ b/src/asm/rgbasm.5 @@ -337,7 +337,7 @@ followed by one or more \[en] .Ql 9 . If specified, prints this many digits of a fixed-point fraction. -Defaults to 5 digits. +Defaults to 5 digits, maximum 255 digits. .It Ql Ta Specifies the type of value. .El .Pp diff --git a/test/asm/format-truncation.asm b/test/asm/format-truncation.asm new file mode 100644 index 00000000..e54ddd9e --- /dev/null +++ b/test/asm/format-truncation.asm @@ -0,0 +1,15 @@ +num equ 42 +fix equ 123.0 +str equs "hello" + +println "{#0260x:num}" +println "{#-260x:num}" +println "{0280.260f:fix}" +println "{260s:str}" +println "{-260s:str}" + +println "<{#0260x:num}>" +println "<{#-260x:num}>" +println "<{0280.260f:fix}>" +println "<{260s:str}>" +println "<{-260s:str}>" diff --git a/test/asm/format-truncation.err b/test/asm/format-truncation.err new file mode 100644 index 00000000..01ee8689 --- /dev/null +++ b/test/asm/format-truncation.err @@ -0,0 +1,35 @@ +ERROR: format-truncation.asm(5): + Formatted numeric value too long +ERROR: format-truncation.asm(6): + Formatted numeric value too long +ERROR: format-truncation.asm(7): + Fractional width 260 too long, limiting to 255 +ERROR: format-truncation.asm(7): + Formatted numeric value too long +ERROR: format-truncation.asm(8): + Formatted string value too long +ERROR: format-truncation.asm(9): + Formatted string value too long +ERROR: format-truncation.asm(11): + Formatted numeric value too long +warning: format-truncation.asm(11): [-Wlong-string] + String constant too long +ERROR: format-truncation.asm(12): + Formatted numeric value too long +warning: format-truncation.asm(12): [-Wlong-string] + String constant too long +ERROR: format-truncation.asm(13): + Fractional width 260 too long, limiting to 255 +ERROR: format-truncation.asm(13): + Formatted numeric value too long +warning: format-truncation.asm(13): [-Wlong-string] + String constant too long +ERROR: format-truncation.asm(14): + Formatted string value too long +warning: format-truncation.asm(14): [-Wlong-string] + String constant too long +ERROR: format-truncation.asm(15): + Formatted string value too long +warning: format-truncation.asm(15): [-Wlong-string] + String constant too long +error: Assembly aborted (12 errors)! diff --git a/test/asm/format-truncation.out b/test/asm/format-truncation.out new file mode 100644 index 00000000..635542f7 --- /dev/null +++ b/test/asm/format-truncation.out @@ -0,0 +1,10 @@ +$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a +$2a +123.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + hello +hello +<$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 +<$2a +<123.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +< hell +