bpf/verifier: track signed and unsigned min/max values
Allows us to, sometimes, combine information from a signed check of one bound and an unsigned check of the other. We now track the full range of possible values, rather than restricting ourselves to [0, 1<<30) and considering anything beyond that as unknown. While this is probably not necessary, it makes the code more straightforward and symmetrical between signed and unsigned bounds. Signed-off-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
f1174f77b5
commit
b03c9f9fdc
|
@ -11,11 +11,15 @@
|
||||||
#include <linux/filter.h> /* for MAX_BPF_STACK */
|
#include <linux/filter.h> /* for MAX_BPF_STACK */
|
||||||
#include <linux/tnum.h>
|
#include <linux/tnum.h>
|
||||||
|
|
||||||
/* Just some arbitrary values so we can safely do math without overflowing and
|
/* Maximum variable offset umax_value permitted when resolving memory accesses.
|
||||||
* are obviously wrong for any sort of memory access.
|
* 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_REGISTER_MAX_RANGE (1024 * 1024 * 1024)
|
*/
|
||||||
#define BPF_REGISTER_MIN_RANGE -1
|
#define BPF_MAX_VAR_OFF (1ULL << 31)
|
||||||
|
/* 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
|
||||||
|
|
||||||
struct bpf_reg_state {
|
struct bpf_reg_state {
|
||||||
enum bpf_reg_type type;
|
enum bpf_reg_type type;
|
||||||
|
@ -36,7 +40,7 @@ struct bpf_reg_state {
|
||||||
* came from, when one is tested for != NULL.
|
* came from, when one is tested for != NULL.
|
||||||
*/
|
*/
|
||||||
u32 id;
|
u32 id;
|
||||||
/* These three fields must be last. See states_equal() */
|
/* These five fields must be last. See states_equal() */
|
||||||
/* For scalar types (SCALAR_VALUE), this represents our knowledge of
|
/* For scalar types (SCALAR_VALUE), this represents our knowledge of
|
||||||
* the actual value.
|
* the actual value.
|
||||||
* For pointer types, this represents the variable part of the offset
|
* For pointer types, this represents the variable part of the offset
|
||||||
|
@ -49,9 +53,10 @@ struct bpf_reg_state {
|
||||||
* These refer to the same value as var_off, not necessarily the actual
|
* These refer to the same value as var_off, not necessarily the actual
|
||||||
* contents of the register.
|
* contents of the register.
|
||||||
*/
|
*/
|
||||||
s64 min_value;
|
s64 smin_value; /* minimum possible (s64)value */
|
||||||
u64 max_value;
|
s64 smax_value; /* maximum possible (s64)value */
|
||||||
bool value_from_signed;
|
u64 umin_value; /* minimum possible (u64)value */
|
||||||
|
u64 umax_value; /* maximum possible (u64)value */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bpf_stack_slot_type {
|
enum bpf_stack_slot_type {
|
||||||
|
|
|
@ -17,6 +17,8 @@ struct tnum {
|
||||||
struct tnum tnum_const(u64 value);
|
struct tnum tnum_const(u64 value);
|
||||||
/* A completely unknown value */
|
/* A completely unknown value */
|
||||||
extern const struct tnum tnum_unknown;
|
extern const struct tnum tnum_unknown;
|
||||||
|
/* A value that's unknown except that @min <= value <= @max */
|
||||||
|
struct tnum tnum_range(u64 min, u64 max);
|
||||||
|
|
||||||
/* Arithmetic and logical ops */
|
/* Arithmetic and logical ops */
|
||||||
/* Shift a tnum left (by a fixed shift) */
|
/* Shift a tnum left (by a fixed shift) */
|
||||||
|
|
|
@ -17,6 +17,22 @@ struct tnum tnum_const(u64 value)
|
||||||
return TNUM(value, 0);
|
return TNUM(value, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct tnum tnum_range(u64 min, u64 max)
|
||||||
|
{
|
||||||
|
u64 chi = min ^ max, delta;
|
||||||
|
u8 bits = fls64(chi);
|
||||||
|
|
||||||
|
/* special case, needed because 1ULL << 64 is undefined */
|
||||||
|
if (bits > 63)
|
||||||
|
return tnum_unknown;
|
||||||
|
/* e.g. if chi = 4, bits = 3, delta = (1<<3) - 1 = 7.
|
||||||
|
* if chi = 0, bits = 0, delta = (1<<0) - 1 = 0, so we return
|
||||||
|
* constant min (since min == max).
|
||||||
|
*/
|
||||||
|
delta = (1ULL << bits) - 1;
|
||||||
|
return TNUM(min & ~delta, delta);
|
||||||
|
}
|
||||||
|
|
||||||
struct tnum tnum_lshift(struct tnum a, u8 shift)
|
struct tnum tnum_lshift(struct tnum a, u8 shift)
|
||||||
{
|
{
|
||||||
return TNUM(a.value << shift, a.mask << shift);
|
return TNUM(a.value << shift, a.mask << shift);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user