google / kernel-sanitizers

Linux Kernel Sanitizers, fast bug-detectors for the Linux kernel
https://google.github.io/kernel-sanitizers/
437 stars 87 forks source link

[kfence] Proper handling of all allocation API #82

Closed ramosian-glider closed 4 years ago

ramosian-glider commented 4 years ago

We need to ensure naive implementation calls our runtime exactly once.

Every allocation API function from include/linux/slab.h ends up calling one of the following three functions to allocate memory:

- kmalloc_large(size, flags)
- kmalloc_large_node(size, flags, node)
- slab_alloc_node(cache, flags, node, _RET_IP_)

The first two functions are used only for big allocations and therefore don't need to be instrumented with KFENCE. The last function (there also exists its node-agnostic version, slab_alloc(), that calls slab_alloc_node()) actually performs all allocations we are interested in.

For kmalloc() allocations the exact allocation size is never passed to slab_alloc_node(), which makes it impossible to precisely put kmalloc()-allocated objects at the end of the page. To handle this properly, we can:

  1. either pass the size requested by kmalloc() to slab_alloc_node() (we can use the upper bits in _RET_IP_ to store it)
  2. or insert KFENCE hooks into allocation functions in a way that:
    • there is at least one hook on every path from an API function to slab_alloc_node() at a place where size is available;
    • if there is more than one such hook, only one should be executed;

The first option looks a bit hackier, but will work for both naive and stealing implementations. The second one will require additional trickery to support kmalloc() in the stealing implementation.

For the second option, there is no basis API subset that can be instrumented with exactly one hook, e.g. because kmalloc() may call either __kmalloc() or slab_alloc_node() (which means we need a hook in kmalloc()), whereas kmalloc_array() may call either kmalloc() or __kmalloc() (which means we need a hook in __kmalloc() as well).

We can use a special GFP flag to tell the underlying functions we've run KFENCE for this allocation already.

ramosian-glider commented 4 years ago

We need to ensure naive implementation calls our runtime exactly once.

Right now there is a single point where KFENCE allocator is invoked, so this is resolved.