In the CVE it almost hints that this specifically is UDP related.
Am I right in thinking this?
In the CVE it almost hints that this specifically is UDP related.
Am I right in thinking this?
void *x = malloc(16);
free(x);
...
free(x);
When malloc makes the allocation, it actually allocates a bit more than 16 bytes so it knows how much to free. Usually what it'll do is store a structure right before the pointer it returns; for instance, it might allocate 24 bytes, use the first 8 bytes to store the number 24, and then return the rest of that allocation. Then, when free is called, it subtracts 8 from what it was given, and frees that many bytes. (More complicated malloc implementations will have more complicated structures, possibly including pointers to other, shared structures.)If you call free a second time on the same pointer, a lot of things could happen. In particular, if there was another call to malloc() in between, it might reuse that same location, and you might free that. Or if some other sequence of allocations happened, there might be a completely nonsensical number stored at x-8.
If the second call "frees" data that's still in use, two parts of the program will be writing to the same memory. Maybe one thing is storing the return value of a call, and the other thing is storing the current UID. So when the first thing writes 0, now you're UID 0.
Alternatively, if the second call frees something nonsensical, you might "free" memory that was never the allocator's in the first place - and cause the next call to malloc() to return a pointer to something important elsewhere in memory, perhaps even the program's stack frame. If the program then reads attacker-controlled data into that newly-allocated memory, the attacker takes over the program's control flow.
DCCP is a protocol that is kind of like UDP, in that it's message-oriented, but kind of like TCP, in that it's reliable and implements congestion control. It's not very common, and uncommon networking protocols have been a historic good source of vulnerabilities in the Linux kernel.
DCCP implements congestion control but is not reliable.
OpenBSD's malloc implementation for example actually will let you enable an option that instead marks unused pages in the freelist maintained by malloc as PROT_NONE, i.e. reuse of a page will cause a segfault.
However, there are just various tradeoffs here. For example, OpenBSD's malloc does not guarantee that a `free` will immediately cause a freelist entry to become unused (AFAICS reading the man page -- option `F` vs `U`). So it will not immediately be marked as PROT_NONE, but only once a number of unused entries get 'flushed'. This is (presumably) an optimization so malloc can keep unused chunks around for quick-reuse, without having to do remappings.
In the case of the Linux kernel, obviously it is using a different allocator, but the same basic ideas apply.
For a protection against this particular exploit, you can look to grsecurity. In particular, I believe PAX_MEMORY_SANITIZE nullifies the major attack vector of this bug, immediately -- as it frees/erases all unused slab/memory pages immediately. If a page or object that is freed contains a pointer (i.e. the traditional mode of attack requires some pointer dereference, instead pointing to attacker-controlled code) -- this will trip PAX_MEMORY_SANITIZE as use of a freed pointer, and kill the kernel task.
Other techniques for exploiting this are probably handled by similar defenses.
(So I think this is like, kernel exploit #93,234,893 stopped immediately by a grsecurity feature?)
EDIT: mikeash also points out another good bit I forgot, which is that the basic defenses don't immediately catch a kind of 'ABA-style' double free. e.g. you free, something else gets the allocation instantly after, then you cause another free again. You'll only crash later.
But nullifying the attack vector is what's really important here, which is: you need to stop this from extending into an (almost arbitrary) read/write vector.
x_1 = malloc(n)
free(x_1)
...
x_2 = malloc(n) // malloc returns the same address as x_1, ie x_1 == x_2
...
free(x_1)
...
example_function(x_2)
How would the runtime know the difference between a double free of x_1 and freeing x_1 then later x_2? And now we think x_2 points to valid data when it doesn't, which can lead to a use after free error.This won't immediately detect the case where you free, something else mallocs in that exact spot, and then you free again. It will eventually catch it when the something else tries to call free. It will catch cases where the second free call happens before the memory is reused, or when the memory is reused but the new chunk starts earlier.
I assume this isn't done because maintaining and checking the list would cost too much.
At 1 TB/sec of allocations it would take 500 years to reach the end of the 64-bit address space in the kernel. (It's actually 544 years ignoring leap years and leap seconds, but subtract 44 years for reserved address ranges).
The benefit would be making it impossible to use-after-free because the same address would never be re-used.
Pointers point to descriptors that point to allocations. Of course, you can't dynamically manage descriptors without reintroducing the same problem, but the answer to every problem in computer science...
That said, the Linux kernel is a helluva lot more attack surface than the relatively few hypercalls provided by most Type 1 hypervisors.