1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-10-26 00:37:43 +03:00

Rework XPath rounding functions

Use the C library's floor and ceil functions. The old code was overly
complicated for no apparent reason and could result in undefined
behavior when handling NaNs (found with afl-fuzz and UBSan).

Fix wrong comment in xmlXPathRoundFunction. The implementation was
already following the spec and rounding half up.
This commit is contained in:
Nick Wellnhofer
2016-04-21 13:41:09 +02:00
parent 43f50f4dfc
commit 4bebb030db
3 changed files with 100 additions and 56 deletions

View File

@@ -43,6 +43,26 @@ Object is a number : 5
Expression: floor(-5.2) Expression: floor(-5.2)
Object is a number : -6 Object is a number : -6
========================
Expression: floor(100000000000000000000)
Object is a number : 1e+20
========================
Expression: floor(-100000000000000000000)
Object is a number : -1e+20
========================
Expression: floor(0 div 0)
Object is a number : NaN
========================
Expression: floor(1 div 0)
Object is a number : Infinity
========================
Expression: floor(-1 div 0)
Object is a number : -Infinity
======================== ========================
Expression: ceiling(0.1) Expression: ceiling(0.1)
Object is a number : 1 Object is a number : 1
@@ -67,6 +87,26 @@ Object is a number : 6
Expression: ceiling(-5.2) Expression: ceiling(-5.2)
Object is a number : -5 Object is a number : -5
========================
Expression: ceiling(100000000000000000000)
Object is a number : 1e+20
========================
Expression: ceiling(-100000000000000000000)
Object is a number : -1e+20
========================
Expression: ceiling(0 div 0)
Object is a number : NaN
========================
Expression: ceiling(1 div 0)
Object is a number : Infinity
========================
Expression: ceiling(-1 div 0)
Object is a number : -Infinity
======================== ========================
Expression: round(0.1) Expression: round(0.1)
Object is a number : 0 Object is a number : 0
@@ -98,3 +138,31 @@ Object is a number : -5
======================== ========================
Expression: round(-5.6) Expression: round(-5.6)
Object is a number : -6 Object is a number : -6
========================
Expression: round(0.5)
Object is a number : 1
========================
Expression: round(-0.5)
Object is a number : 0
========================
Expression: round(100000000000000000000)
Object is a number : 1e+20
========================
Expression: round(-100000000000000000000)
Object is a number : -1e+20
========================
Expression: round(0 div 0)
Object is a number : NaN
========================
Expression: round(1 div 0)
Object is a number : Infinity
========================
Expression: round(-1 div 0)
Object is a number : -Infinity

View File

@@ -9,12 +9,22 @@ floor(-0)
floor(0) floor(0)
floor(5.2) floor(5.2)
floor(-5.2) floor(-5.2)
floor(100000000000000000000)
floor(-100000000000000000000)
floor(0 div 0)
floor(1 div 0)
floor(-1 div 0)
ceiling(0.1) ceiling(0.1)
ceiling(-0.1) ceiling(-0.1)
ceiling(-0) ceiling(-0)
ceiling(0) ceiling(0)
ceiling(5.2) ceiling(5.2)
ceiling(-5.2) ceiling(-5.2)
ceiling(100000000000000000000)
ceiling(-100000000000000000000)
ceiling(0 div 0)
ceiling(1 div 0)
ceiling(-1 div 0)
round(0.1) round(0.1)
round(5.2) round(5.2)
round(5.5) round(5.5)
@@ -23,3 +33,10 @@ round(-0.1)
round(-5.2) round(-5.2)
round(-5.5) round(-5.5)
round(-5.6) round(-5.6)
round(0.5)
round(-0.5)
round(100000000000000000000)
round(-100000000000000000000)
round(0 div 0)
round(1 div 0)
round(-1 div 0)

69
xpath.c
View File

