<xlocnum>: Fix num_put::do_put() to avoid frexp()'s unspecified behavior for inf/nan#6168
Open
StephanTLavavej wants to merge 3 commits intomicrosoft:mainfrom
Open
<xlocnum>: Fix num_put::do_put() to avoid frexp()'s unspecified behavior for inf/nan#6168StephanTLavavej wants to merge 3 commits intomicrosoft:mainfrom
<xlocnum>: Fix num_put::do_put() to avoid frexp()'s unspecified behavior for inf/nan#6168StephanTLavavej wants to merge 3 commits intomicrosoft:mainfrom
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
This bug was reported to me by @Codiferous, reversing the natural order of things. (I'm supposed to report compiler bugs to him; he's not supposed to report library bugs to me! 😹) He found this while doing brain surgery to replace the UCRT's math functions with LLVM libc's math functions.
CRT
frexpbehaviorC23 WG14-N3220 7.12.6.7 "The
frexpfunctions"/3:Microsoft UCRT
When
frexp()is called with inf/nan, our behavior is that we always set the integer exponent to-1. (This is internalminkernel/crts/ucrt/src/appcrt/tran/frexp.cassigningINT_NANwhich is defined inminkernel/crts/ucrt/inc/trans.h; we ship most UCRT sources but I don't believe we ship these ones, sorry.)LLVM libc
Their behavior is that they don't assign anything to the integer exponent by default, although they do support a mode to control this behavior:
libc/src/__support/FPUtil/ManipulationFunctions.hSTL
Bug
The problem was that
num_put::do_put()was callingfrexp()while calculating how much to increase its internal buffer size for fixed-precision formatting (which might need lots of characters for large positive or negative exponents). Unfortunately, this wasn't guarded for finite values only, so we were allowing inf/nan to go through this codepath when they didn't need it. This went unnoticed for decades because the Microsoft UCRT (and DevDiv's CRT before that, presumably) set the integer exponent to-1, which we then took theabs()of, with a final result of adding exactly0to_Bufsize.However, because we were garbage-initing
int _Ptwo;, if we replacefrexp()'s implementation with LLVM libc's, we end up using that garbage integer while calculating how much to adjust_Bufsize, which is catastrophic.Fix
From previous fixes (#4212), we're already determining whether the value is finite (i.e. neither inf nor nan). Move that up, so we'll call
frexp()to perform the buffer size adjustment for finite values only. inf/nan don't need this because immediately afterwards, we add 50 as a "fudge factor"; this isn't pretty but it is absolutely enough for our possible inf/nan outputs:STL/stl/inc/xlocnum
Line 1395 in 2626cf1
Additionally, I have a commit to avoid garbage-init for
frexp()'s integer exponent. While this isn't needed for correctness (the previous commit is the correctness fix), zero is a strict subset of garbage, and this may help avoid analysis warnings in the future. Note that this zero-init isn't sufficient for correctness either, since there's nothing stoppingfrexp()'s unspecified behavior from being "assign a billion".Finally, #4212 introduced comprehensive test coverage for #4210, which is where @Codiferous actually found this bug. It was almost perfect to leave as-is, but I noticed that we weren't expecting specific strings. They're implementation-defined (C23 WG14-N3220 7.23.6.1 "The
fprintffunction"/8), so expecting these exact outputs will improve the strictness of the test.