Skip to content

Commit 9e22610

Browse files
Improve our detection of floating-point that are integers
Turns out that we were relying on undefined behaviour when doing (uint64_t)fabs(val) == fabs(val) This problem was noted when running our test data on an AArch64 build. Signed-off-by: Thiago Macieira <[email protected]>
1 parent 412e40f commit 9e22610

File tree

1 file changed

+32
-2
lines changed

1 file changed

+32
-2
lines changed

src/cborpretty.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,36 @@
149149
* \value CborPrettyDefaultFlags Default conversion flags.
150150
*/
151151

152+
static inline bool convertToUint64(double v, uint64_t *absolute)
153+
{
154+
v = fabs(v);
155+
156+
/* C11 standard section 6.3.1.4 "Real floating and integer" says:
157+
*
158+
* 1 When a finite value of real floating type is converted to an integer
159+
* type other than _Bool, the fractional part is discarded (i.e., the
160+
* value is truncated toward zero). If the value of the integral part
161+
* cannot be represented by the integer type, the behavior is undefined.
162+
*
163+
* So we must perform a range check that v <= UINT64_MAX, but we can't use
164+
* UINT64_MAX + 1.0 because the standard continues:
165+
*
166+
* 2 When a value of integer type is converted to a real floating type, if
167+
* the value being converted can be represented exactly in the new type,
168+
* it is unchanged. If the value being converted is in the range of
169+
* values that can be represented but cannot be represented exactly, the
170+
* result is either the nearest higher or nearest lower representable
171+
* value, chosen in an implementation-defined manner.
172+
*/
173+
double supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */
174+
if (v >= supremum)
175+
return false;
176+
177+
/* Now we can convert, these two conversions cannot be UB */
178+
*absolute = v;
179+
return *absolute == v;
180+
}
181+
152182
static void printRecursionLimit(CborStreamFunction stream, void *out)
153183
{
154184
stream(out, "<nesting too deep, recursion stopped>");
@@ -489,8 +519,8 @@ static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue
489519
suffix = "";
490520
}
491521

492-
uint64_t ival = (uint64_t)fabs(val);
493-
if (ival == fabs(val)) {
522+
uint64_t ival;
523+
if (convertToUint64(val, &ival)) {
494524
/* this double value fits in a 64-bit integer, so show it as such
495525
* (followed by a floating point suffix, to disambiguate) */
496526
err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix);

0 commit comments

Comments
 (0)