@@ -9675,18 +9675,6 @@ xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlXPathReleaseObject(ctxt->context, cur); xmlXPathReleaseObject(ctxt->context, cur);
} }
/*
* To assure working code on multiple platforms, we want to only depend
* upon the characteristic truncation of converting a floating point value
* to an integer. Unfortunately, because of the different storage sizes
* of our internal floating point value (double) and integer (int), we
* can't directly convert (see bug 301162). This macro is a messy
* 'workaround'
*/
#define XTRUNC(f, v) \
f = fmod((v), INT_MAX); \
f = (v) - (f) + (double)((int)(f));
/** /**
* xmlXPathFloorFunction: * xmlXPathFloorFunction:
* @ctxt: the XPath Parser context * @ctxt: the XPath Parser context
@@ -9699,19 +9687,11 @@ xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
*/ */
void void
xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
double f;
CHECK_ARITY(1); CHECK_ARITY(1);
CAST_TO_NUMBER; CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER); CHECK_TYPE(XPATH_NUMBER);
XTRUNC(f, ctxt->value->floatval); ctxt->value->floatval = floor(ctxt->value->floatval);
if (f != ctxt->value->floatval) {
if (ctxt->value->floatval > 0)
ctxt->value->floatval = f;
else
ctxt->value->floatval = f - 1;
}
} }
/** /**
@@ -9726,28 +9706,11 @@ xmlXPathFloorFunction(xmlXPathParserContextPtr ctxt, int nargs) {
*/ */
void void
xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
double f;
CHECK_ARITY(1); CHECK_ARITY(1);
CAST_TO_NUMBER; CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER); CHECK_TYPE(XPATH_NUMBER);
#if 0
ctxt->value->floatval = ceil(ctxt->value->floatval); ctxt->value->floatval = ceil(ctxt->value->floatval);
#else
XTRUNC(f, ctxt->value->floatval);
if (f != ctxt->value->floatval) {
if (ctxt->value->floatval > 0)
ctxt->value->floatval = f + 1;
else {
if (ctxt->value->floatval < 0 && f == 0)
ctxt->value->floatval = xmlXPathNZERO;
else
ctxt->value->floatval = f;
}
}
#endif
} }
/** /**
@@ -9759,7 +9722,7 @@ xmlXPathCeilingFunction(xmlXPathParserContextPtr ctxt, int nargs) {
* number round(number) * number round(number)
* The round function returns the number that is closest to the * The round function returns the number that is closest to the
* argument and that is an integer. If there are two such numbers, * argument and that is an integer. If there are two such numbers,
* then the one that is even is returned. * then the one that is closest to positive infinity is returned.
*/ */
void void
xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) { xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
@@ -9769,25 +9732,21 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CAST_TO_NUMBER; CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER); CHECK_TYPE(XPATH_NUMBER);
if ((xmlXPathIsNaN(ctxt->value->floatval)) || f = ctxt->value->floatval;
(xmlXPathIsInf(ctxt->value->floatval) == 1) ||
(xmlXPathIsInf(ctxt->value->floatval) == -1) || /* Test for zero to keep negative zero unchanged. */
(ctxt->value->floatval == 0.0)) if ((xmlXPathIsNaN(f)) || (f == 0.0))
return; return;
XTRUNC(f, ctxt->value->floatval); if ((f >= -0.5) && (f < 0.0)) {
if (ctxt->value->floatval < 0) { /* Negative zero. */
if (ctxt->value->floatval < f - 0.5)
ctxt->value->floatval = f - 1;
else
ctxt->value->floatval = f;
if (ctxt->value->floatval == 0)
ctxt->value->floatval = xmlXPathNZERO; ctxt->value->floatval = xmlXPathNZERO;
} else { }
if (ctxt->value->floatval < f + 0.5) else {
ctxt->value->floatval = f; double rounded = floor(f);
else if (f - rounded >= 0.5)
ctxt->value->floatval = f + 1; rounded += 1.0;
ctxt->value->floatval = rounded;
} }
} }