Potential sum and subtraction overflows were reported by Coverity in a few places where we use size_t and ssize_t types. Strictly speaking, those are not false positives even if I don't see a way to trigger those: for instance, if we receive up to n bytes from a socket up, the return value from recv() is already constrained and can't overflow for the usage we make in tap_handler_passt(). In any case, take care of those by adding two functions that explicitly check for overflows in sums and subtractions of long signed values, and using them. Signed-off-by: Stefano Brivio <sbrivio(a)redhat.com> --- lineread.c | 5 +++-- tap.c | 21 +++++++++++++++------ util.c | 5 +++++ util.h | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/lineread.c b/lineread.c index 0387f4a..f72a14e 100644 --- a/lineread.c +++ b/lineread.c @@ -17,6 +17,7 @@ #include <string.h> #include <stdbool.h> #include <unistd.h> +#include <errno.h> #include "lineread.h" #include "util.h" @@ -102,8 +103,8 @@ ssize_t lineread_get(struct lineread *lr, char **line) if (rc == 0) eof = true; - else - lr->count += rc; + else if (sadd_overflow(lr->count, rc, &lr->count)) + return -ERANGE; } *line = lr->buf + lr->next_line; diff --git a/tap.c b/tap.c index ec994a2..e5c1693 100644 --- a/tap.c +++ b/tap.c @@ -1031,7 +1031,11 @@ redo: */ if (l2len > n) { rem = recv(c->fd_tap, p + n, l2len - n, 0); - if ((n += rem) != l2len) + + if (sadd_overflow(n, rem, &n)) + return; + + if (n != l2len) return; } @@ -1046,7 +1050,9 @@ redo: next: p += l2len; - n -= l2len; + + if (ssub_overflow(n, l2len, &n)) + return; } tap_handler(c, now); @@ -1077,17 +1083,20 @@ redo: tap_flush_pools(); restart: while ((len = read(c->fd_tap, pkt_buf + n, TAP_BUF_BYTES - n)) > 0) { - if (len < (ssize_t)sizeof(struct ethhdr) || len > (ssize_t)ETH_MAX_MTU) { - n += len; + if (sadd_overflow(n, len, &n)) + return; + continue; } - tap_add_packet(c, len, pkt_buf + n); - if ((n += len) == TAP_BUF_BYTES) + if (sadd_overflow(n, len, &n)) + return; + + if (n == TAP_BUF_BYTES) break; } diff --git a/util.c b/util.c index dd2e57f..4de872b 100644 --- a/util.c +++ b/util.c @@ -585,6 +585,11 @@ int write_remainder(int fd, const struct iovec *iov, int iovcnt, size_t skip) if (rc < 0) return -1; + if (skip > (size_t)SIZE_MAX - rc) { + errno = -ERANGE; + return -1; + } + skip += rc; } diff --git a/util.h b/util.h index eebb027..1da2616 100644 --- a/util.h +++ b/util.h @@ -223,6 +223,61 @@ static inline bool mod_between(unsigned x, unsigned i, unsigned j, unsigned m) return mod_sub(x, i, m) < mod_sub(j, i, m); } +#if defined __has_builtin +# if __has_builtin(__builtin_add_overflow) +# define sadd_overflow(a, b, res) __builtin_add_overflow(a, b, res) +# endif +# if __has_builtin(__builtin_sub_overflow) +# define ssub_overflow(a, b, res) __builtin_sub_overflow(a, b, res) +# endif +#endif + +#ifndef SSIZE_MIN +# if SSIZE_MAX == LONG_MAX +# define SSIZE_MIN LONG_MIN +# else +# define SSIZE_MIN INT_MIN +# endif +#endif + +#ifndef sadd_overflow +/** + * sadd_overflow() - Sum with overflow check for ssize_t values + * @a: First value + * @b: Second value + * @sum: Pointer to result of sum, if it doesn't overflow + * + * Return: true if the sum would overflow, false otherwise + */ +static inline bool sadd_overflow(ssize_t a, ssize_t b, ssize_t *sum) +{ + if ((a > 0 && a > SSIZE_MAX - b) || (b < 0 && a < SSIZE_MIN - b)) + return true; + + *sum = a + b; + return false; +} +#endif + +#ifndef ssub_overflow +/** + * ssub_overflow() - Subtraction with overflow check for ssize_t values + * @a: Minuend + * @b: Subtrahend + * @sum: Pointer to result of subtraction, if it doesn't overflow + * + * Return: true if the subtraction would overflow, false otherwise + */ +static inline bool ssub_overflow(ssize_t a, ssize_t b, ssize_t *diff) +{ + if ((b > 0 && a < SSIZE_MIN + b) || (b < 0 && a > SSIZE_MAX + b)) + return true; + + *diff = a - b; + return false; +} +#endif + /* * Workarounds for https://github.com/llvm/llvm-project/issues/58992 * -- 2.43.0