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;
|
||||
}
|
||||
|
||||
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) {
|
||||
// This function basically returns the difference in ms between the
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Overflow not checked because fuck overflow checking in C
|
||||
// TODO: Check overflow
|
||||
intmax_t sec_diff = then.tv_sec - now.tv_sec;
|
||||
intmax_t ns_diff = then.tv_nsec - now.tv_nsec;
|
||||
intmax_t ms = sec_diff * 1000 + ns_diff / 1000 / 1000;
|
||||
// Uses saturating arithmetic. I guess this might be wrong in some cases
|
||||
// but can't be bothered to deal with it any other way, especially as a
|
||||
// clamping the values to even something like [0, 1] would result in
|
||||
// mostly correct functioning
|
||||
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
|
||||
if (ms > INT_MAX) {
|
||||
|
|
Loading…
Reference in a new issue