aboutgitcodebugslistschat
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2025-03-17 20:24:15 +1100
committerStefano Brivio <sbrivio@redhat.com>2025-03-20 20:33:06 +0100
commite43e00719d7701301e4bc4fb179dc7adff175409 (patch)
treea9a905785d1dacbca34f751fe30d7b4028c30e1d
parent4592719a744bcb47db2ff5680be4b8f6362a97ce (diff)
downloadpasst-e43e00719d7701301e4bc4fb179dc7adff175409.tar
passt-e43e00719d7701301e4bc4fb179dc7adff175409.tar.gz
passt-e43e00719d7701301e4bc4fb179dc7adff175409.tar.bz2
passt-e43e00719d7701301e4bc4fb179dc7adff175409.tar.lz
passt-e43e00719d7701301e4bc4fb179dc7adff175409.tar.xz
passt-e43e00719d7701301e4bc4fb179dc7adff175409.tar.zst
passt-e43e00719d7701301e4bc4fb179dc7adff175409.zip
packet: More cautious checks to avoid pointer arithmetic UB
packet_check_range and vu_packet_check_range() verify that the packet or section of packet we're interested in lies in the packet buffer pool we expect it to. However, in doing so it doesn't avoid the possibility of an integer overflow while performing pointer arithmetic, with is UB. In fact, AFAICT it's UB even to use arbitrary pointer arithmetic to construct a pointer outside of a known valid buffer. To do this safely, we can't calculate the end of a memory region with pointer addition when then the length as untrusted. Instead we must work out the offset of one memory region within another using pointer subtraction, then do integer checks against the length of the outer region. We then need to be careful about the order of checks so that those integer checks can't themselves overflow. Signed-off-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
-rw-r--r--packet.c12
-rw-r--r--vu_common.c10
2 files changed, 16 insertions, 6 deletions
diff --git a/packet.c b/packet.c
index bcac037..d1a51a5 100644
--- a/packet.c
+++ b/packet.c
@@ -52,9 +52,15 @@ static int packet_check_range(const struct pool *p, const char *ptr, size_t len,
return -1;
}
- if (ptr + len > p->buf + p->buf_size) {
- trace("packet range end %p after buffer end %p, %s:%i",
- (void *)(ptr + len), (void *)(p->buf + p->buf_size),
+ if (len > p->buf_size) {
+ trace("packet range length %zu larger than buffer %zu, %s:%i",
+ len, p->buf_size, func, line);
+ return -1;
+ }
+
+ if ((size_t)(ptr - p->buf) > p->buf_size - len) {
+ trace("packet range %p, len %zu after buffer end %p, %s:%i",
+ (void *)ptr, len, (void *)(p->buf + p->buf_size),
func, line);
return -1;
}
diff --git a/vu_common.c b/vu_common.c
index 9eea4f2..cefe5e2 100644
--- a/vu_common.c
+++ b/vu_common.c
@@ -36,11 +36,15 @@ int vu_packet_check_range(void *buf, const char *ptr, size_t len)
struct vu_dev_region *dev_region;
for (dev_region = buf; dev_region->mmap_addr; dev_region++) {
- /* NOLINTNEXTLINE(performance-no-int-to-ptr) */
- char *m = (char *)(uintptr_t)dev_region->mmap_addr +
+ uintptr_t base_addr = dev_region->mmap_addr +
dev_region->mmap_offset;
+ /* NOLINTNEXTLINE(performance-no-int-to-ptr) */
+ const char *base = (const char *)base_addr;
+
+ ASSERT(base_addr >= dev_region->mmap_addr);
- if (m <= ptr && ptr + len <= m + dev_region->size)
+ if (len <= dev_region->size && base <= ptr &&
+ (size_t)(ptr - base) <= dev_region->size - len)
return 0;
}