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 | 7 +++++-- util.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/lineread.c b/lineread.c index 0387f4a..12f2d24 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 (saddl_overflow(lr->count, rc, &lr->count)) + return -ERANGE; } *line = lr->buf + lr->next_line; diff --git a/tap.c b/tap.c index ec994a2..7f8c26d 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 (saddl_overflow(n, rem, &n)) + return; + + if (n != l2len) return; } @@ -1046,7 +1050,9 @@ redo: next: p += l2len; - n -= l2len; + + if (ssubl_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 (saddl_overflow(n, len, &n)) + return; + continue; } - tap_add_packet(c, len, pkt_buf + n); - if ((n += len) == TAP_BUF_BYTES) + if (saddl_overflow(n, len, &n)) + return; + + if (n == TAP_BUF_BYTES) break; } diff --git a/util.c b/util.c index dd2e57f..a72d6c5 100644 --- a/util.c +++ b/util.c @@ -567,7 +567,7 @@ int do_clone(int (*fn)(void *), char *stack_area, size_t stack_size, int flags, * * #syscalls write writev */ -int write_remainder(int fd, const struct iovec *iov, int iovcnt, size_t skip) +int write_remainder(int fd, const struct iovec *iov, int iovcnt, ssize_t skip) { int i; size_t offset; @@ -585,7 +585,10 @@ int write_remainder(int fd, const struct iovec *iov, int iovcnt, size_t skip) if (rc < 0) return -1; - skip += rc; + if (saddl_overflow(skip, rc, &skip)) { + errno = -ERANGE; + return -1; + } } return 0; diff --git a/util.h b/util.h index eebb027..497d2fd 100644 --- a/util.h +++ b/util.h @@ -161,7 +161,7 @@ void pidfile_write(int fd, pid_t pid); int __daemon(int pidfile_fd, int devnull_fd); int fls(unsigned long x); int write_file(const char *path, const char *buf); -int write_remainder(int fd, const struct iovec *iov, int iovcnt, size_t skip); +int write_remainder(int fd, const struct iovec *iov, int iovcnt, ssize_t skip); /** * af_name() - Return name of an address family @@ -223,6 +223,50 @@ static inline bool mod_between(unsigned x, unsigned i, unsigned j, unsigned m) return mod_sub(x, i, m) < mod_sub(j, i, m); } +/** + * saddl_overflow() - Sum with overflow check for long signed 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 saddl_overflow(long a, long b, long *sum) +{ +#if __GNUC__ + return __builtin_saddl_overflow(a, b, sum); +#else + if ((a > 0 && a > LONG_MAX - b) || + (b < 0 && a < LONG_MIN - b)) + return true; + + *sum = a + b; + return false; +#endif +} + +/** + * saddl_overflow() - Subtraction with overflow check for long signed 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 ssubl_overflow(long a, long b, long *diff) +{ +#if __GNUC__ + return __builtin_ssubl_overflow(a, b, diff); +#else + if ((b > 0 && a < LONG_MIN + b) || + (b < 0 && a > LONG_MAX + b)) + return true; + + *diff = a - b; + return false; +#endif +} + /* * Workarounds for https://github.com/llvm/llvm-project/issues/58992 * -- 2.43.0