ref: 992be3fd9bd5fd62d2955bb494e14ce5e47c73d4
parent: c755fa3469af9c4179b36f7b06ef54a95b8eafeb
author: Rangi <remy.oukaour+rangi42@gmail.com>
date: Fri Apr 16 18:00:17 EDT 2021
Fix interpolation/STRFMT overflow issues Widths and fractional widths greater than 255 would overflow a uint8_t and wrap around to smaller values. Total formatted lengths greater than the avilable buffer size would overflow it and potentially corrupt memory. Fixes #830 Closes #831
--- a/include/asm/format.h
+++ b/include/asm/format.h
@@ -28,9 +28,9 @@
bool prefix;
bool alignLeft;
bool padZero;
- uint8_t width;
+ size_t width;
bool hasFrac;
- uint8_t fracWidth;
+ size_t fracWidth;
int type;
bool valid;
};
--- a/src/asm/format.c
+++ b/src/asm/format.c
@@ -147,14 +147,18 @@
size_t len = strlen(value);
size_t totalLen = fmt->width > len ? fmt->width : len;
+ size_t padLen = fmt->width > len ? fmt->width - len : 0;
- if (totalLen + 1 > bufLen) /* bufLen includes terminator */
+ if (totalLen + 1 > bufLen) { /* bufLen includes terminator */
error("Formatted string value too long\n");
+ totalLen = bufLen - 1;
+ if (len > totalLen)
+ len = totalLen;
+ padLen = totalLen - len;
+ }
- size_t padLen = fmt->width > len ? fmt->width - len : 0;
-
if (fmt->alignLeft) {
- strncpy(buf, value, len < bufLen ? len : bufLen);
+ memcpy(buf, value, len < bufLen ? len : bufLen);
for (size_t i = 0; i < totalLen && len + i < bufLen; i++)
buf[len + i] = ' ';
} else {
@@ -161,7 +165,7 @@
for (size_t i = 0; i < padLen && i < bufLen; i++)
buf[i] = ' ';
if (bufLen > padLen)
- strncpy(buf + padLen, value, bufLen - padLen - 1);
+ memcpy(buf + padLen, value, bufLen - padLen - 1);
}
buf[totalLen] = '\0';
@@ -221,12 +225,18 @@
/* 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 {
@@ -252,11 +262,17 @@
numLen++;
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
+ size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
- if (totalLen + 1 > bufLen) /* bufLen includes terminator */
+ if (totalLen + 1 > bufLen) { /* bufLen includes terminator */
error("Formatted numeric value too long\n");
-
- size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
+ totalLen = bufLen - 1;
+ if (numLen > totalLen) {
+ len = totalLen - (numLen - len);
+ numLen = totalLen;
+ }
+ padLen = totalLen - numLen;
+ }
if (fmt->alignLeft) {
size_t pos = 0;
--- a/src/asm/rgbasm.5
+++ b/src/asm/rgbasm.5
@@ -337,7 +337,7 @@
\[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 <type> Ta Specifies the type of value.
.El
.Pp
--- /dev/null
+++ b/test/asm/format-truncation.asm
@@ -1,0 +1,15 @@
+num equ 42
+fix equ 123.456
+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}>"
--- /dev/null
+++ b/test/asm/format-truncation.err
@@ -1,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)!
--- /dev/null
+++ b/test/asm/format-truncation.out
@@ -1,0 +1,10 @@
+$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a
+$2a
+123.45599365234375001369732334415661667551799560000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+ hello
+hello
+<$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002
+<$2a
+<123.4559936523437500136973233441566166755179956000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+< hell
+<hello
--- /dev/null
+++ b/test/asm/interpolation-overflow.asm
@@ -1,0 +1,4 @@
+; It seems that \1 was the easiest way to notice the memory corruption that
+; resulted from this overflow
+x = 0
+{.99999999f:x}\1
--- /dev/null
+++ b/test/asm/interpolation-overflow.err
@@ -1,0 +1,9 @@
+ERROR: interpolation-overflow.asm(4):
+ Fractional width 99999999 too long, limiting to 255
+ERROR: interpolation-overflow.asm(4):
+ Formatted numeric value too long
+warning: interpolation-overflow.asm(4): [-Wlarge-constant]
+ Precision of fixed-point constant is too large
+while expanding symbol "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+FATAL: interpolation-overflow.asm(4):
+ Macro argument '\1' not defined