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: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
6caf9fb3bd
|
@ -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++) {
|
||||||
code = insn->code;
|
u8 code;
|
||||||
if (BPF_CLASS(code) != BPF_JMP)
|
|
||||||
continue;
|
/* In the probing pass we still operate on the original,
|
||||||
if (BPF_OP(code) == BPF_EXIT)
|
* unpatched image in order to check overflows before we
|
||||||
continue;
|
* do any other adjustments. Therefore skip the patchlet.
|
||||||
if (BPF_OP(code) == BPF_CALL) {
|
*/
|
||||||
if (insn->src_reg == BPF_PSEUDO_CALL)
|
if (probe_pass && i == pos) {
|
||||||
pseudo_call = true;
|
i += delta + 1;
|
||||||
else
|
insn++;
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
pseudo_call = false;
|
|
||||||
}
|
}
|
||||||
off = pseudo_call ? insn->imm : insn->off;
|
code = insn->code;
|
||||||
|
if (BPF_CLASS(code) != BPF_JMP ||
|
||||||
/* Adjust offset of jmps if we cross boundaries. */
|
BPF_OP(code) == BPF_EXIT)
|
||||||
if (i < pos && i + off + 1 > pos)
|
continue;
|
||||||
off += delta;
|
/* Adjust offset of jmps if we cross patch boundaries. */
|
||||||
else if (i > pos + delta && i + off + 1 <= pos + delta)
|
if (BPF_OP(code) == BPF_CALL) {
|
||||||
off -= delta;
|
if (insn->src_reg != BPF_PSEUDO_CALL)
|
||||||
|
continue;
|
||||||
if (pseudo_call)
|
ret = bpf_adj_delta_to_imm(insn, pos, delta, i,
|
||||||
insn->imm = off;
|
probe_pass);
|
||||||
else
|
} else {
|
||||||
insn->off = off;
|
ret = bpf_adj_delta_to_off(insn, pos, delta, i,
|
||||||
|
probe_pass);
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
* 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
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 @@ do_pass:
|
||||||
|
|
||||||
#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()
|
||||||
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);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче