mirror of
https://github.com/zeek/zeek.git
synced 2025-10-08 17:48:21 +00:00
Avoid double-to-int conversion overflows in modp_dtoa functions
Those methods already had a fallback to use sprintf() for large values except: * The check-for-large-value was unnecessarily done after many operations that aren't relevant to the check and those operations can result in a conversion overflow (undefined behavior). * The check-for-large-value was using the literal value for a 32-bit INT_MAX instead of just using INT_MAX. For a platform where `int` is less than 32-bits, the same conversion overflow from the previous point could still occur (undefined behavior). * The check-for-large-value was not inclusive of INT_MAX. In a case where the conversion of INT_MAX itself to a double can't be represented exactly, it's implementation-defined whether the closest higher or closest lower representable-value is selected. If the higher value is selected, then a `double` value comparing equal to INT_MAX-as-converted-to-double would cause an overflow of an `int` upon conversion (undefined behavior).
This commit is contained in:
parent
264e6858f2
commit
d25ead8f8e
1 changed files with 73 additions and 77 deletions
|
@ -6,6 +6,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
// other interesting references on num to string convesion
|
// other interesting references on num to string convesion
|
||||||
// http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
|
// http://www.jb.man.ac.uk/~slowe/cpp/itoa.html
|
||||||
|
@ -88,8 +89,28 @@ void modp_dtoa(double value, char* str, int prec)
|
||||||
str[0] = 'n'; str[1] = 'a'; str[2] = 'n'; str[3] = '\0';
|
str[0] = 'n'; str[1] = 'a'; str[2] = 'n'; str[3] = '\0';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* we'll work in positive values and deal with the
|
||||||
|
negative sign issue later */
|
||||||
|
int neg = 0;
|
||||||
|
if (value < 0) {
|
||||||
|
neg = 1;
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
/* if input is larger than thres_max, revert to exponential */
|
/* if input is larger than thres_max, revert to exponential */
|
||||||
const double thres_max = (double)(0x7FFFFFFF);
|
const double thres_max = (double)(INT_MAX);
|
||||||
|
|
||||||
|
/* for very large numbers switch back to native sprintf for exponentials.
|
||||||
|
anyone want to write code to replace this? */
|
||||||
|
/*
|
||||||
|
normal printf behavior is to print EVERY whole number digit
|
||||||
|
which can be 100s of characters overflowing your buffers == bad
|
||||||
|
*/
|
||||||
|
if (value >= thres_max) {
|
||||||
|
sprintf(str, "%e", neg ? -value : value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
double diff = 0.0;
|
double diff = 0.0;
|
||||||
char* wstr = str;
|
char* wstr = str;
|
||||||
|
@ -101,16 +122,6 @@ void modp_dtoa(double value, char* str, int prec)
|
||||||
prec = 9;
|
prec = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* we'll work in positive values and deal with the
|
|
||||||
negative sign issue later */
|
|
||||||
int neg = 0;
|
|
||||||
if (value < 0) {
|
|
||||||
neg = 1;
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int whole = (int) value;
|
int whole = (int) value;
|
||||||
double tmp = (value - whole) * _pow10[prec];
|
double tmp = (value - whole) * _pow10[prec];
|
||||||
uint32_t frac = (uint32_t)(tmp);
|
uint32_t frac = (uint32_t)(tmp);
|
||||||
|
@ -129,17 +140,6 @@ void modp_dtoa(double value, char* str, int prec)
|
||||||
++frac;
|
++frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for very large numbers switch back to native sprintf for exponentials.
|
|
||||||
anyone want to write code to replace this? */
|
|
||||||
/*
|
|
||||||
normal printf behavior is to print EVERY whole number digit
|
|
||||||
which can be 100s of characters overflowing your buffers == bad
|
|
||||||
*/
|
|
||||||
if (value > thres_max) {
|
|
||||||
sprintf(str, "%e", neg ? -value : value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prec == 0) {
|
if (prec == 0) {
|
||||||
diff = value - whole;
|
diff = value - whole;
|
||||||
if (diff > 0.5) {
|
if (diff > 0.5) {
|
||||||
|
@ -189,8 +189,27 @@ void modp_dtoa2(double value, char* str, int prec)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* we'll work in positive values and deal with the
|
||||||
|
negative sign issue later */
|
||||||
|
int neg = 0;
|
||||||
|
if (value < 0) {
|
||||||
|
neg = 1;
|
||||||
|
value = -value;
|
||||||
|
}
|
||||||
|
|
||||||
/* if input is larger than thres_max, revert to exponential */
|
/* if input is larger than thres_max, revert to exponential */
|
||||||
const double thres_max = (double)(0x7FFFFFFF);
|
const double thres_max = (double)(INT_MAX);
|
||||||
|
|
||||||
|
/* for very large numbers switch back to native sprintf for exponentials.
|
||||||
|
anyone want to write code to replace this? */
|
||||||
|
/*
|
||||||
|
normal printf behavior is to print EVERY whole number digit
|
||||||
|
which can be 100s of characters overflowing your buffers == bad
|
||||||
|
*/
|
||||||
|
if (value >= thres_max) {
|
||||||
|
sprintf(str, "%e", neg ? -value : value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int count;
|
int count;
|
||||||
double diff = 0.0;
|
double diff = 0.0;
|
||||||
|
@ -203,16 +222,6 @@ void modp_dtoa2(double value, char* str, int prec)
|
||||||
prec = 9;
|
prec = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* we'll work in positive values and deal with the
|
|
||||||
negative sign issue later */
|
|
||||||
int neg = 0;
|
|
||||||
if (value < 0) {
|
|
||||||
neg = 1;
|
|
||||||
value = -value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int whole = (int) value;
|
int whole = (int) value;
|
||||||
double tmp = (value - whole) * _pow10[prec];
|
double tmp = (value - whole) * _pow10[prec];
|
||||||
uint32_t frac = (uint32_t)(tmp);
|
uint32_t frac = (uint32_t)(tmp);
|
||||||
|
@ -231,17 +240,6 @@ void modp_dtoa2(double value, char* str, int prec)
|
||||||
++frac;
|
++frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for very large numbers switch back to native sprintf for exponentials.
|
|
||||||
anyone want to write code to replace this? */
|
|
||||||
/*
|
|
||||||
normal printf behavior is to print EVERY whole number digit
|
|
||||||
which can be 100s of characters overflowing your buffers == bad
|
|
||||||
*/
|
|
||||||
if (value > thres_max) {
|
|
||||||
sprintf(str, "%e", neg ? -value : value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prec == 0) {
|
if (prec == 0) {
|
||||||
diff = value - whole;
|
diff = value - whole;
|
||||||
if (diff > 0.5) {
|
if (diff > 0.5) {
|
||||||
|
@ -301,21 +299,6 @@ void modp_dtoa3(double value, char* str, int n, int prec)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if input is larger than thres_max, revert to exponential */
|
|
||||||
const double thres_max = (double)(0x7FFFFFFF);
|
|
||||||
|
|
||||||
int count;
|
|
||||||
double diff = 0.0;
|
|
||||||
char* wstr = str;
|
|
||||||
|
|
||||||
if (prec < 0) {
|
|
||||||
prec = 0;
|
|
||||||
} else if (prec > 9) {
|
|
||||||
/* precision of >= 10 can lead to overflow errors */
|
|
||||||
prec = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* we'll work in positive values and deal with the
|
/* we'll work in positive values and deal with the
|
||||||
negative sign issue later */
|
negative sign issue later */
|
||||||
int neg = 0;
|
int neg = 0;
|
||||||
|
@ -324,32 +307,23 @@ void modp_dtoa3(double value, char* str, int n, int prec)
|
||||||
value = -value;
|
value = -value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prec < 0) {
|
||||||
int whole = (int) value;
|
prec = 0;
|
||||||
double tmp = (value - whole) * _pow10[prec];
|
} else if (prec > 9) {
|
||||||
uint32_t frac = (uint32_t)(tmp);
|
/* precision of >= 10 can lead to overflow errors */
|
||||||
diff = tmp - frac;
|
prec = 9;
|
||||||
|
|
||||||
if (diff > 0.5) {
|
|
||||||
++frac;
|
|
||||||
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
|
|
||||||
if (frac >= _pow10[prec]) {
|
|
||||||
frac = 0;
|
|
||||||
++whole;
|
|
||||||
}
|
|
||||||
} else if (diff == 0.5 && ((frac == 0) || (frac & 1))) {
|
|
||||||
/* if halfway, round up if odd, OR
|
|
||||||
if last digit is 0. That last part is strange */
|
|
||||||
++frac;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if input is larger than thres_max, revert to exponential */
|
||||||
|
const double thres_max = (double)(INT_MAX);
|
||||||
|
|
||||||
/* for very large numbers switch back to native sprintf for exponentials.
|
/* for very large numbers switch back to native sprintf for exponentials.
|
||||||
anyone want to write code to replace this? */
|
anyone want to write code to replace this? */
|
||||||
/*
|
/*
|
||||||
normal printf behavior is to print EVERY whole number digit
|
normal printf behavior is to print EVERY whole number digit
|
||||||
which can be 100s of characters overflowing your buffers == bad
|
which can be 100s of characters overflowing your buffers == bad
|
||||||
*/
|
*/
|
||||||
if (value > thres_max) {
|
if (value >= thres_max) {
|
||||||
/* ---- Modified part, compared to modp_dtoa3. */
|
/* ---- Modified part, compared to modp_dtoa3. */
|
||||||
int i = snprintf(str, n, "%.*f", prec, neg ? -value : value);
|
int i = snprintf(str, n, "%.*f", prec, neg ? -value : value);
|
||||||
|
|
||||||
|
@ -373,6 +347,28 @@ void modp_dtoa3(double value, char* str, int n, int prec)
|
||||||
/* ---- End of modified part.. */
|
/* ---- End of modified part.. */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int count;
|
||||||
|
double diff = 0.0;
|
||||||
|
char* wstr = str;
|
||||||
|
|
||||||
|
int whole = (int) value;
|
||||||
|
double tmp = (value - whole) * _pow10[prec];
|
||||||
|
uint32_t frac = (uint32_t)(tmp);
|
||||||
|
diff = tmp - frac;
|
||||||
|
|
||||||
|
if (diff > 0.5) {
|
||||||
|
++frac;
|
||||||
|
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
|
||||||
|
if (frac >= _pow10[prec]) {
|
||||||
|
frac = 0;
|
||||||
|
++whole;
|
||||||
|
}
|
||||||
|
} else if (diff == 0.5 && ((frac == 0) || (frac & 1))) {
|
||||||
|
/* if halfway, round up if odd, OR
|
||||||
|
if last digit is 0. That last part is strange */
|
||||||
|
++frac;
|
||||||
|
}
|
||||||
|
|
||||||
if (prec == 0) {
|
if (prec == 0) {
|
||||||
diff = value - whole;
|
diff = value - whole;
|
||||||
if (diff > 0.5) {
|
if (diff > 0.5) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue