Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b03c9f9f authored by Edward Cree's avatar Edward Cree Committed by David S. Miller
Browse files

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: default avatarEdward Cree <ecree@solarflare.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f1174f77
Loading
Loading
Loading
Loading
+14 −9
Original line number Diff line number Diff line
@@ -11,11 +11,15 @@
#include <linux/filter.h> /* for MAX_BPF_STACK */
#include <linux/tnum.h>

 /* Just some arbitrary values so we can safely do math without overflowing and
  * are obviously wrong for any sort of memory access.
/* Maximum variable offset umax_value permitted when resolving memory accesses.
 * 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 {
	enum bpf_reg_type type;
@@ -36,7 +40,7 @@ struct bpf_reg_state {
	 * came from, when one is tested for != NULL.
	 */
	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
	 * the actual value.
	 * 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
	 * contents of the register.
	 */
	s64 min_value;
	u64 max_value;
	bool value_from_signed;
	s64 smin_value; /* minimum possible (s64)value */
	s64 smax_value; /* maximum possible (s64)value */
	u64 umin_value; /* minimum possible (u64)value */
	u64 umax_value; /* maximum possible (u64)value */
};

enum bpf_stack_slot_type {
+2 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@ struct tnum {
struct tnum tnum_const(u64 value);
/* A completely unknown value */
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 */
/* Shift a tnum left (by a fixed shift) */
+16 −0
Original line number Diff line number Diff line
@@ -17,6 +17,22 @@ struct tnum tnum_const(u64 value)
	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)
{
	return TNUM(a.value << shift, a.mask << shift);
+429 −308

File changed.

Preview size limit exceeded, changes collapsed.