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
---
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
#include
#include
+#include
#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