| From foo@baz Fri Dec 22 16:47:02 CET 2017 |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| Date: Fri, 22 Dec 2017 16:23:11 +0100 |
| Subject: bpf: fix integer overflows |
| To: gregkh@linuxfoundation.org |
| Cc: ast@kernel.org, daniel@iogearbox.net, jannh@google.com, stable@vger.kernel.org |
| Message-ID: <20171222152312.2945-14-daniel@iogearbox.net> |
| |
| From: Daniel Borkmann <daniel@iogearbox.net> |
| |
| |
| From: Alexei Starovoitov <ast@kernel.org> |
| |
| [ Upstream commit bb7f0f989ca7de1153bd128a40a71709e339fa03 ] |
| |
| There were various issues related to the limited size of integers used in |
| the verifier: |
| - `off + size` overflow in __check_map_access() |
| - `off + reg->off` overflow in check_mem_access() |
| - `off + reg->var_off.value` overflow or 32-bit truncation of |
| `reg->var_off.value` in check_mem_access() |
| - 32-bit truncation in check_stack_boundary() |
| |
| Make sure that any integer math cannot overflow by not allowing |
| pointer math with large values. |
| |
| Also reduce the scope of "scalar op scalar" tracking. |
| |
| Fixes: f1174f77b50c ("bpf/verifier: rework value tracking") |
| Reported-by: Jann Horn <jannh@google.com> |
| Signed-off-by: Alexei Starovoitov <ast@kernel.org> |
| Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| include/linux/bpf_verifier.h | 4 +-- |
| kernel/bpf/verifier.c | 48 +++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 50 insertions(+), 2 deletions(-) |
| |
| --- a/include/linux/bpf_verifier.h |
| +++ b/include/linux/bpf_verifier.h |
| @@ -15,11 +15,11 @@ |
| * In practice this is far bigger than any realistic pointer offset; this limit |
| * ensures that umax_value + (int)off + (int)size cannot overflow a u64. |
| */ |
| -#define BPF_MAX_VAR_OFF (1ULL << 31) |
| +#define BPF_MAX_VAR_OFF (1 << 29) |
| /* Maximum variable size permitted for ARG_CONST_SIZE[_OR_ZERO]. This ensures |
| * that converting umax_value to int cannot overflow. |
| */ |
| -#define BPF_MAX_VAR_SIZ INT_MAX |
| +#define BPF_MAX_VAR_SIZ (1 << 29) |
| |
| /* Liveness marks, used for registers and spilled-regs (in stack slots). |
| * Read marks propagate upwards until they find a write mark; they record that |
| --- a/kernel/bpf/verifier.c |
| +++ b/kernel/bpf/verifier.c |
| @@ -1789,6 +1789,41 @@ static bool signed_sub_overflows(s64 a, |
| return res > a; |
| } |
| |
| +static bool check_reg_sane_offset(struct bpf_verifier_env *env, |
| + const struct bpf_reg_state *reg, |
| + enum bpf_reg_type type) |
| +{ |
| + bool known = tnum_is_const(reg->var_off); |
| + s64 val = reg->var_off.value; |
| + s64 smin = reg->smin_value; |
| + |
| + if (known && (val >= BPF_MAX_VAR_OFF || val <= -BPF_MAX_VAR_OFF)) { |
| + verbose("math between %s pointer and %lld is not allowed\n", |
| + reg_type_str[type], val); |
| + return false; |
| + } |
| + |
| + if (reg->off >= BPF_MAX_VAR_OFF || reg->off <= -BPF_MAX_VAR_OFF) { |
| + verbose("%s pointer offset %d is not allowed\n", |
| + reg_type_str[type], reg->off); |
| + return false; |
| + } |
| + |
| + if (smin == S64_MIN) { |
| + verbose("math between %s pointer and register with unbounded min value is not allowed\n", |
| + reg_type_str[type]); |
| + return false; |
| + } |
| + |
| + if (smin >= BPF_MAX_VAR_OFF || smin <= -BPF_MAX_VAR_OFF) { |
| + verbose("value %lld makes %s pointer be out of bounds\n", |
| + smin, reg_type_str[type]); |
| + return false; |
| + } |
| + |
| + return true; |
| +} |
| + |
| /* Handles arithmetic on a pointer and a scalar: computes new min/max and var_off. |
| * Caller should also handle BPF_MOV case separately. |
| * If we return -EACCES, caller may want to try again treating pointer as a |
| @@ -1854,6 +1889,10 @@ static int adjust_ptr_min_max_vals(struc |
| dst_reg->type = ptr_reg->type; |
| dst_reg->id = ptr_reg->id; |
| |
| + if (!check_reg_sane_offset(env, off_reg, ptr_reg->type) || |
| + !check_reg_sane_offset(env, ptr_reg, ptr_reg->type)) |
| + return -EINVAL; |
| + |
| switch (opcode) { |
| case BPF_ADD: |
| /* We can take a fixed offset as long as it doesn't overflow |
| @@ -1984,6 +2023,9 @@ static int adjust_ptr_min_max_vals(struc |
| return -EACCES; |
| } |
| |
| + if (!check_reg_sane_offset(env, dst_reg, ptr_reg->type)) |
| + return -EINVAL; |
| + |
| __update_reg_bounds(dst_reg); |
| __reg_deduce_bounds(dst_reg); |
| __reg_bound_offset(dst_reg); |
| @@ -2013,6 +2055,12 @@ static int adjust_scalar_min_max_vals(st |
| src_known = tnum_is_const(src_reg.var_off); |
| dst_known = tnum_is_const(dst_reg->var_off); |
| |
| + if (!src_known && |
| + opcode != BPF_ADD && opcode != BPF_SUB && opcode != BPF_AND) { |
| + __mark_reg_unknown(dst_reg); |
| + return 0; |
| + } |
| + |
| switch (opcode) { |
| case BPF_ADD: |
| if (signed_add_overflows(dst_reg->smin_value, smin_val) || |