authorgravatar for 3405586+schmee@users.noreply.github.comJohn Schmidt <3405586+schmee@users.noreply.github.com> 2022-01-29 12:25:25+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-29 12:25:25+01:00
logadea9a1765aea53f1033eadb6f10f5be139e6c62
tree28edcb2371462469a13f3d584bc2c2a2e50e8022
parente51a44b3422a1fad0ec75078f3576f0fa447d986
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

std.fmt: fix out-of-bounds array write in float printing

This commit fixes an out of bounds write that can occur when formatting certain float values. The write messes up the stack and causes incorrect results, segfaults, or nothing at all, depending on the optimization mode used. The `errol` function writes the digits of the float into `buffer` starting from index 1, leaving index 0 untouched, and returns `buffer[1..]` and the exponent. This is because `roundToPrecision` relies on index 0 being unused in case the rounding adds a digit (e.g rounding 999.99 to 1000.00). When this happens, pointer arithmetic is used [here](https://github.com/ziglang/zig/blob/0e6d2184cacf2dd1fad7508b2f9ae99d78763148/lib/std/fmt/errol.zig#L61-L65) to access index 0 and put the ones digit in the right place. However, `errol3u` contains two special cases: `errolInt` and `errolFixed`, which return from the function early. For these two special cases index 0 was never reserved, and the return value contains `buffer` instead of `buffer[1..]`. This causes the pointer arithmetic in `roundToPrecision` to write out of bounds, which in the case of `std.fmt.formatFloatDecimal` messes up the stack and causes undefined behavior. The fix is to move the slicing of `buffer` to `buffer[1..]` from `errol3u` to `errol` so that both the default and the special cases operate on the sliced buffer.

2 files changed, 7 insertions(+), 7 deletions(-)

lib/std/fmt.zig+1
...@@ -2297,6 +2297,7 @@ test "float.decimal" {...@@ -2297,6 +2297,7 @@ test "float.decimal" {
2297 try expectFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)});2297 try expectFmt("f64: 0.00030000", "f64: {d:.8}", .{@as(f64, 0.0003)});
2298 try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)});2298 try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 1.40130e-45)});
2299 try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)});2299 try expectFmt("f64: 0.00000", "f64: {d:.5}", .{@as(f64, 9.999960e-40)});
2300 try expectFmt("f64: 10000000000000.00", "f64: {d:.2}", .{@as(f64, 9999999999999.999)});
2300}2301}
23012302
2302test "float.libc.sanity" {2303test "float.libc.sanity" {
lib/std/fmt/errol.zig+6-7
...@@ -92,7 +92,10 @@ pub fn errol3(value: f64, buffer: []u8) FloatDecimal {...@@ -92,7 +92,10 @@ pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
92 };92 };
93 }93 }
9494
95 return errol3u(value, buffer);95 // We generate digits starting at index 1. If rounding a buffer later then it may be
96 // required to generate a preceding digit in some cases (9.999) in which case we use
97 // the 0-index for this extra digit.
98 return errol3u(value, buffer[1..]);
96}99}
97100
98/// Uncorrected Errol3 double to ASCII conversion.101/// Uncorrected Errol3 double to ASCII conversion.
...@@ -162,11 +165,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -162,11 +165,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
162 }165 }
163166
164 // digit generation167 // digit generation
165168 var buf_index: usize = 0;
166 // We generate digits starting at index 1. If rounding a buffer later then it may be
167 // required to generate a preceding digit in some cases (9.999) in which case we use
168 // the 0-index for this extra digit.
169 var buf_index: usize = 1;
170 while (true) {169 while (true) {
171 var hdig = @floatToInt(u8, math.floor(high.val));170 var hdig = @floatToInt(u8, math.floor(high.val));
172 if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1;171 if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1;
...@@ -192,7 +191,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -192,7 +191,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
192 buf_index += 1;191 buf_index += 1;
193192
194 return FloatDecimal{193 return FloatDecimal{
195 .digits = buffer[1..buf_index],194 .digits = buffer[0..buf_index],
196 .exp = exp,195 .exp = exp,
197 };196 };
198}197}