Commit 6caf9fb3 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf

Daniel Borkmann says:

====================
pull-request: bpf 2018-05-18

The following pull-request contains BPF updates for your *net* tree.

The main changes are:

1) Fix two bugs in sockmap, a use after free in sockmap's error path
   from sock_map_ctx_update_elem() where we mistakenly drop a reference
   we didn't take prior to that, and in the same function fix a race
   in bpf_prog_inc_not_zero() where we didn't use the progs from prior
   READ_ONCE(), from John.

2) Reject program expansions once we figure out that their jump target
   which crosses patchlet boundaries could otherwise get truncated in
   insn->off space, from Daniel.

3) Check the return value of fopen() in BPF selftest's test_verifier
   where we determine whether unpriv BPF is disabled, and iff we do
   fail there then just assume it is disabled. This fixes a segfault
   when used with older kernels, from Jesper.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 113f99c3 050fad7c
...@@ -218,47 +218,84 @@ int bpf_prog_calc_tag(struct bpf_prog *fp) ...@@ -218,47 +218,84 @@ int bpf_prog_calc_tag(struct bpf_prog *fp)
return 0; return 0;
} }
static void bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta) static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, u32 delta,
u32 curr, const bool probe_pass)
{ {
const s64 imm_min = S32_MIN, imm_max = S32_MAX;
s64 imm = insn->imm;
if (curr < pos && curr + imm + 1 > pos)
imm += delta;
else if (curr > pos + delta && curr + imm + 1 <= pos + delta)
imm -= delta;
if (imm < imm_min || imm > imm_max)
return -ERANGE;
if (!probe_pass)
insn->imm = imm;
return 0;
}
static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, u32 delta,
u32 curr, const bool probe_pass)
{
const s32 off_min = S16_MIN, off_max = S16_MAX;
s32 off = insn->off;
if (curr < pos && curr + off + 1 > pos)
off += delta;
else if (curr > pos + delta && curr + off + 1 <= pos + delta)
off -= delta;
if (off < off_min || off > off_max)
return -ERANGE;
if (!probe_pass)
insn->off = off;
return 0;
}
static int bpf_adj_branches(struct bpf_prog *prog, u32 pos, u32 delta,
const bool probe_pass)
{
u32 i, insn_cnt = prog->len + (probe_pass ? delta : 0);
struct bpf_insn *insn = prog->insnsi; struct bpf_insn *insn = prog->insnsi;
u32 i, insn_cnt = prog->len; int ret = 0;
bool pseudo_call;
u8 code;
int off;
for (i = 0; i < insn_cnt; i++, insn++) { for (i = 0; i < insn_cnt; i++, insn++) {
u8 code;
/* In the probing pass we still operate on the original,
* unpatched image in order to check overflows before we
* do any other adjustments. Therefore skip the patchlet.
*/
if (probe_pass && i == pos) {
i += delta + 1;
insn++;
}
code = insn->code; code = insn->code;
if (BPF_CLASS(code) != BPF_JMP) if (BPF_CLASS(code) != BPF_JMP ||
continue; BPF_OP(code) == BPF_EXIT)
if (BPF_OP(code) == BPF_EXIT)
continue; continue;
/* Adjust offset of jmps if we cross patch boundaries. */
if (BPF_OP(code) == BPF_CALL) { if (BPF_OP(code) == BPF_CALL) {
if (insn->src_reg == BPF_PSEUDO_CALL) if (insn->src_reg != BPF_PSEUDO_CALL)
pseudo_call = true;
else
continue; continue;
ret = bpf_adj_delta_to_imm(insn, pos, delta, i,
probe_pass);
} else { } else {
pseudo_call = false; ret = bpf_adj_delta_to_off(insn, pos, delta, i,
probe_pass);
} }
off = pseudo_call ? insn->imm : insn->off; if (ret)
break;
/* Adjust offset of jmps if we cross boundaries. */
if (i < pos && i + off + 1 > pos)
off += delta;
else if (i > pos + delta && i + off + 1 <= pos + delta)
off -= delta;
if (pseudo_call)
insn->imm = off;
else
insn->off = off;
} }
return ret;
} }
struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len) const struct bpf_insn *patch, u32 len)
{ {
u32 insn_adj_cnt, insn_rest, insn_delta = len - 1; u32 insn_adj_cnt, insn_rest, insn_delta = len - 1;
const u32 cnt_max = S16_MAX;
struct bpf_prog *prog_adj; struct bpf_prog *prog_adj;
/* Since our patchlet doesn't expand the image, we're done. */ /* 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,6 +306,15 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
insn_adj_cnt = prog->len + insn_delta; insn_adj_cnt = prog->len + insn_delta;
/* Reject anything that would potentially let the insn->off
* target overflow when we have excessive program expansions.
* We need to probe here before we do any reallocation where
* we afterwards may not fail anymore.
*/
if (insn_adj_cnt > cnt_max &&
bpf_adj_branches(prog, off, insn_delta, true))
return NULL;
/* Several new instructions need to be inserted. Make room /* Several new instructions need to be inserted. Make room
* for them. Likely, there's no need for a new allocation as * for them. Likely, there's no need for a new allocation as
* last page could have large enough tailroom. * 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,7 +340,11 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
sizeof(*patch) * insn_rest); sizeof(*patch) * insn_rest);
memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len); memcpy(prog_adj->insnsi + off, patch, sizeof(*patch) * len);
bpf_adj_branches(prog_adj, off, insn_delta); /* We are guaranteed to not fail at this point, otherwise
* the ship has sailed to reverse to the original state. An
* overflow cannot happen at this point.
*/
BUG_ON(bpf_adj_branches(prog_adj, off, insn_delta, false));
return prog_adj; return prog_adj;
} }
......
...@@ -1703,11 +1703,11 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, ...@@ -1703,11 +1703,11 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
* we increment the refcnt. If this is the case abort with an * we increment the refcnt. If this is the case abort with an
* error. * error.
*/ */
verdict = bpf_prog_inc_not_zero(stab->bpf_verdict); verdict = bpf_prog_inc_not_zero(verdict);
if (IS_ERR(verdict)) if (IS_ERR(verdict))
return PTR_ERR(verdict); return PTR_ERR(verdict);
parse = bpf_prog_inc_not_zero(stab->bpf_parse); parse = bpf_prog_inc_not_zero(parse);
if (IS_ERR(parse)) { if (IS_ERR(parse)) {
bpf_prog_put(verdict); bpf_prog_put(verdict);
return PTR_ERR(parse); return PTR_ERR(parse);
...@@ -1715,12 +1715,12 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, ...@@ -1715,12 +1715,12 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
} }
if (tx_msg) { if (tx_msg) {
tx_msg = bpf_prog_inc_not_zero(stab->bpf_tx_msg); tx_msg = bpf_prog_inc_not_zero(tx_msg);
if (IS_ERR(tx_msg)) { if (IS_ERR(tx_msg)) {
if (verdict) if (parse && verdict) {
bpf_prog_put(verdict);
if (parse)
bpf_prog_put(parse); bpf_prog_put(parse);
bpf_prog_put(verdict);
}
return PTR_ERR(tx_msg); return PTR_ERR(tx_msg);
} }
} }
...@@ -1805,10 +1805,10 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, ...@@ -1805,10 +1805,10 @@ static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops,
out_free: out_free:
smap_release_sock(psock, sock); smap_release_sock(psock, sock);
out_progs: out_progs:
if (verdict) if (parse && verdict) {
bpf_prog_put(verdict);
if (parse)
bpf_prog_put(parse); bpf_prog_put(parse);
bpf_prog_put(verdict);
}
if (tx_msg) if (tx_msg)
bpf_prog_put(tx_msg); bpf_prog_put(tx_msg);
write_unlock_bh(&sock->sk_callback_lock); write_unlock_bh(&sock->sk_callback_lock);
......
...@@ -481,11 +481,18 @@ static int bpf_convert_filter(struct sock_filter *prog, int len, ...@@ -481,11 +481,18 @@ static int bpf_convert_filter(struct sock_filter *prog, int len,
#define BPF_EMIT_JMP \ #define BPF_EMIT_JMP \
do { \ do { \
const s32 off_min = S16_MIN, off_max = S16_MAX; \
s32 off; \
\
if (target >= len || target < 0) \ if (target >= len || target < 0) \
goto err; \ goto err; \
insn->off = addrs ? addrs[target] - addrs[i] - 1 : 0; \ off = addrs ? addrs[target] - addrs[i] - 1 : 0; \
/* Adjust pc relative offset for 2nd or 3rd insn. */ \ /* Adjust pc relative offset for 2nd or 3rd insn. */ \
insn->off -= insn - tmp_insns; \ off -= insn - tmp_insns; \
/* Reject anything not fitting into insn->off. */ \
if (off < off_min || off > off_max) \
goto err; \
insn->off = off; \
} while (0) } while (0)
case BPF_JMP | BPF_JA: case BPF_JMP | BPF_JA:
......
...@@ -11713,6 +11713,11 @@ static void get_unpriv_disabled() ...@@ -11713,6 +11713,11 @@ static void get_unpriv_disabled()
FILE *fd; FILE *fd;
fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r"); fd = fopen("/proc/sys/"UNPRIV_SYSCTL, "r");
if (!fd) {
perror("fopen /proc/sys/"UNPRIV_SYSCTL);
unpriv_disabled = true;
return;
}
if (fgets(buf, 2, fd) == buf && atoi(buf)) if (fgets(buf, 2, fd) == buf && atoi(buf))
unpriv_disabled = true; unpriv_disabled = true;
fclose(fd); fclose(fd);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment