#include <linux/poison.h>
#include <linux/module.h>
#include <linux/cpumask.h>
+#include <linux/cnum.h>
#include <linux/bpf_mem_alloc.h>
#include <net/xdp.h>
#include <linux/trace_events.h>
static void ___mark_reg_known(struct bpf_reg_state *reg, u64 imm)
{
reg->var_off = tnum_const(imm);
- reg_set_srange64(reg, (s64)imm, (s64)imm);
- reg_set_urange64(reg, imm, imm);
- reg_set_srange32(reg, (s32)imm, (s32)imm);
- reg_set_urange32(reg, (u32)imm, (u32)imm);
+ reg->r64 = cnum64_from_urange(imm, imm);
+ reg->r32 = cnum32_from_urange((u32)imm, (u32)imm);
}
/* Mark the unknown part of a register (variable offset or scalar value) as
static void __mark_reg32_known(struct bpf_reg_state *reg, u64 imm)
{
reg->var_off = tnum_const_subreg(reg->var_off, imm);
- reg_set_srange32(reg, (s32)imm, (s32)imm);
- reg_set_urange32(reg, (u32)imm, (u32)imm);
+ reg->r32 = cnum32_from_urange((u32)imm, (u32)imm);
}
/* Mark the 'variable offset' part of a register as zero. This should be
static void __mark_reg32_unbounded(struct bpf_reg_state *reg)
{
- reg_set_srange32(reg, S32_MIN, S32_MAX);
- reg_set_urange32(reg, 0, U32_MAX);
+ reg->r32 = CNUM32_UNBOUNDED;
}
-/* Reset the min/max bounds of a register */
-static void __mark_reg_unbounded(struct bpf_reg_state *reg)
+static void __mark_reg64_unbounded(struct bpf_reg_state *reg)
{
- reg_set_srange64(reg, S64_MIN, S64_MAX);
- reg_set_urange64(reg, 0, U64_MAX);
-
- __mark_reg32_unbounded(reg);
+ reg->r64 = CNUM64_UNBOUNDED;
}
-static void __mark_reg64_unbounded(struct bpf_reg_state *reg)
+/* Reset the min/max bounds of a register */
+static void __mark_reg_unbounded(struct bpf_reg_state *reg)
{
- reg_set_srange64(reg, S64_MIN, S64_MAX);
- reg_set_urange64(reg, 0, U64_MAX);
+ __mark_reg64_unbounded(reg);
+ __mark_reg32_unbounded(reg);
}
static void reset_reg64_and_tnum(struct bpf_reg_state *reg)
reg->var_off = tnum_unknown;
}
-static void __update_reg32_bounds(struct bpf_reg_state *reg)
+static struct cnum32 cnum32_from_tnum(struct tnum tnum)
{
- struct tnum var32_off = tnum_subreg(reg->var_off);
+ tnum = tnum_subreg(tnum);
+ if ((tnum.mask & S32_MIN) || (tnum.value & S32_MIN))
+ /* min signed is max(sign bit) | min(other bits) */
+ /* max signed is min(sign bit) | max(other bits) */
+ return cnum32_from_srange(tnum.value | (tnum.mask & S32_MIN),
+ tnum.value | (tnum.mask & S32_MAX));
+ else
+ return cnum32_from_urange(tnum.value, (tnum.value | tnum.mask));
+}
- reg_set_srange32(reg,
- /* min signed is max(sign bit) | min(other bits) */
- max_t(s32, reg_s32_min(reg), var32_off.value | (var32_off.mask & S32_MIN)),
- /* max signed is min(sign bit) | max(other bits) */
- min_t(s32, reg_s32_max(reg), var32_off.value | (var32_off.mask & S32_MAX)));
- reg_set_urange32(reg,
- max_t(u32, reg_u32_min(reg), (u32)var32_off.value),
- min(reg_u32_max(reg), (u32)(var32_off.value | var32_off.mask)));
+static struct cnum64 cnum64_from_tnum(struct tnum tnum)
+{
+ if ((tnum.mask & S64_MIN) || (tnum.value & S64_MIN))
+ /* min signed is max(sign bit) | min(other bits) */
+ /* max signed is min(sign bit) | max(other bits) */
+ return cnum64_from_srange(tnum.value | (tnum.mask & S64_MIN),
+ tnum.value | (tnum.mask & S64_MAX));
+ else
+ return cnum64_from_urange(tnum.value, (tnum.value | tnum.mask));
+}
+
+static void __update_reg32_bounds(struct bpf_reg_state *reg)
+{
+ cnum32_intersect_with(®->r32, cnum32_from_tnum(reg->var_off));
}
static void __update_reg64_bounds(struct bpf_reg_state *reg)
u64 tnum_next, tmax;
bool umin_in_tnum;
- /* min signed is max(sign bit) | min(other bits) */
- /* max signed is min(sign bit) | max(other bits) */
- reg_set_srange64(reg,
- max_t(s64, reg_smin(reg),
- reg->var_off.value | (reg->var_off.mask & S64_MIN)),
- min_t(s64, reg_smax(reg),
- reg->var_off.value | (reg->var_off.mask & S64_MAX)));
- reg_set_urange64(reg,
- max(reg_umin(reg), reg->var_off.value),
- min(reg_umax(reg),
- reg->var_off.value | reg->var_off.mask));
+ cnum64_intersect_with(®->r64, cnum64_from_tnum(reg->var_off));
/* Check if u64 and tnum overlap in a single value */
tnum_next = tnum_step(reg->var_off, reg_umin(reg));
__update_reg64_bounds(reg);
}
-/* Uses signed min/max values to inform unsigned, and vice-versa */
static void deduce_bounds_32_from_64(struct bpf_reg_state *reg)
{
- /* If upper 32 bits of u64/s64 range don't change, we can use lower 32
- * bits to improve our u32/s32 boundaries.
- *
- * E.g., the case where we have upper 32 bits as zero ([10, 20] in
- * u64) is pretty trivial, it's obvious that in u32 we'll also have
- * [10, 20] range. But this property holds for any 64-bit range as
- * long as upper 32 bits in that entire range of values stay the same.
- *
- * E.g., u64 range [0x10000000A, 0x10000000F] ([4294967306, 4294967311]
- * in decimal) has the same upper 32 bits throughout all the values in
- * that range. As such, lower 32 bits form a valid [0xA, 0xF] ([10, 15])
- * range.
- *
- * Note also, that [0xA, 0xF] is a valid range both in u32 and in s32,
- * following the rules outlined below about u64/s64 correspondence
- * (which equally applies to u32 vs s32 correspondence). In general it
- * depends on actual hexadecimal values of 32-bit range. They can form
- * only valid u32, or only valid s32 ranges in some cases.
- *
- * So we use all these insights to derive bounds for subregisters here.
- */
- if ((reg_umin(reg) >> 32) == (reg_umax(reg) >> 32)) {
- /* u64 to u32 casting preserves validity of low 32 bits as
- * a range, if upper 32 bits are the same
- */
- reg_set_urange32(reg,
- max_t(u32, reg_u32_min(reg), (u32)reg_umin(reg)),
- min_t(u32, reg_u32_max(reg), (u32)reg_umax(reg)));
-
- if ((s32)reg_umin(reg) <= (s32)reg_umax(reg)) {
- reg_set_srange32(reg,
- max_t(s32, reg_s32_min(reg), (s32)reg_umin(reg)),
- min_t(s32, reg_s32_max(reg), (s32)reg_umax(reg)));
- }
- }
- if ((reg_smin(reg) >> 32) == (reg_smax(reg) >> 32)) {
- /* low 32 bits should form a proper u32 range */
- if ((u32)reg_smin(reg) <= (u32)reg_smax(reg)) {
- reg_set_urange32(reg,
- max_t(u32, reg_u32_min(reg), (u32)reg_smin(reg)),
- min_t(u32, reg_u32_max(reg), (u32)reg_smax(reg)));
- }
- /* low 32 bits should form a proper s32 range */
- if ((s32)reg_smin(reg) <= (s32)reg_smax(reg)) {
- reg_set_srange32(reg,
- max_t(s32, reg_s32_min(reg), (s32)reg_smin(reg)),
- min_t(s32, reg_s32_max(reg), (s32)reg_smax(reg)));
- }
- }
- /* Special case where upper bits form a small sequence of two
- * sequential numbers (in 32-bit unsigned space, so 0xffffffff to
- * 0x00000000 is also valid), while lower bits form a proper s32 range
- * going from negative numbers to positive numbers. E.g., let's say we
- * have s64 range [-1, 1] ([0xffffffffffffffff, 0x0000000000000001]).
- * Possible s64 values are {-1, 0, 1} ({0xffffffffffffffff,
- * 0x0000000000000000, 0x00000000000001}). Ignoring upper 32 bits,
- * we still get a valid s32 range [-1, 1] ([0xffffffff, 0x00000001]).
- * Note that it doesn't have to be 0xffffffff going to 0x00000000 in
- * upper 32 bits. As a random example, s64 range
- * [0xfffffff0fffffff0; 0xfffffff100000010], forms a valid s32 range
- * [-16, 16] ([0xfffffff0; 0x00000010]) in its 32 bit subregister.
- */
- if ((u32)(reg_umin(reg) >> 32) + 1 == (u32)(reg_umax(reg) >> 32) &&
- (s32)reg_umin(reg) < 0 && (s32)reg_umax(reg) >= 0) {
- reg_set_srange32(reg,
- max_t(s32, reg_s32_min(reg), (s32)reg_umin(reg)),
- min_t(s32, reg_s32_max(reg), (s32)reg_umax(reg)));
- }
- if ((u32)(reg_smin(reg) >> 32) + 1 == (u32)(reg_smax(reg) >> 32) &&
- (s32)reg_smin(reg) < 0 && (s32)reg_smax(reg) >= 0) {
- reg_set_srange32(reg,
- max_t(s32, reg_s32_min(reg), (s32)reg_smin(reg)),
- min_t(s32, reg_s32_max(reg), (s32)reg_smax(reg)));
- }
-}
-
-static void deduce_bounds_32_from_32(struct bpf_reg_state *reg)
-{
- /* if u32 range forms a valid s32 range (due to matching sign bit),
- * try to learn from that
- */
- if ((s32)reg_u32_min(reg) <= (s32)reg_u32_max(reg)) {
- reg_set_srange32(reg,
- max_t(s32, reg_s32_min(reg), reg_u32_min(reg)),
- min_t(s32, reg_s32_max(reg), reg_u32_max(reg)));
- }
- /* If we cannot cross the sign boundary, then signed and unsigned bounds
- * are the same, so combine. This works even in the negative case, e.g.
- * -3 s<= x s<= -1 implies 0xf...fd u<= x u<= 0xf...ff.
- */
- if ((u32)reg_s32_min(reg) <= (u32)reg_s32_max(reg)) {
- reg_set_urange32(reg,
- max_t(u32, reg_s32_min(reg), reg_u32_min(reg)),
- min_t(u32, reg_s32_max(reg), reg_u32_max(reg)));
- } else {
- if (reg_u32_max(reg) < (u32)reg_s32_min(reg)) {
- /* See __reg64_deduce_bounds() for detailed explanation.
- * Refine ranges in the following situation:
- *
- * 0 U32_MAX
- * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
- * |----------------------------|----------------------------|
- * |xxxxx s32 range xxxxxxxxx] [xxxxxxx|
- * 0 S32_MAX S32_MIN -1
- */
- reg_set_srange32(reg, (s32)reg_u32_min(reg), reg_s32_max(reg));
- reg_set_urange32(reg,
- reg_u32_min(reg),
- min_t(u32, reg_u32_max(reg), reg_s32_max(reg)));
- } else if ((u32)reg_s32_max(reg) < reg_u32_min(reg)) {
- /*
- * 0 U32_MAX
- * | [xxxxxxxxxxxxxx u32 range xxxxxxxxxxxxxx] |
- * |----------------------------|----------------------------|
- * |xxxxxxxxx] [xxxxxxxxxxxx s32 range |
- * 0 S32_MAX S32_MIN -1
- */
- reg_set_srange32(reg, reg_s32_min(reg), (s32)reg_u32_max(reg));
- reg_set_urange32(reg,
- max_t(u32, reg_u32_min(reg), reg_s32_min(reg)),
- reg_u32_max(reg));
- }
- }
-}
-
-static void deduce_bounds_64_from_64(struct bpf_reg_state *reg)
-{
- /* If u64 range forms a valid s64 range (due to matching sign bit),
- * try to learn from that. Let's do a bit of ASCII art to see when
- * this is happening. Let's take u64 range first:
- *
- * 0 0x7fffffffffffffff 0x8000000000000000 U64_MAX
- * |-------------------------------|--------------------------------|
- *
- * Valid u64 range is formed when umin and umax are anywhere in the
- * range [0, U64_MAX], and umin <= umax. u64 case is simple and
- * straightforward. Let's see how s64 range maps onto the same range
- * of values, annotated below the line for comparison:
- *
- * 0 0x7fffffffffffffff 0x8000000000000000 U64_MAX
- * |-------------------------------|--------------------------------|
- * 0 S64_MAX S64_MIN -1
- *
- * So s64 values basically start in the middle and they are logically
- * contiguous to the right of it, wrapping around from -1 to 0, and
- * then finishing as S64_MAX (0x7fffffffffffffff) right before
- * S64_MIN. We can try drawing the continuity of u64 vs s64 values
- * more visually as mapped to sign-agnostic range of hex values.
- *
- * u64 start u64 end
- * _______________________________________________________________
- * / \
- * 0 0x7fffffffffffffff 0x8000000000000000 U64_MAX
- * |-------------------------------|--------------------------------|
- * 0 S64_MAX S64_MIN -1
- * / \
- * >------------------------------ ------------------------------->
- * s64 continues... s64 end s64 start s64 "midpoint"
- *
- * What this means is that, in general, we can't always derive
- * something new about u64 from any random s64 range, and vice versa.
- *
- * But we can do that in two particular cases. One is when entire
- * u64/s64 range is *entirely* contained within left half of the above
- * diagram or when it is *entirely* contained in the right half. I.e.:
- *
- * |-------------------------------|--------------------------------|
- * ^ ^ ^ ^
- * A B C D
- *
- * [A, B] and [C, D] are contained entirely in their respective halves
- * and form valid contiguous ranges as both u64 and s64 values. [A, B]
- * will be non-negative both as u64 and s64 (and in fact it will be
- * identical ranges no matter the signedness). [C, D] treated as s64
- * will be a range of negative values, while in u64 it will be
- * non-negative range of values larger than 0x8000000000000000.
- *
- * Now, any other range here can't be represented in both u64 and s64
- * simultaneously. E.g., [A, C], [A, D], [B, C], [B, D] are valid
- * contiguous u64 ranges, but they are discontinuous in s64. [B, C]
- * in s64 would be properly presented as [S64_MIN, C] and [B, S64_MAX],
- * for example. Similarly, valid s64 range [D, A] (going from negative
- * to positive values), would be two separate [D, U64_MAX] and [0, A]
- * ranges as u64. Currently reg_state can't represent two segments per
- * numeric domain, so in such situations we can only derive maximal
- * possible range ([0, U64_MAX] for u64, and [S64_MIN, S64_MAX] for s64).
- *
- * So we use these facts to derive umin/umax from smin/smax and vice
- * versa only if they stay within the same "half". This is equivalent
- * to checking sign bit: lower half will have sign bit as zero, upper
- * half have sign bit 1. Below in code we simplify this by just
- * casting umin/umax as smin/smax and checking if they form valid
- * range, and vice versa. Those are equivalent checks.
- */
- if ((s64)reg_umin(reg) <= (s64)reg_umax(reg)) {
- reg_set_srange64(reg,
- max_t(s64, reg_smin(reg), reg_umin(reg)),
- min_t(s64, reg_smax(reg), reg_umax(reg)));
- }
- /* If we cannot cross the sign boundary, then signed and unsigned bounds
- * are the same, so combine. This works even in the negative case, e.g.
- * -3 s<= x s<= -1 implies 0xf...fd u<= x u<= 0xf...ff.
- */
- if ((u64)reg_smin(reg) <= (u64)reg_smax(reg)) {
- reg_set_urange64(reg,
- max_t(u64, reg_smin(reg), reg_umin(reg)),
- min_t(u64, reg_smax(reg), reg_umax(reg)));
- } else {
- /* If the s64 range crosses the sign boundary, then it's split
- * between the beginning and end of the U64 domain. In that
- * case, we can derive new bounds if the u64 range overlaps
- * with only one end of the s64 range.
- *
- * In the following example, the u64 range overlaps only with
- * positive portion of the s64 range.
- *
- * 0 U64_MAX
- * | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] |
- * |----------------------------|----------------------------|
- * |xxxxx s64 range xxxxxxxxx] [xxxxxxx|
- * 0 S64_MAX S64_MIN -1
- *
- * We can thus derive the following new s64 and u64 ranges.
- *
- * 0 U64_MAX
- * | [xxxxxx u64 range xxxxx] |
- * |----------------------------|----------------------------|
- * | [xxxxxx s64 range xxxxx] |
- * 0 S64_MAX S64_MIN -1
- *
- * If they overlap in two places, we can't derive anything
- * because reg_state can't represent two ranges per numeric
- * domain.
- *
- * 0 U64_MAX
- * | [xxxxxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxxxxx] |
- * |----------------------------|----------------------------|
- * |xxxxx s64 range xxxxxxxxx] [xxxxxxxxxx|
- * 0 S64_MAX S64_MIN -1
- *
- * The first condition below corresponds to the first diagram
- * above.
- */
- if (reg_umax(reg) < (u64)reg_smin(reg)) {
- reg_set_srange64(reg, (s64)reg_umin(reg), reg_smax(reg));
- reg_set_urange64(reg, reg_umin(reg), min_t(u64, reg_umax(reg), reg_smax(reg)));
- } else if ((u64)reg_smax(reg) < reg_umin(reg)) {
- /* This second condition considers the case where the u64 range
- * overlaps with the negative portion of the s64 range:
- *
- * 0 U64_MAX
- * | [xxxxxxxxxxxxxx u64 range xxxxxxxxxxxxxx] |
- * |----------------------------|----------------------------|
- * |xxxxxxxxx] [xxxxxxxxxxxx s64 range |
- * 0 S64_MAX S64_MIN -1
- */
- reg_set_srange64(reg, reg_smin(reg), (s64)reg_umax(reg));
- reg_set_urange64(reg, max_t(u64, reg_umin(reg), reg_smin(reg)), reg_umax(reg));
- }
- }
+ cnum32_intersect_with(®->r32, cnum32_from_cnum64(reg->r64));
}
static void deduce_bounds_64_from_32(struct bpf_reg_state *reg)
{
- /* Try to tighten 64-bit bounds from 32-bit knowledge, using 32-bit
- * values on both sides of 64-bit range in hope to have tighter range.
- * E.g., if r1 is [0x1'00000000, 0x3'80000000], and we learn from
- * 32-bit signed > 0 operation that s32 bounds are now [1; 0x7fffffff].
- * With this, we can substitute 1 as low 32-bits of _low_ 64-bit bound
- * (0x100000000 -> 0x100000001) and 0x7fffffff as low 32-bits of
- * _high_ 64-bit bound (0x380000000 -> 0x37fffffff) and arrive at a
- * better overall bounds for r1 as [0x1'000000001; 0x3'7fffffff].
- * We just need to make sure that derived bounds we are intersecting
- * with are well-formed ranges in respective s64 or u64 domain, just
- * like we do with similar kinds of 32-to-64 or 64-to-32 adjustments.
- */
- __u64 new_umin, new_umax;
- __s64 new_smin, new_smax;
-
- /* u32 -> u64 tightening, it's always well-formed */
- new_umin = (reg_umin(reg) & ~0xffffffffULL) | reg_u32_min(reg);
- new_umax = (reg_umax(reg) & ~0xffffffffULL) | reg_u32_max(reg);
- reg_set_urange64(reg,
- max_t(u64, reg_umin(reg), new_umin),
- min_t(u64, reg_umax(reg), new_umax));
- /* u32 -> s64 tightening, u32 range embedded into s64 preserves range validity */
- new_smin = (reg_smin(reg) & ~0xffffffffULL) | reg_u32_min(reg);
- new_smax = (reg_smax(reg) & ~0xffffffffULL) | reg_u32_max(reg);
- reg_set_srange64(reg,
- max_t(s64, reg_smin(reg), new_smin),
- min_t(s64, reg_smax(reg), new_smax));
-
- /* Here we would like to handle a special case after sign extending load,
- * when upper bits for a 64-bit range are all 1s or all 0s.
- *
- * Upper bits are all 1s when register is in a range:
- * [0xffff_ffff_0000_0000, 0xffff_ffff_ffff_ffff]
- * Upper bits are all 0s when register is in a range:
- * [0x0000_0000_0000_0000, 0x0000_0000_ffff_ffff]
- * Together this forms are continuous range:
- * [0xffff_ffff_0000_0000, 0x0000_0000_ffff_ffff]
- *
- * Now, suppose that register range is in fact tighter:
- * [0xffff_ffff_8000_0000, 0x0000_0000_ffff_ffff] (R)
- * Also suppose that it's 32-bit range is positive,
- * meaning that lower 32-bits of the full 64-bit register
- * are in the range:
- * [0x0000_0000, 0x7fff_ffff] (W)
- *
- * If this happens, then any value in a range:
- * [0xffff_ffff_0000_0000, 0xffff_ffff_7fff_ffff]
- * is smaller than a lowest bound of the range (R):
- * 0xffff_ffff_8000_0000
- * which means that upper bits of the full 64-bit register
- * can't be all 1s, when lower bits are in range (W).
- *
- * Note that:
- * - 0xffff_ffff_8000_0000 == (s64)S32_MIN
- * - 0x0000_0000_7fff_ffff == (s64)S32_MAX
- * These relations are used in the conditions below.
- */
- if (reg_s32_min(reg) >= 0 && reg_smin(reg) >= S32_MIN && reg_smax(reg) <= S32_MAX) {
- reg_set_srange64(reg, reg_s32_min(reg), reg_s32_max(reg));
- reg_set_urange64(reg, reg_s32_min(reg), reg_s32_max(reg));
- reg->var_off = tnum_intersect(reg->var_off,
- tnum_range(reg_smin(reg), reg_smax(reg)));
- }
+ reg->r64 = cnum64_cnum32_intersect(reg->r64, reg->r32);
}
static void __reg_deduce_bounds(struct bpf_reg_state *reg)
{
- deduce_bounds_64_from_64(reg);
deduce_bounds_32_from_64(reg);
- deduce_bounds_32_from_32(reg);
deduce_bounds_64_from_32(reg);
}
__update_reg_bounds(reg);
}
-static bool range_bounds_violation(struct bpf_reg_state *reg)
-{
- return (reg_umin(reg) > reg_umax(reg) || reg_smin(reg) > reg_smax(reg) ||
- reg_u32_min(reg) > reg_u32_max(reg) ||
- reg_s32_min(reg) > reg_s32_max(reg));
-}
-
static bool const_tnum_range_mismatch(struct bpf_reg_state *reg)
{
- u64 uval = reg->var_off.value;
- s64 sval = (s64)uval;
-
if (!tnum_is_const(reg->var_off))
return false;
- return reg_umin(reg) != uval || reg_umax(reg) != uval ||
- reg_smin(reg) != sval || reg_smax(reg) != sval;
+ return !cnum64_is_const(reg->r64) || reg->r64.base != reg->var_off.value;
}
static bool const_tnum_range_mismatch_32(struct bpf_reg_state *reg)
{
- u32 uval32 = tnum_subreg(reg->var_off).value;
- s32 sval32 = (s32)uval32;
-
if (!tnum_subreg_is_const(reg->var_off))
return false;
- return reg_u32_min(reg) != uval32 || reg_u32_max(reg) != uval32 ||
- reg_s32_min(reg) != sval32 || reg_s32_max(reg) != sval32;
+ return !cnum32_is_const(reg->r32) || reg->r32.base != tnum_subreg(reg->var_off).value;
+}
+
+static bool range_bounds_violation(struct bpf_reg_state *reg)
+{
+ return cnum32_is_empty(reg->r32) || cnum64_is_empty(reg->r64);
}
static int reg_bounds_sanity_check(struct bpf_verifier_env *env,
return 0;
out:
- verifier_bug(env, "REG INVARIANTS VIOLATION (%s): %s u64=[%#llx, %#llx] "
- "s64=[%#llx, %#llx] u32=[%#x, %#x] s32=[%#x, %#x] var_off=(%#llx, %#llx)",
- ctx, msg, reg_umin(reg), reg_umax(reg),
- reg_smin(reg), reg_smax(reg),
- reg_u32_min(reg), reg_u32_max(reg),
- reg_s32_min(reg), reg_s32_max(reg),
+ verifier_bug(env, "REG INVARIANTS VIOLATION (%s): %s r64={.base=%#llx, .size=%#llx} "
+ "r32={.base=%#x, .size=%#x} var_off=(%#llx, %#llx)",
+ ctx, msg,
+ reg->r64.base, reg->r64.size,
+ reg->r32.base, reg->r32.size,
reg->var_off.value, reg->var_off.mask);
if (env->test_reg_invariants)
return -EFAULT;
return 0;
}
-static bool __reg32_bound_s64(s32 a)
-{
- return a >= 0 && a <= S32_MAX;
-}
-
-static void __reg_assign_32_into_64(struct bpf_reg_state *reg)
-{
- reg_set_urange64(reg, reg_u32_min(reg), reg_u32_max(reg));
-
- /* Attempt to pull 32-bit signed bounds into 64-bit bounds but must
- * be positive otherwise set to worse case bounds and refine later
- * from tnum.
- */
- if (__reg32_bound_s64(reg_s32_min(reg)) &&
- __reg32_bound_s64(reg_s32_max(reg)))
- reg_set_srange64(reg, reg_s32_min(reg), reg_s32_max(reg));
- else
- reg_set_srange64(reg, 0, U32_MAX);
-}
-
/* Mark a register as having a completely unknown (scalar) value. */
void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
{
static void zext_32_to_64(struct bpf_reg_state *reg)
{
reg->var_off = tnum_subreg(reg->var_off);
- __reg_assign_32_into_64(reg);
+ reg_set_urange64(reg, reg_u32_min(reg), reg_u32_max(reg));
}
/* truncate register to smaller size (in bytes)
/* fix arithmetic bounds */
mask = ((u64)1 << (size * 8)) - 1;
- if ((reg_umin(reg) & ~mask) == (reg_umax(reg) & ~mask)) {
+ if ((reg_umin(reg) & ~mask) == (reg_umax(reg) & ~mask))
reg_set_urange64(reg, reg_umin(reg) & mask, reg_umax(reg) & mask);
- } else {
+ else
reg_set_urange64(reg, 0, mask);
- }
- reg_set_srange64(reg, reg_umin(reg), reg_umax(reg));
/* If size is smaller than 32bit register the 32bit register
* values are also truncated so we push 64-bit bounds into
reg_set_srange64(reg, S32_MIN, S32_MAX);
reg_set_srange32(reg, S32_MIN, S32_MAX);
}
- reg_set_urange64(reg, 0, U64_MAX);
- reg_set_urange32(reg, 0, U32_MAX);
reg->var_off = tnum_unknown;
}
reg->var_off = tnum_const((s32)u64_cval);
u64_cval = reg->var_off.value;
- reg_set_srange64(reg, u64_cval, u64_cval);
- reg_set_urange64(reg, u64_cval, u64_cval);
- reg_set_srange32(reg, u64_cval, u64_cval);
- reg_set_urange32(reg, u64_cval, u64_cval);
+ reg->r64 = cnum64_from_urange(u64_cval, u64_cval);
+ reg->r32 = cnum32_from_urange((u32)u64_cval, (u32)u64_cval);
return;
}
/* both of s64_max/s64_min positive or negative */
if ((s64_max >= 0) == (s64_min >= 0)) {
reg_set_srange64(reg, s64_min, s64_max);
- reg_set_urange64(reg, s64_min, s64_max);
reg_set_srange32(reg, s64_min, s64_max);
- reg_set_urange32(reg, s64_min, s64_max);
reg->var_off = tnum_range(s64_min, s64_max);
return;
}
else
/* size == 2 */
reg_set_srange32(reg, S16_MIN, S16_MAX);
- reg_set_urange32(reg, 0, U32_MAX);
reg->var_off = tnum_subreg(tnum_unknown);
}
u32_val = reg->var_off.value;
reg_set_srange32(reg, u32_val, u32_val);
- reg_set_urange32(reg, u32_val, u32_val);
return;
}
if ((s32_min >= 0) == (s32_max >= 0)) {
reg_set_srange32(reg, s32_min, s32_max);
- reg_set_urange32(reg, (u32)s32_min, (u32)s32_max);
reg->var_off = tnum_subreg(tnum_range(s32_min, s32_max));
return;
}
case BPF_FUNC_get_smp_processor_id:
reg_set_urange64(ret_reg, 0, nr_cpu_ids - 1);
reg_set_urange32(ret_reg, 0, nr_cpu_ids - 1);
- reg_set_srange64(ret_reg, 0, nr_cpu_ids - 1);
- reg_set_srange32(ret_reg, 0, nr_cpu_ids - 1);
reg_bounds_sync(ret_reg);
break;
}
struct bpf_func_state *state = vstate->frame[vstate->curframe];
struct bpf_reg_state *regs = state->regs, *dst_reg;
bool known = tnum_is_const(off_reg->var_off);
- s64 smin_val = reg_smin(off_reg), smax_val = reg_smax(off_reg),
- smin_ptr = reg_smin(ptr_reg), smax_ptr = reg_smax(ptr_reg);
- u64 umin_val = reg_umin(off_reg), umax_val = reg_umax(off_reg),
- umin_ptr = reg_umin(ptr_reg), umax_ptr = reg_umax(ptr_reg);
+ s64 smin_val = reg_smin(off_reg), smax_val = reg_smax(off_reg);
+ u64 umin_val = reg_umin(off_reg), umax_val = reg_umax(off_reg);
struct bpf_sanitize_info info = {};
u8 opcode = BPF_OP(insn->code);
u32 dst = insn->dst_reg;
* added into the variable offset, and we copy the fixed offset
* from ptr_reg.
*/
- {
- s64 smin_res, smax_res;
- u64 umin_res, umax_res;
-
- if (check_add_overflow(smin_ptr, smin_val, &smin_res) ||
- check_add_overflow(smax_ptr, smax_val, &smax_res)) {
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
- } else {
- reg_set_srange64(dst_reg, smin_res, smax_res);
- }
- if (check_add_overflow(umin_ptr, umin_val, &umin_res) ||
- check_add_overflow(umax_ptr, umax_val, &umax_res)) {
- reg_set_urange64(dst_reg, 0, U64_MAX);
- } else {
- reg_set_urange64(dst_reg, umin_res, umax_res);
- }
- }
+ dst_reg->r64 = cnum64_add(ptr_reg->r64, off_reg->r64);
dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off);
dst_reg->raw = ptr_reg->raw;
if (reg_is_pkt_pointer(ptr_reg)) {
dst);
return -EACCES;
}
- /* A new variable offset is created. If the subtrahend is known
- * nonnegative, then any reg->range we had before is still good.
- */
- {
- s64 smin_res, smax_res;
-
- if (check_sub_overflow(smin_ptr, smax_val, &smin_res) ||
- check_sub_overflow(smax_ptr, smin_val, &smax_res)) {
- /* Overflow possible, we know nothing */
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
- } else {
- reg_set_srange64(dst_reg, smin_res, smax_res);
- }
- }
- if (umin_ptr < umax_val) {
- /* Overflow possible, we know nothing */
- reg_set_urange64(dst_reg, 0, U64_MAX);
- } else {
- /* Cannot overflow (as long as bounds are consistent) */
- reg_set_urange64(dst_reg, umin_ptr - umax_val, umax_ptr - umin_val);
- }
+ dst_reg->r64 = cnum64_add(ptr_reg->r64, cnum64_negate(off_reg->r64));
dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off);
dst_reg->raw = ptr_reg->raw;
if (reg_is_pkt_pointer(ptr_reg)) {
static void scalar32_min_max_add(struct bpf_reg_state *dst_reg,
struct bpf_reg_state *src_reg)
{
- s32 smin = reg_s32_min(dst_reg);
- s32 smax = reg_s32_max(dst_reg);
- u32 umin = reg_u32_min(dst_reg);
- u32 umax = reg_u32_max(dst_reg);
- u32 umin_val = reg_u32_min(src_reg);
- u32 umax_val = reg_u32_max(src_reg);
- bool min_overflow, max_overflow;
-
- if (check_add_overflow(smin, reg_s32_min(src_reg), &smin) ||
- check_add_overflow(smax, reg_s32_max(src_reg), &smax)) {
- smin = S32_MIN;
- smax = S32_MAX;
- }
-
- /* If either all additions overflow or no additions overflow, then
- * it is okay to set: dst_umin = dst_umin + src_umin, dst_umax =
- * dst_umax + src_umax. Otherwise (some additions overflow), set
- * the output bounds to unbounded.
- */
- min_overflow = check_add_overflow(umin, umin_val, &umin);
- max_overflow = check_add_overflow(umax, umax_val, &umax);
-
- if (!min_overflow && max_overflow) {
- umin = 0;
- umax = U32_MAX;
- }
-
- reg_set_srange32(dst_reg, smin, smax);
- reg_set_urange32(dst_reg, umin, umax);
+ dst_reg->r32 = cnum32_add(dst_reg->r32, src_reg->r32);
}
static void scalar_min_max_add(struct bpf_reg_state *dst_reg,
struct bpf_reg_state *src_reg)
{
- s64 smin = reg_smin(dst_reg);
- s64 smax = reg_smax(dst_reg);
- u64 umin = reg_umin(dst_reg);
- u64 umax = reg_umax(dst_reg);
- u64 umin_val = reg_umin(src_reg);
- u64 umax_val = reg_umax(src_reg);
- bool min_overflow, max_overflow;
-
- if (check_add_overflow(smin, reg_smin(src_reg), &smin) ||
- check_add_overflow(smax, reg_smax(src_reg), &smax)) {
- smin = S64_MIN;
- smax = S64_MAX;
- }
-
- /* If either all additions overflow or no additions overflow, then
- * it is okay to set: dst_umin = dst_umin + src_umin, dst_umax =
- * dst_umax + src_umax. Otherwise (some additions overflow), set
- * the output bounds to unbounded.
- */
- min_overflow = check_add_overflow(umin, umin_val, &umin);
- max_overflow = check_add_overflow(umax, umax_val, &umax);
-
- if (!min_overflow && max_overflow) {
- umin = 0;
- umax = U64_MAX;
- }
-
- reg_set_srange64(dst_reg, smin, smax);
- reg_set_urange64(dst_reg, umin, umax);
+ dst_reg->r64 = cnum64_add(dst_reg->r64, src_reg->r64);
}
static void scalar32_min_max_sub(struct bpf_reg_state *dst_reg,
struct bpf_reg_state *src_reg)
{
- s32 smin = reg_s32_min(dst_reg);
- s32 smax = reg_s32_max(dst_reg);
- u32 umin = reg_u32_min(dst_reg);
- u32 umax = reg_u32_max(dst_reg);
- u32 umin_val = reg_u32_min(src_reg);
- u32 umax_val = reg_u32_max(src_reg);
- bool min_underflow, max_underflow;
-
- if (check_sub_overflow(smin, reg_s32_max(src_reg), &smin) ||
- check_sub_overflow(smax, reg_s32_min(src_reg), &smax)) {
- /* Overflow possible, we know nothing */
- smin = S32_MIN;
- smax = S32_MAX;
- }
-
- /* If either all subtractions underflow or no subtractions
- * underflow, it is okay to set: dst_umin = dst_umin - src_umax,
- * dst_umax = dst_umax - src_umin. Otherwise (some subtractions
- * underflow), set the output bounds to unbounded.
- */
- min_underflow = check_sub_overflow(umin, umax_val, &umin);
- max_underflow = check_sub_overflow(umax, umin_val, &umax);
-
- if (min_underflow && !max_underflow) {
- umin = 0;
- umax = U32_MAX;
- }
-
- reg_set_srange32(dst_reg, smin, smax);
- reg_set_urange32(dst_reg, umin, umax);
+ dst_reg->r32 = cnum32_add(dst_reg->r32, cnum32_negate(src_reg->r32));
}
static void scalar_min_max_sub(struct bpf_reg_state *dst_reg,
struct bpf_reg_state *src_reg)
{
- s64 smin = reg_smin(dst_reg);
- s64 smax = reg_smax(dst_reg);
- u64 umin = reg_umin(dst_reg);
- u64 umax = reg_umax(dst_reg);
- u64 umin_val = reg_umin(src_reg);
- u64 umax_val = reg_umax(src_reg);
- bool min_underflow, max_underflow;
-
- if (check_sub_overflow(smin, reg_smax(src_reg), &smin) ||
- check_sub_overflow(smax, reg_smin(src_reg), &smax)) {
- /* Overflow possible, we know nothing */
- smin = S64_MIN;
- smax = S64_MAX;
- }
-
- /* If either all subtractions underflow or no subtractions
- * underflow, it is okay to set: dst_umin = dst_umin - src_umax,
- * dst_umax = dst_umax - src_umin. Otherwise (some subtractions
- * underflow), set the output bounds to unbounded.
- */
- min_underflow = check_sub_overflow(umin, umax_val, &umin);
- max_underflow = check_sub_overflow(umax, umin_val, &umax);
-
- if (min_underflow && !max_underflow) {
- umin = 0;
- umax = U64_MAX;
- }
-
- reg_set_srange64(dst_reg, smin, smax);
- reg_set_urange64(dst_reg, umin, umax);
+ dst_reg->r64 = cnum64_add(dst_reg->r64, cnum64_negate(src_reg->r64));
}
static void scalar32_min_max_mul(struct bpf_reg_state *dst_reg,
smax = max_array(tmp_prod, 4);
}
- reg_set_srange32(dst_reg, smin, smax);
- reg_set_urange32(dst_reg, umin, umax);
+ dst_reg->r32 = cnum32_intersect(cnum32_from_urange(umin, umax),
+ cnum32_from_srange(smin, smax));
}
static void scalar_min_max_mul(struct bpf_reg_state *dst_reg,
smax = max_array(tmp_prod, 4);
}
- reg_set_srange64(dst_reg, smin, smax);
- reg_set_urange64(dst_reg, umin, umax);
+ dst_reg->r64 = cnum64_intersect(cnum64_from_urange(umin, umax),
+ cnum64_from_srange(smin, smax));
}
static void scalar32_min_max_udiv(struct bpf_reg_state *dst_reg,
reg_u32_max(dst_reg) / src_val);
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
reset_reg64_and_tnum(dst_reg);
}
div64_u64(reg_umax(dst_reg), src_val));
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
reset_reg32_and_tnum(dst_reg);
}
reset:
reg_set_srange32(dst_reg, smin, smax);
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_urange32(dst_reg, 0, U32_MAX);
reset_reg64_and_tnum(dst_reg);
}
reset:
reg_set_srange64(dst_reg, smin, smax);
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_urange64(dst_reg, 0, U64_MAX);
reset_reg32_and_tnum(dst_reg);
}
reg_set_urange32(dst_reg, 0, min(reg_u32_max(dst_reg), res_max));
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
reset_reg64_and_tnum(dst_reg);
}
reg_set_urange64(dst_reg, 0, min(reg_umax(dst_reg), res_max));
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
reset_reg32_and_tnum(dst_reg);
}
}
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_urange32(dst_reg, 0, U32_MAX);
reset_reg64_and_tnum(dst_reg);
}
}
/* Reset other ranges/tnum to unbounded/unknown. */
- reg_set_urange64(dst_reg, 0, U64_MAX);
reset_reg32_and_tnum(dst_reg);
}
/* We get our minimum from the var_off, since that's inherently
* bitwise. Our maximum is the minimum of the operands' maxima.
*/
- reg_set_urange32(dst_reg, var32_off.value, min(reg_u32_max(dst_reg), umax_val));
-
- /* Safe to set s32 bounds by casting u32 result into s32 when u32
- * doesn't cross sign boundary. Otherwise set s32 bounds to unbounded.
- */
- if ((s32)reg_u32_min(dst_reg) <= (s32)reg_u32_max(dst_reg))
- reg_set_srange32(dst_reg, reg_u32_min(dst_reg), reg_u32_max(dst_reg));
- else
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
+ reg_set_urange32(dst_reg,
+ var32_off.value,
+ min(reg_u32_max(dst_reg), umax_val));
}
static void scalar_min_max_and(struct bpf_reg_state *dst_reg,
/* We get our minimum from the var_off, since that's inherently
* bitwise. Our maximum is the minimum of the operands' maxima.
*/
- reg_set_urange64(dst_reg, dst_reg->var_off.value, min(reg_umax(dst_reg), umax_val));
+ reg_set_urange64(dst_reg,
+ dst_reg->var_off.value,
+ min(reg_umax(dst_reg), umax_val));
- /* Safe to set s64 bounds by casting u64 result into s64 when u64
- * doesn't cross sign boundary. Otherwise set s64 bounds to unbounded.
- */
- if ((s64)reg_umin(dst_reg) <= (s64)reg_umax(dst_reg))
- reg_set_srange64(dst_reg, reg_umin(dst_reg), reg_umax(dst_reg));
- else
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
/* We may learn something more from the var_off */
__update_reg_bounds(dst_reg);
}
/* We get our maximum from the var_off, and our minimum is the
* maximum of the operands' minima
*/
- reg_set_urange32(dst_reg, max(reg_u32_min(dst_reg), umin_val),
+ reg_set_urange32(dst_reg,
+ max(reg_u32_min(dst_reg), umin_val),
var32_off.value | var32_off.mask);
-
- /* Safe to set s32 bounds by casting u32 result into s32 when u32
- * doesn't cross sign boundary. Otherwise set s32 bounds to unbounded.
- */
- if ((s32)reg_u32_min(dst_reg) <= (s32)reg_u32_max(dst_reg))
- reg_set_srange32(dst_reg, reg_u32_min(dst_reg), reg_u32_max(dst_reg));
- else
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
}
static void scalar_min_max_or(struct bpf_reg_state *dst_reg,
/* We get our maximum from the var_off, and our minimum is the
* maximum of the operands' minima
*/
- reg_set_urange64(dst_reg, max(reg_umin(dst_reg), umin_val),
+ reg_set_urange64(dst_reg,
+ max(reg_umin(dst_reg), umin_val),
dst_reg->var_off.value | dst_reg->var_off.mask);
- /* Safe to set s64 bounds by casting u64 result into s64 when u64
- * doesn't cross sign boundary. Otherwise set s64 bounds to unbounded.
- */
- if ((s64)reg_umin(dst_reg) <= (s64)reg_umax(dst_reg))
- reg_set_srange64(dst_reg, reg_umin(dst_reg), reg_umax(dst_reg));
- else
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
/* We may learn something more from the var_off */
__update_reg_bounds(dst_reg);
}
/* We get both minimum and maximum from the var32_off. */
reg_set_urange32(dst_reg, var32_off.value, var32_off.value | var32_off.mask);
-
- /* Safe to set s32 bounds by casting u32 result into s32 when u32
- * doesn't cross sign boundary. Otherwise set s32 bounds to unbounded.
- */
- if ((s32)reg_u32_min(dst_reg) <= (s32)reg_u32_max(dst_reg))
- reg_set_srange32(dst_reg, reg_u32_min(dst_reg), reg_u32_max(dst_reg));
- else
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
}
static void scalar_min_max_xor(struct bpf_reg_state *dst_reg,
}
/* We get both minimum and maximum from the var_off. */
- reg_set_urange64(dst_reg, dst_reg->var_off.value,
+ reg_set_urange64(dst_reg,
+ dst_reg->var_off.value,
dst_reg->var_off.value | dst_reg->var_off.mask);
-
- /* Safe to set s64 bounds by casting u64 result into s64 when u64
- * doesn't cross sign boundary. Otherwise set s64 bounds to unbounded.
- */
- if ((s64)reg_umin(dst_reg) <= (s64)reg_umax(dst_reg))
- reg_set_srange64(dst_reg, reg_umin(dst_reg), reg_umax(dst_reg));
- else
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
-
- __update_reg_bounds(dst_reg);
}
static void __scalar32_min_max_lsh(struct bpf_reg_state *dst_reg,
u64 umin_val, u64 umax_val)
{
- /* We lose all sign bit information (except what we can pick
- * up from var_off)
- */
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
/* If we might shift our top bit out, then we know nothing */
if (umax_val > 31 || reg_u32_max(dst_reg) > 1ULL << (31 - umax_val))
reg_set_urange32(dst_reg, 0, U32_MAX);
else
+ /* We lose all sign bit information (except what we can pick
+ * up from var_off)
+ */
reg_set_urange32(dst_reg, reg_u32_min(dst_reg) << umin_val,
reg_u32_max(dst_reg) << umax_val);
}
static void __scalar64_min_max_lsh(struct bpf_reg_state *dst_reg,
u64 umin_val, u64 umax_val)
{
+ struct cnum64 u, s;
+
/* Special case <<32 because it is a common compiler pattern to sign
* extend subreg by doing <<32 s>>32. smin/smax assignments are correct
* because s32 bounds don't flip sign when shifting to the left by
* 32bits.
*/
if (umin_val == 32 && umax_val == 32)
- reg_set_srange64(dst_reg, (s64)reg_s32_min(dst_reg) << 32,
- (s64)reg_s32_max(dst_reg) << 32);
+ s = cnum64_from_srange((s64)reg_s32_min(dst_reg) << 32,
+ (s64)reg_s32_max(dst_reg) << 32);
else
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
+ s = CNUM64_UNBOUNDED;
/* If we might shift our top bit out, then we know nothing */
if (reg_umax(dst_reg) > 1ULL << (63 - umax_val))
- reg_set_urange64(dst_reg, 0, U64_MAX);
+ u = CNUM64_UNBOUNDED;
else
- reg_set_urange64(dst_reg, reg_umin(dst_reg) << umin_val,
- reg_umax(dst_reg) << umax_val);
+ u = cnum64_from_urange(reg_umin(dst_reg) << umin_val,
+ reg_umax(dst_reg) << umax_val);
+
+ dst_reg->r64 = cnum64_intersect(u, s);
}
static void scalar_min_max_lsh(struct bpf_reg_state *dst_reg,
* and rely on inferring new ones from the unsigned bounds and
* var_off of the result.
*/
- reg_set_srange32(dst_reg, S32_MIN, S32_MAX);
dst_reg->var_off = tnum_rshift(subreg, umin_val);
reg_set_urange32(dst_reg, reg_u32_min(dst_reg) >> umax_val,
* and rely on inferring new ones from the unsigned bounds and
* var_off of the result.
*/
- reg_set_srange64(dst_reg, S64_MIN, S64_MAX);
dst_reg->var_off = tnum_rshift(dst_reg->var_off, umin_val);
reg_set_urange64(dst_reg, reg_umin(dst_reg) >> umax_val,
reg_umax(dst_reg) >> umin_val);
/* Upon reaching here, src_known is true and
* umax_val is equal to umin_val.
+ * Blow away the dst_reg umin_value/umax_value and rely on
+ * dst_reg var_off to refine the result.
*/
reg_set_srange32(dst_reg,
(u32)(((s32)reg_s32_min(dst_reg)) >> umin_val),
dst_reg->var_off = tnum_arshift(tnum_subreg(dst_reg->var_off), umin_val, 32);
- /* blow away the dst_reg umin_value/umax_value and rely on
- * dst_reg var_off to refine the result.
- */
- reg_set_urange32(dst_reg, 0, U32_MAX);
-
__mark_reg64_unbounded(dst_reg);
__update_reg32_bounds(dst_reg);
}
dst_reg->var_off = tnum_arshift(dst_reg->var_off, umin_val, 64);
- /* blow away the dst_reg umin_value/umax_value and rely on
- * dst_reg var_off to refine the result.
- */
- reg_set_urange64(dst_reg, 0, U64_MAX);
-
/* Its not easy to operate on alu32 bounds here because it depends
* on bits being shifted in from upper 32-bits. Take easy way out
* and mark unbounded so we can recalculate later from tnum.
switch (opcode) {
case BPF_JEQ:
if (is_jmp32) {
- reg_set_urange32(reg1, max(reg_u32_min(reg1), reg_u32_min(reg2)),
- min(reg_u32_max(reg1), reg_u32_max(reg2)));
- reg_set_srange32(reg1, max(reg_s32_min(reg1), reg_s32_min(reg2)),
- min(reg_s32_max(reg1), reg_s32_max(reg2)));
- reg_set_urange32(reg2, reg_u32_min(reg1), reg_u32_max(reg1));
- reg_set_srange32(reg2, reg_s32_min(reg1), reg_s32_max(reg1));
+ reg1->r32 = cnum32_intersect(reg1->r32, reg2->r32);
+ reg2->r32 = reg1->r32;
t = tnum_intersect(tnum_subreg(reg1->var_off), tnum_subreg(reg2->var_off));
reg1->var_off = tnum_with_subreg(reg1->var_off, t);
reg2->var_off = tnum_with_subreg(reg2->var_off, t);
} else {
- reg_set_urange64(reg1, max(reg_umin(reg1), reg_umin(reg2)),
- min(reg_umax(reg1), reg_umax(reg2)));
- reg_set_srange64(reg1, max(reg_smin(reg1), reg_smin(reg2)),
- min(reg_smax(reg1), reg_smax(reg2)));
- reg_set_urange64(reg2, reg_umin(reg1), reg_umax(reg1));
- reg_set_srange64(reg2, reg_smin(reg1), reg_smax(reg1));
+ reg1->r64 = cnum64_intersect(reg1->r64, reg2->r64);
+ reg2->r64 = reg1->r64;
reg1->var_off = tnum_intersect(reg1->var_off, reg2->var_off);
reg2->var_off = reg1->var_off;
*/
val = reg_const_value(reg2, is_jmp32);
if (is_jmp32) {
- /* u32_min is not equal to 0xffffffff at this point,
- * because otherwise u32_max is 0xffffffff as well,
- * in such a case both reg1 and reg2 would be constants,
- * jump would be predicted and regs_refine_cond_op()
- * wouldn't be called.
- *
- * Same reasoning works for all {u,s}{min,max}{32,64} cases
- * below.
- */
- if (reg_u32_min(reg1) == (u32)val)
- reg_set_urange32(reg1, reg_u32_min(reg1) + 1, reg_u32_max(reg1));
- if (reg_u32_max(reg1) == (u32)val)
- reg_set_urange32(reg1, reg_u32_min(reg1), reg_u32_max(reg1) - 1);
- if (reg_s32_min(reg1) == (s32)val)
- reg_set_srange32(reg1, reg_s32_min(reg1) + 1, reg_s32_max(reg1));
- if (reg_s32_max(reg1) == (s32)val)
- reg_set_srange32(reg1, reg_s32_min(reg1), reg_s32_max(reg1) - 1);
+ /* Complement of the range [val, val] as cnum32. */
+ cnum32_intersect_with(®1->r32, (struct cnum32){ val + 1, U32_MAX - 1 });
} else {
- if (reg_umin(reg1) == (u64)val)
- reg_set_urange64(reg1, reg_umin(reg1) + 1, reg_umax(reg1));
- if (reg_umax(reg1) == (u64)val)
- reg_set_urange64(reg1, reg_umin(reg1), reg_umax(reg1) - 1);
- if (reg_smin(reg1) == (s64)val)
- reg_set_srange64(reg1, reg_smin(reg1) + 1, reg_smax(reg1));
- if (reg_smax(reg1) == (s64)val)
- reg_set_srange64(reg1, reg_smin(reg1), reg_smax(reg1) - 1);
+ /* Complement of the range [val, val] as cnum64. */
+ cnum64_intersect_with(®1->r64, (struct cnum64){ val + 1, U64_MAX - 1 });
}
break;
case BPF_JSET:
break;
case BPF_JLE:
if (is_jmp32) {
- reg_set_urange32(reg1, reg_u32_min(reg1), min(reg_u32_max(reg1), reg_u32_max(reg2)));
- reg_set_urange32(reg2, max(reg_u32_min(reg1), reg_u32_min(reg2)), reg_u32_max(reg2));
+ cnum32_intersect_with_urange(®1->r32, 0, reg_u32_max(reg2));
+ cnum32_intersect_with_urange(®2->r32, reg_u32_min(reg1), U32_MAX);
} else {
- reg_set_urange64(reg1, reg_umin(reg1), min(reg_umax(reg1), reg_umax(reg2)));
- reg_set_urange64(reg2, max(reg_umin(reg1), reg_umin(reg2)), reg_umax(reg2));
+ cnum64_intersect_with_urange(®1->r64, 0, reg_umax(reg2));
+ cnum64_intersect_with_urange(®2->r64, reg_umin(reg1), U64_MAX);
}
break;
case BPF_JLT:
if (is_jmp32) {
- reg_set_urange32(reg1, reg_u32_min(reg1), min(reg_u32_max(reg1), reg_u32_max(reg2) - 1));
- reg_set_urange32(reg2, max(reg_u32_min(reg1) + 1, reg_u32_min(reg2)), reg_u32_max(reg2));
+ cnum32_intersect_with_urange(®1->r32, 0, reg_u32_max(reg2) - 1);
+ cnum32_intersect_with_urange(®2->r32, reg_u32_min(reg1) + 1, U32_MAX);
} else {
- reg_set_urange64(reg1, reg_umin(reg1), min(reg_umax(reg1), reg_umax(reg2) - 1));
- reg_set_urange64(reg2, max(reg_umin(reg1) + 1, reg_umin(reg2)), reg_umax(reg2));
+ cnum64_intersect_with_urange(®1->r64, 0, reg_umax(reg2) - 1);
+ cnum64_intersect_with_urange(®2->r64, reg_umin(reg1) + 1, U64_MAX);
}
break;
case BPF_JSLE:
if (is_jmp32) {
- reg_set_srange32(reg1, reg_s32_min(reg1), min(reg_s32_max(reg1), reg_s32_max(reg2)));
- reg_set_srange32(reg2, max(reg_s32_min(reg1), reg_s32_min(reg2)), reg_s32_max(reg2));
+ cnum32_intersect_with_srange(®1->r32, S32_MIN, reg_s32_max(reg2));
+ cnum32_intersect_with_srange(®2->r32, reg_s32_min(reg1), S32_MAX);
} else {
- reg_set_srange64(reg1, reg_smin(reg1), min(reg_smax(reg1), reg_smax(reg2)));
- reg_set_srange64(reg2, max(reg_smin(reg1), reg_smin(reg2)), reg_smax(reg2));
+ cnum64_intersect_with_srange(®1->r64, S64_MIN, reg_smax(reg2));
+ cnum64_intersect_with_srange(®2->r64, reg_smin(reg1), S64_MAX);
}
break;
case BPF_JSLT:
if (is_jmp32) {
- reg_set_srange32(reg1, reg_s32_min(reg1), min(reg_s32_max(reg1), reg_s32_max(reg2) - 1));
- reg_set_srange32(reg2, max(reg_s32_min(reg1) + 1, reg_s32_min(reg2)), reg_s32_max(reg2));
+ cnum32_intersect_with_srange(®1->r32, S32_MIN, reg_s32_max(reg2) - 1);
+ cnum32_intersect_with_srange(®2->r32, reg_s32_min(reg1) + 1, S32_MAX);
} else {
- reg_set_srange64(reg1, reg_smin(reg1), min(reg_smax(reg1), reg_smax(reg2) - 1));
- reg_set_srange64(reg2, max(reg_smin(reg1) + 1, reg_smin(reg2)), reg_smax(reg2));
+ cnum64_intersect_with_srange(®1->r64, S64_MIN, reg_smax(reg2) - 1);
+ cnum64_intersect_with_srange(®2->r64, reg_smin(reg1) + 1, S64_MAX);
}
break;
default: