Skip to content

Commit cef0638

Browse files
enlist12Kernel Patches Daemon
authored andcommitted
Uninitialized Stack Variable / NULL Pointer Dereference in __sys_socket_create via BPF LSM hooks
> Our fuzzing found an Uninitialized Stack Variable vulnerability in the > Linux Socket Subsystem. The issue is triggered when a > `BPF_PROG_TYPE_LSM` attached to the `bpf_lsm_socket_create` cgroup hook > uses the `bpf_set_retval()` helper to return a value greater than 0 > (e.g., `1`). This bypasses the actual `socket_create` execution but > returns a success status back to `__sys_socket_create`, which then > accesses the uninitialized stack variable `sock` leading to a NULL > pointer dereference or potential privilege escalation. > > Reported-by: Quan Sun <2022090917019@std.uestc.edu.cn> > Reported-by: Yinhao Hu <dddddd@hust.edu.cn> > Reported-by: Kaiyan Mei <M202472210@hust.edu.cn> > Reviewed-by: Dongliang Mu <dzm91@hust.edu.cn> > > ## Root Cause > > This vulnerability is caused by treating a manipulated return value from > an LSM hook as complete success in `__sys_socket_create` without > ensuring that the underlying object has been fully initialized. > > 1. `__sys_socket_create()` in `net/socket.c` reserves a pointer variable > `struct socket *sock;` on the kernel stack without zero-initializing it. > 2. It calls `sock_create(family, type, protocol, &sock);` to allocate > and initialize the socket object. > 3. `sock_create` proceeds to allocate the socket internally and invokes > the `security_socket_create` LSM hook (which triggers > `bpf_lsm_socket_create`). > 4. If a BPF program is attached to the cgroup LSM hook for > `bpf_lsm_socket_create`, it can call `bpf_set_retval(1)` and return `1`. > 5. Because the BPF hook completes without an error code (< 0), > `sock_create` interprets this bypass as success and promptly returns `1` > (or a positive bypass value) back to `__sys_socket_create()`. However, > the critical variable `sock` is never populated. > 6. The caller `__sys_socket_create()` checks if `sock_create()`'s return > value is `< 0`. Since `1` is not less than `0`, it assumes the `sock` > pointer is valid. > 7. Subsequent socket operations inside `__sys_socket_create()`, such as > `sock_map_fd(sock, ...)`, attempt to dereference the uninitialized > `sock` stack pointer. > > #### Execution Flow Visualization > > ```text > Vulnerability Execution Flow > | > |--- 1. `__sys_socket_create(...)` > | |\ > | | `-- struct socket *sock; (Uninitialized pointer on stack) > | | > |--- 2. `sock_create(...)` -> `security_socket_create(...)` > | |\ > | | `-- BPF LSM CGROUP hook for `bpf_lsm_socket_create` triggered. > | | | > | | `-- bpf_set_retval(1); return 1; > | | > |--- 3. Context switches back to `sock_create` > | |\ > | | `-- LSM hook returns `1`. `sock_create` skips allocation and > returns `1`. > | | > |--- 4. Context switches back to `__sys_socket_create` > | |\ > | | `-- Check return value (1 < 0 is False). Treated as SUCCESS. > | | | > | | `-- `sock_map_fd(sock, ...)` > | | | > | | `-- Dereferences `sock` leading to KERNEL PANIC or Hijack. > ``` > > ## Reproduction Steps > > 1. Load a `BPF_PROG_TYPE_LSM` BPF program that: > - Sets the target BTF ID to `bpf_lsm_socket_create`. > - Calls the `bpf_set_retval(1)` helper inside the hook. > 2. Attach the loaded program to a chosen cgroup directory using > `BPF_LSM_CGROUP`. > 3. Add the current process to the targeted cgroup. > 4. Trigger the creation of a socket by invoking the `socket(AF_INET, > SOCK_STREAM, 0)` system call from within the cgroup. > 5. The `sock_create` function skips allocation, returns 1, and > `__sys_socket_create` dereferences the uninitialized stack pointer. Thanks for the report. Maybe we could fix like below. I guess all (most?) places suppose LSM to return 0 or a negative value, and the btf_id_set_contains() check would be unnecessary ? ---8<--- ---8<---
1 parent c82410a commit cef0638

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

kernel/bpf/bpf_lsm.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ BTF_ID(func, bpf_lsm_socket_socketpair)
8787
#endif
8888
BTF_SET_END(bpf_lsm_unlocked_sockopt_hooks)
8989

90+
BTF_SET_START(bpf_lsm_negative_set_retval_hooks)
91+
#ifdef CONFIG_SECURITY_NETWORK
92+
BTF_ID(func, bpf_lsm_socket_create)
93+
#endif
94+
BTF_SET_END(bpf_lsm_negative_set_retval_hooks)
95+
96+
9097
#ifdef CONFIG_CGROUP_BPF
9198
void bpf_lsm_find_cgroup_shim(const struct bpf_prog *prog,
9299
bpf_func_t *bpf_func)
@@ -221,12 +228,37 @@ static const struct bpf_func_proto bpf_get_attach_cookie_proto = {
221228
.arg1_type = ARG_PTR_TO_CTX,
222229
};
223230

231+
BPF_CALL_1(bpf_lsm_set_retval, int, retval)
232+
{
233+
struct bpf_cg_run_ctx *ctx;
234+
235+
if (retval > 0)
236+
return -EINVAL;
237+
238+
ctx = container_of(current->bpf_ctx, struct bpf_cg_run_ctx, run_ctx);
239+
ctx->retval = retval;
240+
241+
return 0;
242+
}
243+
244+
const struct bpf_func_proto bpf_lsm_set_retval_proto = {
245+
.func = bpf_lsm_set_retval,
246+
.gpl_only = false,
247+
.ret_type = RET_INTEGER,
248+
.arg1_type = ARG_ANYTHING,
249+
};
250+
224251
static const struct bpf_func_proto *
225252
bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
226253
{
227254
const struct bpf_func_proto *func_proto;
228255

229256
if (prog->expected_attach_type == BPF_LSM_CGROUP) {
257+
if (func_id == BPF_FUNC_set_retval &&
258+
btf_id_set_contains(&bpf_lsm_negative_set_retval_hooks,
259+
prog->aux->attach_btf_id))
260+
return &bpf_lsm_set_retval_proto;
261+
230262
func_proto = cgroup_common_func_proto(func_id, prog);
231263
if (func_proto)
232264
return func_proto;

0 commit comments

Comments
 (0)