-
Notifications
You must be signed in to change notification settings - Fork 867
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Out-of-bounds memory access in user-space BPF VM due to unsigned int overflow #379
Comments
Note that OFFSET + sizeof(int32) overflows to 0 on an ILP32 platform but not an LP64 platform, and probably not even on an LLP64 platform (cf. WinPcap) as long as sizeof is 64-bit. In particular, with
I didn't see any packets on 64-bit OpenBSD but did see them on 32-bit OpenBSD. (This was capturing on a network device, so that's the kernel BPF. That's not our bug, but it's the same issue, so we want to report that issue to various uses of the in-kernel BPF engine as well, if they're not already checking whether the starting offset of the halfword or word being fetched is within the packet data.) |
For kernel BPFs, the
(yes,
which contains nothing that The userland However, we also need to make sure the indexed loads are checked correctly at run time in userland and all the kernel BPF interpreters. (The kernel's |
I've checked change 26a3075 into the trunk; it makes the interpreter more like the FreeBSD interpreter, which does more bounds checking and catches this issue. Unless somebody finds a problem with it, I'll backport it to the 1.6 branch. |
This prevents the checks from incorrectly succeeding due to integer overflow on 32-bit platforms, addressing GitHub issue #379. It also catches integer overflow in indexed loads when adding the X register to the offset; that's not necessary for bounds checking, but we'll put it in anyway. Also, make the variable "k", and the memory array, unsigned; that's the way it is in most kernel BPF interpreters, and means we use unsigned address arithmetic consistently.
After the change commited by Guy, any reason to keep this issue open? |
@guyharris, do you think this matter still requires any attention in libpcap or elsewhere? |
The command given above, modified not to assume a location for tcpdump or an interface on which to capture:
will either 1) have the filter accepted by the kernel, in which case any out-of-bounds reference is a bug in the OS kernel, not in libpcap, or 2) have the filter rejected by the kernel, in which case our current userland filter should handle it without an out-of-bounds access. |
A warning. Nothing captured.
Capture packets. |
Should be an error and exit ? |
Traditionally libpcap uses userland filtering if kernel filtering didn't work. It looks odd that the "any" interface didn't reject the filter. |
At least some kernel cBPF implementations impose a maximum size for filters ("here's a 3GB BPF program", on a 32-bit machine, is a bit rude :-)), so valid-but-too-large filters are rejected by the kernel; this allows the packets to be filtered, albeit without the advantage of having packets that aren't what the user wants to see being discarded early, before they have to be put into a limited-size kernel buffer or be copied up to userland.
I'll see what code gets generated for Ethernet rather than cooked Linux captures; perhaps the cooked-Linux BPF passes the validator. |
Thank you. |
Ah, yes. |
The difference is that the sample expression is |
For now, we can report EINVAL as "filter is not supported by the kernel". |
Another common reason for rejecting the filter on some OSes is presence of |
In terms of the hypothetical new function mentioned in #1375 this could be: p = pcap_create("mydevice", errbuf);
pcap_set_flags(p, pcap_get_flags(p) | PCAP_REQUIRE_KERNEL_FILTERING);
pcap_activate(p); which would make |
To reproduce:
The BPF:
Note that the resulting offset is 2**32 - 4.
Expected results: nothing prints, as the offset is not within the range of the packet
Actual results: depends on what's mapped at offset 2**32-4 from your packet start. On my system I don't get a crash but I do get passing packets. The reason is that the length check is performed against OFFSET + sizeof(int32), which overflows to 0.
Note that this vulnerability does not appear to affect the Linux kernel, as its range validation is different. The precise example above doesn't run on the kernel, because loading large constant offsets has special meaning to the kernel, and the constant is invalid, forcing the user-space VM to run. You can make the kernel try a similar codepath by making the offset a computed value. I wasn't able to cause a crash or unexpected passes, but I can't rule out the possibility of a vulnerability there.
The text was updated successfully, but these errors were encountered: