aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2018-05-16 19:44:11 -0400
committerAlexei Starovoitov <ast@kernel.org>2018-05-17 19:05:35 -0400
commit050fad7c4534c13c8eb1d9c2ba66012e014773cb (patch)
tree2a5273ddd08ba61ab57f12d6a3d4702fca8c0bf3
parent9617456054a6160f5e11e892b713fade78aea2e9 (diff)
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 <daniel@iogearbox.net> Acked-by: Martin KaFai Lau <kafai@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--kernel/bpf/core.c100
-rw-r--r--net/core/filter.c11
2 files changed, 84 insertions, 27 deletions
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index ba03ec39efb3..6ef6746a7871 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -218,47 +218,84 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
218 return 0; 218 return 0;
219} 219}
220 220
221static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta) 221static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, u32 delta,
222 u32 curr, const bool probe_pass)
222{ 223{
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
238static 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
255static 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);
223 struct bpf_insn *insn = prog->insnsi; 259 struct bpf_insn *insn = prog->insnsi;
224 u32 i, insn_cnt = prog->len; 260 int ret = 0;
225 bool pseudo_call;
226 u8 code;
227 int off;
228 261
229 for (i = 0; i < insn_cnt; i++, insn++) { 262 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 }
230 code = insn->code; 273 code = insn->code;
231 if (BPF_CLASS(code) != BPF_JMP) 274 if (BPF_CLASS(code) != BPF_JMP ||
232 continue; 275 BPF_OP(code) == BPF_EXIT)
233 if (BPF_OP(code) == BPF_EXIT)
234 continue; 276 continue;
277 /* Adjust offset of jmps if we cross patch boundaries. */
235 if (BPF_OP(code) == BPF_CALL) { 278 if (BPF_OP(code) == BPF_CALL) {
236 if (insn->src_reg == BPF_PSEUDO_CALL) 279 if (insn->src_reg != BPF_PSEUDO_CALL)
237 pseudo_call = true;
238 else
239 continue; 280 continue;
281 ret = bpf_adj_delta_to_imm(insn, pos, delta, i,
282 probe_pass);
240 } else { 283 } else {
241 pseudo_call = false; 284 ret = bpf_adj_delta_to_off(insn, pos, delta, i,
285 probe_pass);
242 } 286 }
243 off = pseudo_call ? insn->imm : insn->off; 287 if (ret)
244 288 break;
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;
255 } 289 }
290
291 return ret;
256} 292}
257 293
258struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, 294struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
259 const struct bpf_insn *patch, u32 len) 295 const struct bpf_insn *patch, u32 len)
260{ 296{
261 u32 insn_adj_cnt, insn_rest, insn_delta = len - 1; 297 u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
298 const u32 cnt_max = S16_MAX;
262 struct bpf_prog *prog_adj; 299 struct bpf_prog *prog_adj;
263 300
264 /* Since our patchlet doesn't expand the image, we're done. */ 301 /* 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,
269 306
270 insn_adj_cnt = prog->len + insn_delta; 307 insn_adj_cnt = prog->len + insn_delta;
271 308
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
272 /* Several new instructions need to be inserted. Make room 318 /* Several new instructions need to be inserted. Make room
273 * for them. Likely, there's no need for a new allocation as 319 * for them. Likely, there's no need for a new allocation as
274 * last page could have large enough tailroom. 320 * last page could have large enough tailroom.
@@ -294,7 +340,11 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
294 sizeof(*patch) * insn_rest); 340 sizeof(*patch) * insn_rest);
295 memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len); 341 memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
296 342
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));
298 348
299 return prog_adj; 349 return prog_adj;
300} 350}
diff --git a/net/core/filter.c b/net/core/filter.c
index e77c30ca491d..201ff36b17a8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -481,11 +481,18 @@ do_pass:
481 481
482#define BPF_EMIT_JMP \ 482#define BPF_EMIT_JMP \
483 do { \ 483 do { \
484 const s32 off_min = S16_MIN, off_max = S16_MAX; \
485 s32 off; \
486 \
484 if (target >= len || target < 0) \ 487 if (target >= len || target < 0) \
485 goto err; \ 488 goto err; \
486 insn->off = addrs ? addrs[target] - addrs[i] - 1 : 0; \ 489 off = addrs ? addrs[target] - addrs[i] - 1 : 0; \
487 /* Adjust pc relative offset for 2nd or 3rd insn. */ \ 490 /* 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; \
489 } while (0) 496 } while (0)
490 497
491 case BPF_JMP | BPF_JA: 498 case BPF_JMP | BPF_JA: