Skip to content

Commit 050fad7

Browse files
borkmannAlexei Starovoitov
authored andcommitted
bpf: fix truncated jump targets on heavy expansions
Recently during testing, I ran into the following panic: [ 207.892422] Internal error: Accessing user space memory outside uaccess.h routines: 96000004 [#1] SMP [ 207.901637] Modules linked in: binfmt_misc [...] [ 207.966530] CPU: 45 PID: 2256 Comm: test_verifier Tainted: G W 4.17.0-rc3+ #7 [ 207.974956] Hardware name: FOXCONN R2-1221R-A4/C2U4N_MB, BIOS G31FB18A 03/31/2017 [ 207.982428] pstate: 60400005 (nZCv daif +PAN -UAO) [ 207.987214] pc : bpf_skb_load_helper_8_no_cache+0x34/0xc0 [ 207.992603] lr : 0xffff000000bdb754 [ 207.996080] sp : ffff000013703ca0 [ 207.999384] x29: ffff000013703ca0 x28: 0000000000000001 [ 208.004688] x27: 0000000000000001 x26: 0000000000000000 [ 208.009992] x25: ffff000013703ce0 x24: ffff800fb4afcb00 [ 208.015295] x23: ffff00007d2f5038 x22: ffff00007d2f5000 [ 208.020599] x21: fffffffffeff2a6f x20: 000000000000000a [ 208.025903] x19: ffff000009578000 x18: 0000000000000a03 [ 208.031206] x17: 0000000000000000 x16: 0000000000000000 [ 208.036510] x15: 0000ffff9de83000 x14: 0000000000000000 [ 208.041813] x13: 0000000000000000 x12: 0000000000000000 [ 208.047116] x11: 0000000000000001 x10: ffff0000089e7f18 [ 208.052419] x9 : fffffffffeff2a6f x8 : 0000000000000000 [ 208.057723] x7 : 000000000000000a x6 : 00280c6160000000 [ 208.063026] x5 : 0000000000000018 x4 : 0000000000007db6 [ 208.068329] x3 : 000000000008647a x2 : 19868179b1484500 [ 208.073632] x1 : 0000000000000000 x0 : ffff000009578c08 [ 208.078938] Process test_verifier (pid: 2256, stack limit = 0x0000000049ca7974) [ 208.086235] Call trace: [ 208.088672] bpf_skb_load_helper_8_no_cache+0x34/0xc0 [ 208.093713] 0xffff000000bdb754 [ 208.096845] bpf_test_run+0x78/0xf8 [ 208.100324] bpf_prog_test_run_skb+0x148/0x230 [ 208.104758] sys_bpf+0x314/0x1198 [ 208.108064] el0_svc_naked+0x30/0x34 [ 208.111632] Code: 91302260 f9400001 f9001fa1 d2800001 (29500680) [ 208.117717] ---[ end trace 263cb8a59b5bf29f ]--- The program itself which caused this had a long jump over the whole instruction sequence where all of the inner instructions required heavy expansions into multiple BPF instructions. Additionally, I also had BPF hardening enabled which requires once more rewrites of all constant values in order to blind them. Each time we rewrite insns, bpf_adj_branches() would need to potentially adjust branch targets which cross the patchlet boundary to accommodate for the additional delta. Eventually that lead to the case where the target offset could not fit into insn->off's upper 0x7fff limit anymore where then offset wraps around becoming negative (in s16 universe), or vice versa depending on the jump direction. Therefore it becomes necessary to detect and reject any such occasions in a generic way for native eBPF and cBPF to eBPF migrations. For the latter we can simply check bounds in the bpf_convert_filter()'s BPF_EMIT_JMP helper macro and bail out once we surpass limits. The bpf_patch_insn_single() for native eBPF (and cBPF to eBPF in case of subsequent hardening) is a bit more complex in that we need to detect such truncations before hitting the bpf_prog_realloc(). Thus the latter is split into an extra pass to probe problematic offsets on the original program in order to fail early. With that in place and carefully tested I no longer hit the panic and the rewrites are rejected properly. The above example panic I've seen on bpf-next, though the issue itself is generic in that a guard against this issue in bpf seems more appropriate in this case. Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 9617456 commit 050fad7

File tree

2 files changed

+84
-27
lines changed

2 files changed

+84
-27
lines changed

kernel/bpf/core.c

Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -218,47 +218,84 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
218218
return 0;
219219
}
220220

221-
static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta)
221+
static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, u32 delta,
222+
u32 curr, const bool probe_pass)
222223
{
224+
const s64 imm_min = S32_MIN, imm_max = S32_MAX;
225+
s64 imm = insn->imm;
226+
227+
if (curr < pos && curr + imm + 1 > pos)
228+
imm += delta;
229+
else if (curr > pos + delta && curr + imm + 1 <= pos + delta)
230+
imm -= delta;
231+
if (imm < imm_min || imm > imm_max)
232+
return -ERANGE;
233+
if (!probe_pass)
234+
insn->imm = imm;
235+
return 0;
236+
}
237+
238+
static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, u32 delta,
239+
u32 curr, const bool probe_pass)
240+
{
241+
const s32 off_min = S16_MIN, off_max = S16_MAX;
242+
s32 off = insn->off;
243+
244+
if (curr < pos && curr + off + 1 > pos)
245+
off += delta;
246+
else if (curr > pos + delta && curr + off + 1 <= pos + delta)
247+
off -= delta;
248+
if (off < off_min || off > off_max)
249+
return -ERANGE;
250+
if (!probe_pass)
251+
insn->off = off;
252+
return 0;
253+
}
254+
255+
static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
256+
const bool probe_pass)
257+
{
258+
u32 i, insn_cnt = prog->len + (probe_pass ? delta : 0);
223259
struct bpf_insn *insn = prog->insnsi;
224-
u32 i, insn_cnt = prog->len;
225-
bool pseudo_call;
226-
u8 code;
227-
int off;
260+
int ret = 0;
228261

229262
for (i = 0; i < insn_cnt; i++, insn++) {
263+
u8 code;
264+
265+
/* In the probing pass we still operate on the original,
266+
* unpatched image in order to check overflows before we
267+
* do any other adjustments. Therefore skip the patchlet.
268+
*/
269+
if (probe_pass && i == pos) {
270+
i += delta + 1;
271+
insn++;
272+
}
230273
code = insn->code;
231-
if (BPF_CLASS(code) != BPF_JMP)
232-
continue;
233-
if (BPF_OP(code) == BPF_EXIT)
274+
if (BPF_CLASS(code) != BPF_JMP ||
275+
BPF_OP(code) == BPF_EXIT)
234276
continue;
277+
/* Adjust offset of jmps if we cross patch boundaries. */
235278
if (BPF_OP(code) == BPF_CALL) {
236-
if (insn->src_reg == BPF_PSEUDO_CALL)
237-
pseudo_call = true;
238-
else
279+
if (insn->src_reg != BPF_PSEUDO_CALL)
239280
continue;
281+
ret = bpf_adj_delta_to_imm(insn, pos, delta, i,
282+
probe_pass);
240283
} else {
241-
pseudo_call = false;
284+
ret = bpf_adj_delta_to_off(insn, pos, delta, i,
285+
probe_pass);
242286
}
243-
off = pseudo_call ? insn->imm : insn->off;
244-
245-
/* Adjust offset of jmps if we cross boundaries. */
246-
if (i < pos && i + off + 1 > pos)
247-
off += delta;
248-
else if (i > pos + delta && i + off + 1 <= pos + delta)
249-
off -= delta;
250-
251-
if (pseudo_call)
252-
insn->imm = off;
253-
else
254-
insn->off = off;
287+
if (ret)
288+
break;
255289
}
290+
291+
return ret;
256292
}
257293

258294
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
259295
const struct bpf_insn *patch, u32 len)
260296
{
261297
u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
298+
const u32 cnt_max = S16_MAX;
262299
struct bpf_prog *prog_adj;
263300

264301
/* Since our patchlet doesn't expand the image, we're done. */
@@ -269,6 +306,15 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
269306

270307
insn_adj_cnt = prog->len + insn_delta;
271308

309+
/* Reject anything that would potentially let the insn->off
310+
* target overflow when we have excessive program expansions.
311+
* We need to probe here before we do any reallocation where
312+
* we afterwards may not fail anymore.
313+
*/
314+
if (insn_adj_cnt > cnt_max &&
315+
bpf_adj_branches(prog, off, insn_delta, true))
316+
return NULL;
317+
272318
/* Several new instructions need to be inserted. Make room
273319
* for them. Likely, there's no need for a new allocation as
274320
* last page could have large enough tailroom.
@@ -294,7 +340,11 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
294340
sizeof(*patch) * insn_rest);
295341
memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
296342

297-
bpf_adj_branches(prog_adj, off, insn_delta);
343+
/* We are guaranteed to not fail at this point, otherwise
344+
* the ship has sailed to reverse to the original state. An
345+
* overflow cannot happen at this point.
346+
*/
347+
BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
298348

299349
return prog_adj;
300350
}

net/core/filter.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,18 @@ static int bpf_convert_filter(struct sock_filter *prog, int len,
481481

482482
#define BPF_EMIT_JMP \
483483
do { \
484+
const s32 off_min = S16_MIN, off_max = S16_MAX; \
485+
s32 off; \
486+
\
484487
if (target >= len || target < 0) \
485488
goto err; \
486-
insn->off = addrs ? addrs[target] - addrs[i] - 1 : 0; \
489+
off = addrs ? addrs[target] - addrs[i] - 1 : 0; \
487490
/* Adjust pc relative offset for 2nd or 3rd insn. */ \
488-
insn->off -= insn - tmp_insns; \
491+
off -= insn - tmp_insns; \
492+
/* Reject anything not fitting into insn->off. */ \
493+
if (off < off_min || off > off_max) \
494+
goto err; \
495+
insn->off = off; \
489496
} while (0)
490497

491498
case BPF_JMP | BPF_JA:

0 commit comments

Comments
 (0)