MEDIUM: sample: switch to saturated arithmetic
This patch check calculus for overflow and returns capped values.
This permits to protect against integer overflow in certain operations
involving ratios, percentages, limits or anything. That can sometimes
be critically important with some operations (eg: content-length < X).
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 2782c20..1ae9834 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -11290,8 +11290,8 @@
mul(<value>)
Multiplies the input value of type signed integer by <value>, and returns
- the product as an signed integer. In case of overflow, the higher bits are
- lost, leading to seemingly strange values.
+ the product as an signed integer. In case of overflow, the largest possible
+ value for the sign is returned so that the operation doesn't wrap around.
neg
Takes the input value of type signed integer, computes the opposite value,
diff --git a/src/sample.c b/src/sample.c
index 15c004f..41e7540 100644
--- a/src/sample.c
+++ b/src/sample.c
@@ -2053,12 +2053,38 @@
return 1;
}
+static inline long long int arith_add(long long int a, long long int b)
+{
+ /* Prevent overflow and makes capped calculus.
+ * We must ensure that the check calculus doesn't
+ * exceed the signed 64 bits limits.
+ *
+ * +----------+----------+
+ * | a<0 | a>=0 |
+ * +------+----------+----------+
+ * | b<0 | MIN-a>b | no check |
+ * +------+----------+----------+
+ * | b>=0 | no check | MAX-a<b |
+ * +------+----------+----------+
+ */
+ if ((a ^ b) >= 0) {
+ /* signs are differents. */
+ if (a < 0) {
+ if (LLONG_MIN - a > b)
+ return LLONG_MIN;
+ }
+ if (LLONG_MAX - a < b)
+ return LLONG_MAX;
+ }
+ return a + b;
+}
+
/* Takes a SINT on input, applies an arithmetic "add" with the UINT in arg_p,
* and returns the SINT result.
*/
static int sample_conv_arith_add(const struct arg *arg_p, struct sample *smp, void *private)
{
- smp->data.sint += arg_p->data.sint;
+ smp->data.sint = arith_add(smp->data.sint, arg_p->data.sint);
return 1;
}
@@ -2068,7 +2094,21 @@
static int sample_conv_arith_sub(const struct arg *arg_p,
struct sample *smp, void *private)
{
- smp->data.sint -= arg_p->data.sint;
+ /* We cannot represent -LLONG_MIN because abs(LLONG_MIN) is greater
+ * than abs(LLONG_MAX). So, the following code use LLONG_MAX in place
+ * of -LLONG_MIN and correct the result.
+ */
+ if (arg_p->data.sint == LLONG_MIN) {
+ smp->data.sint = arith_add(smp->data.sint, LLONG_MAX);
+ if (smp->data.sint < LLONG_MAX)
+ smp->data.sint++;
+ return 1;
+ }
+
+ /* standard substraction: we use the "add" function and negate
+ * the second operand.
+ */
+ smp->data.sint = arith_add(smp->data.sint, -arg_p->data.sint);
return 1;
}
@@ -2078,7 +2118,35 @@
static int sample_conv_arith_mul(const struct arg *arg_p,
struct sample *smp, void *private)
{
- smp->data.sint *= arg_p->data.sint;
+ long long int c;
+
+ /* prevent divide by 0 during the check */
+ if (!smp->data.sint || !arg_p->data.sint) {
+ smp->data.sint = 0;
+ return 1;
+ }
+
+ /* The multiply between LLONG_MIN and -1 returns a
+ * "floting point exception".
+ */
+ if (smp->data.sint == LLONG_MIN && arg_p->data.sint == -1) {
+ smp->data.sint = LLONG_MAX;
+ return 1;
+ }
+
+ /* execute standard multiplication. */
+ c = smp->data.sint * arg_p->data.sint;
+
+ /* check for overflow and makes capped multiply. */
+ if (smp->data.sint != c / arg_p->data.sint) {
+ if ((smp->data.sint < 0) == (arg_p->data.sint < 0)) {
+ smp->data.sint = LLONG_MAX;
+ return 1;
+ }
+ smp->data.sint = LLONG_MIN;
+ return 1;
+ }
+ smp->data.sint = c;
return 1;
}
@@ -2089,10 +2157,18 @@
static int sample_conv_arith_div(const struct arg *arg_p,
struct sample *smp, void *private)
{
- if (arg_p->data.sint)
+ if (arg_p->data.sint) {
+ /* The divide between LLONG_MIN and -1 returns a
+ * "floting point exception".
+ */
+ if (smp->data.sint == LLONG_MIN && arg_p->data.sint == -1) {
+ smp->data.sint = LLONG_MAX;
+ return 1;
+ }
smp->data.sint /= arg_p->data.sint;
- else
- smp->data.sint = ~0;
+ return 1;
+ }
+ smp->data.sint = LLONG_MAX;
return 1;
}
@@ -2103,10 +2179,18 @@
static int sample_conv_arith_mod(const struct arg *arg_p,
struct sample *smp, void *private)
{
- if (arg_p->data.sint)
+ if (arg_p->data.sint) {
+ /* The divide between LLONG_MIN and -1 returns a
+ * "floting point exception".
+ */
+ if (smp->data.sint == LLONG_MIN && arg_p->data.sint == -1) {
+ smp->data.sint = 0;
+ return 1;
+ }
smp->data.sint %= arg_p->data.sint;
- else
- smp->data.sint = 0;
+ return 1;
+ }
+ smp->data.sint = 0;
return 1;
}
@@ -2116,7 +2200,10 @@
static int sample_conv_arith_neg(const struct arg *arg_p,
struct sample *smp, void *private)
{
- smp->data.sint = -smp->data.sint;
+ if (smp->data.sint == LLONG_MIN)
+ smp->data.sint = LLONG_MAX;
+ else
+ smp->data.sint = -smp->data.sint;
return 1;
}