| From 2d5030c98838e37be3c8b0f3568f91c8e0774fa2 Mon Sep 17 00:00:00 2001 |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| Date: Thu, 1 Nov 2018 22:30:38 +0100 |
| Subject: bpf: fix partial copy of map_ptr when dst is scalar |
| |
| commit 0962590e553331db2cc0aef2dc35c57f6300dbbe upstream. |
| |
| ALU operations on pointers such as scalar_reg += map_value_ptr are |
| handled in adjust_ptr_min_max_vals(). Problem is however that map_ptr |
| and range in the register state share a union, so transferring state |
| through dst_reg->range = ptr_reg->range is just buggy as any new |
| map_ptr in the dst_reg is then truncated (or null) for subsequent |
| checks. Fix this by adding a raw member and use it for copying state |
| over to dst_reg. |
| |
| Fixes: f1174f77b50c ("bpf/verifier: rework value tracking") |
| Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> |
| Cc: Edward Cree <ecree@solarflare.com> |
| Acked-by: Alexei Starovoitov <ast@kernel.org> |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| Acked-by: Edward Cree <ecree@solarflare.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| include/linux/bpf_verifier.h | 3 +++ |
| kernel/bpf/verifier.c | 10 ++++++---- |
| 2 files changed, 9 insertions(+), 4 deletions(-) |
| |
| diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h |
| index 73bec75b74c8..a3333004fd2b 100644 |
| --- a/include/linux/bpf_verifier.h |
| +++ b/include/linux/bpf_verifier.h |
| @@ -50,6 +50,9 @@ struct bpf_reg_state { |
| * PTR_TO_MAP_VALUE_OR_NULL |
| */ |
| struct bpf_map *map_ptr; |
| + |
| + /* Max size from any of the above. */ |
| + unsigned long raw; |
| }; |
| /* Fixed part of pointer offset, pointer types only */ |
| s32 off; |
| diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c |
| index a0ffc62e7677..013b0cd1958e 100644 |
| --- a/kernel/bpf/verifier.c |
| +++ b/kernel/bpf/verifier.c |
| @@ -1935,7 +1935,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, |
| dst_reg->umax_value = umax_ptr; |
| dst_reg->var_off = ptr_reg->var_off; |
| dst_reg->off = ptr_reg->off + smin_val; |
| - dst_reg->range = ptr_reg->range; |
| + dst_reg->raw = ptr_reg->raw; |
| break; |
| } |
| /* A new variable offset is created. Note that off_reg->off |
| @@ -1965,10 +1965,11 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, |
| } |
| dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off); |
| dst_reg->off = ptr_reg->off; |
| + dst_reg->raw = ptr_reg->raw; |
| if (ptr_reg->type == PTR_TO_PACKET) { |
| dst_reg->id = ++env->id_gen; |
| /* something was added to pkt_ptr, set range to zero */ |
| - dst_reg->range = 0; |
| + dst_reg->raw = 0; |
| } |
| break; |
| case BPF_SUB: |
| @@ -1999,7 +2000,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, |
| dst_reg->var_off = ptr_reg->var_off; |
| dst_reg->id = ptr_reg->id; |
| dst_reg->off = ptr_reg->off - smin_val; |
| - dst_reg->range = ptr_reg->range; |
| + dst_reg->raw = ptr_reg->raw; |
| break; |
| } |
| /* A new variable offset is created. If the subtrahend is known |
| @@ -2025,11 +2026,12 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, |
| } |
| dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off); |
| dst_reg->off = ptr_reg->off; |
| + dst_reg->raw = ptr_reg->raw; |
| if (ptr_reg->type == PTR_TO_PACKET) { |
| dst_reg->id = ++env->id_gen; |
| /* something was added to pkt_ptr, set range to zero */ |
| if (smin_val < 0) |
| - dst_reg->range = 0; |
| + dst_reg->raw = 0; |
| } |
| break; |
| case BPF_AND: |
| -- |
| 2.17.1 |
| |