Ensure arithmetic will never overflow
This commit is contained in:
parent
40048618ee
commit
a624ce5374
1 changed files with 85 additions and 5 deletions
90
ethermess.c
90
ethermess.c
|
@ -138,6 +138,84 @@ struct timespec ms_in_future(intmax_t ms) {
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
intmax_t saturating_add(intmax_t a, intmax_t b) {
|
||||||
|
if (a < 0 && b < 0) {
|
||||||
|
// a: [min, -1]
|
||||||
|
// b: [min, -1]
|
||||||
|
// min + min -> underflow
|
||||||
|
// min + -1 -> underflow
|
||||||
|
// -1 + -1 -> ok
|
||||||
|
// a + b < INTMAX_MIN || - a
|
||||||
|
// b < INTMAX_MIN - a
|
||||||
|
if (b < INTMAX_MIN - a) {
|
||||||
|
// Underflow
|
||||||
|
fprintf(stderr, "a+b underflow\n"); // debg
|
||||||
|
return INTMAX_MIN;
|
||||||
|
}
|
||||||
|
} else if (a < 0 && b >= 0) {
|
||||||
|
// a: [min, -1]
|
||||||
|
// b: [0, max]
|
||||||
|
// min + 0 -> ok
|
||||||
|
// min + max -> ok
|
||||||
|
// -1 + 0 -> ok
|
||||||
|
// -1 + max -> ok
|
||||||
|
} else if (a >= 0 && b < 0) {
|
||||||
|
// See above but swap a and b
|
||||||
|
} else if (a >= 0 && b >= 0) {
|
||||||
|
// a: [0, max]
|
||||||
|
// b: [0, max]
|
||||||
|
// 0 + 0 -> ok
|
||||||
|
// 0 + max -> ok
|
||||||
|
// max + max -> overflow
|
||||||
|
// a + b > INTMAX_MAX || -a
|
||||||
|
// b > INTMAX_MAX - a
|
||||||
|
if (b > INTMAX_MAX - a) {
|
||||||
|
// Overflow
|
||||||
|
fprintf(stderr, "a+b overflow\n"); // debg
|
||||||
|
return INTMAX_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
intmax_t saturating_sub(intmax_t a, intmax_t b) {
|
||||||
|
// a - b = a + (-b)
|
||||||
|
// Only case where -b is not safe is when b < -INTMAX_MAX (because
|
||||||
|
// INTMAX_MIN can be smaller than -INTMAX_MAX, but INTMAX_MAX can't be
|
||||||
|
// larger than -INTMAX_MIN)
|
||||||
|
if (b < -INTMAX_MAX) {
|
||||||
|
// a - b || + c - c
|
||||||
|
// a - b + c - c
|
||||||
|
// a - (b - c) - c
|
||||||
|
// a + (c - b) - c
|
||||||
|
fprintf(stderr, "-b overflow\n"); // debg
|
||||||
|
intmax_t c = saturating_sub(b, -INTMAX_MAX);
|
||||||
|
return saturating_sub(saturating_add(a, c - b), c);
|
||||||
|
} else {
|
||||||
|
return saturating_sub(a, -b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intmax_t saturating_mul(intmax_t a, intmax_t b) {
|
||||||
|
// Doesn't give 100% right results when one parameter is INTMAX_MIN,
|
||||||
|
// but at least it won't ever overflow
|
||||||
|
if (a < 0) {
|
||||||
|
return saturating_sub(0, saturating_mul(saturating_sub(0, a), b));
|
||||||
|
}
|
||||||
|
if (b < 0) {
|
||||||
|
return saturating_sub(0, saturating_mul(a, saturating_sub(0, b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (INTMAX_MAX / a < b) {
|
||||||
|
// Overflow
|
||||||
|
fprintf(stderr, "a*b overflow\n"); // debg
|
||||||
|
return INTMAX_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
return a * b;
|
||||||
|
}
|
||||||
|
|
||||||
int wait_ms_until(struct timespec then) {
|
int wait_ms_until(struct timespec then) {
|
||||||
// This function basically returns the difference in ms between the
|
// This function basically returns the difference in ms between the
|
||||||
// current time and the given time, clamped to [0, INT_MAX]
|
// current time and the given time, clamped to [0, INT_MAX]
|
||||||
|
@ -151,11 +229,13 @@ int wait_ms_until(struct timespec then) {
|
||||||
err(1, "clock_gettime");
|
err(1, "clock_gettime");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overflow not checked because fuck overflow checking in C
|
// Uses saturating arithmetic. I guess this might be wrong in some cases
|
||||||
// TODO: Check overflow
|
// but can't be bothered to deal with it any other way, especially as a
|
||||||
intmax_t sec_diff = then.tv_sec - now.tv_sec;
|
// clamping the values to even something like [0, 1] would result in
|
||||||
intmax_t ns_diff = then.tv_nsec - now.tv_nsec;
|
// mostly correct functioning
|
||||||
intmax_t ms = sec_diff * 1000 + ns_diff / 1000 / 1000;
|
intmax_t sec_diff = saturating_sub(then.tv_sec, now.tv_sec);
|
||||||
|
intmax_t ns_diff = saturating_sub(then.tv_nsec, now.tv_nsec);
|
||||||
|
intmax_t ms = saturating_add(saturating_mul(sec_diff, 1000), ns_diff / 1000 / 1000);
|
||||||
|
|
||||||
// Clamp
|
// Clamp
|
||||||
if (ms > INT_MAX) {
|
if (ms > INT_MAX) {
|
||||||
|
|
Loading…
Reference in a new issue