Repository navigation
Fix to_chars writing past the end of the range for negative zero - #317
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #317 +/- ##
===========================================
+ Coverage 96.19% 96.21% +0.01%
===========================================
Files 79 79
Lines 9284 9303 +19
===========================================
+ Hits 8931 8951 +20
+ Misses 353 352 -1
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
@jk-jeon this may impact your upstream copy of Dragonbox |
For a negative zero, the dragonbox path wrote the '-' and then printed "0" without checking the room left, or checked the room for "0e+00" against the size of the whole range. "-0" was written into a range of one character and "-0e+00" into a range of five, one character past the end, with ptr past last and no error. Check the room that is left after the sign.
14cf20f to
1b94d90
Compare
mborland
left a comment
There was a problem hiding this comment.
Thanks! I rebased this on develop to fix a merge conflict with one of your other PRs, so will merge once it cycles green
|
@mborland Thanks for letting me know! Fortunately(?) it doesn't affect the upstream because the upstream doesn't care about the buffer size (it assumes the size is enough and otherwise it's UB). |
Another write past the end of the range, in the same family as #314, #315 and #316, this time for floating point: the zero case in the dragonbox path.
For a negative zero,
to_charswrites the'-'and then the"0"or"0e+00"without checking that the rest still fits. If the range is exactly one character too short, it writes one character past the end and reports success:ec == std::errc()andptr == last + 1.std::to_charsreturnsvalue_too_largeand leavesptr == last.Problem
In
to_chars_n_implindragonbox.hpp, the sign is written first andbuffermoves past it. The zero branch then:"0"with no check at all;buffer_size, the size of the whole range taken before the sign was written.So
"-0"is written into a range of one character and"-0e+00"into a range of five. Positive zero is not affected, because the empty range is rejected before this point. The same code is used forfloatanddouble.As in #316, the sign is written and the rest is compared with a size that still counts the character the sign used. As in #315, a special case for zero returns before the usual room check.
Reproducer
and in scientific format:
Fix
Check the room that is left after the sign: return
value_too_largebefore writing"0"if the range is now empty, and comparelast - bufferrather thanbuffer_sizewith the 5 characters of"0e+00".Tests
test/to_chars_float.cpp:negative_zero_range_tests<T>()writes-0.0into a range one character too short and into a range of exactly the right size, forfloatanddouble, in general ("-0") and scientific ("-0e+00") format. It checks forvalue_too_large,ptr == lastand an untouched character after the range in the first case, and for the exact output and an untouched character after it in the second, as the tests in #314, #315 and #316 do. Without the source change, the new tests report 12 failures.Tested with GCC 15 and Clang 22 with
-fsanitize=address,undefined: the fullto_chars/from_charstest set passes in C++17, andto_chars_floatalso passes in C++11.test/to_chars_float.cppbuilds without warnings with the flags fromtest/Jamfile.Not in this PR
-0.0in fixed format without a precision also returnsvalue_too_largefor any range of 7 characters or fewer, although"-0"needs 2. That comes from the shortest fixed format path rejecting ranges shorter than aboutmax_digits10characters for any value, which is a separate issue and not a memory safety bug, so I have left it for its own PR.