bpf/verifier: rework value tracking
Unifies adjusted and unadjusted register value types (e.g. FRAME_POINTER is now just a PTR_TO_STACK with zero offset). Tracks value alignment by means of tracking known & unknown bits. This also replaces the 'reg->imm' (leading zero bits) calculations for (what were) UNKNOWN_VALUEs. If pointer leaks are allowed, and adjust_ptr_min_max_vals returns -EACCES, treat the pointer as an unknown scalar and try again, because we might be able to conclude something about the result (e.g. pointer & 0x40 is either 0 or 0x40). Verifier hooks in the netronome/nfp driver were changed to match the new data structures. Signed-off-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
e1cb90f2b8
commit
f1174f77b5
@ -1,6 +1,6 @@
|
||||
obj-y := core.o
|
||||
|
||||
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
|
||||
ifeq ($(CONFIG_NET),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
|
||||
|
164
kernel/bpf/tnum.c
Normal file
164
kernel/bpf/tnum.c
Normal file
@ -0,0 +1,164 @@
|
||||
/* tnum: tracked (or tristate) numbers
|
||||
*
|
||||
* A tnum tracks knowledge about the bits of a value. Each bit can be either
|
||||
* known (0 or 1), or unknown (x). Arithmetic operations on tnums will
|
||||
* propagate the unknown bits such that the tnum result represents all the
|
||||
* possible results for possible values of the operands.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/tnum.h>
|
||||
|
||||
#define TNUM(_v, _m) (struct tnum){.value = _v, .mask = _m}
|
||||
/* A completely unknown value */
|
||||
const struct tnum tnum_unknown = { .value = 0, .mask = -1 };
|
||||
|
||||
struct tnum tnum_const(u64 value)
|
||||
{
|
||||
return TNUM(value, 0);
|
||||
}
|
||||
|
||||
struct tnum tnum_lshift(struct tnum a, u8 shift)
|
||||
{
|
||||
return TNUM(a.value << shift, a.mask << shift);
|
||||
}
|
||||
|
||||
struct tnum tnum_rshift(struct tnum a, u8 shift)
|
||||
{
|
||||
return TNUM(a.value >> shift, a.mask >> shift);
|
||||
}
|
||||
|
||||
struct tnum tnum_add(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 sm, sv, sigma, chi, mu;
|
||||
|
||||
sm = a.mask + b.mask;
|
||||
sv = a.value + b.value;
|
||||
sigma = sm + sv;
|
||||
chi = sigma ^ sv;
|
||||
mu = chi | a.mask | b.mask;
|
||||
return TNUM(sv & ~mu, mu);
|
||||
}
|
||||
|
||||
struct tnum tnum_sub(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 dv, alpha, beta, chi, mu;
|
||||
|
||||
dv = a.value - b.value;
|
||||
alpha = dv + a.mask;
|
||||
beta = dv - b.mask;
|
||||
chi = alpha ^ beta;
|
||||
mu = chi | a.mask | b.mask;
|
||||
return TNUM(dv & ~mu, mu);
|
||||
}
|
||||
|
||||
struct tnum tnum_and(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 alpha, beta, v;
|
||||
|
||||
alpha = a.value | a.mask;
|
||||
beta = b.value | b.mask;
|
||||
v = a.value & b.value;
|
||||
return TNUM(v, alpha & beta & ~v);
|
||||
}
|
||||
|
||||
struct tnum tnum_or(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 v, mu;
|
||||
|
||||
v = a.value | b.value;
|
||||
mu = a.mask | b.mask;
|
||||
return TNUM(v, mu & ~v);
|
||||
}
|
||||
|
||||
struct tnum tnum_xor(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 v, mu;
|
||||
|
||||
v = a.value ^ b.value;
|
||||
mu = a.mask | b.mask;
|
||||
return TNUM(v & ~mu, mu);
|
||||
}
|
||||
|
||||
/* half-multiply add: acc += (unknown * mask * value).
|
||||
* An intermediate step in the multiply algorithm.
|
||||
*/
|
||||
static struct tnum hma(struct tnum acc, u64 value, u64 mask)
|
||||
{
|
||||
while (mask) {
|
||||
if (mask & 1)
|
||||
acc = tnum_add(acc, TNUM(0, value));
|
||||
mask >>= 1;
|
||||
value <<= 1;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
struct tnum tnum_mul(struct tnum a, struct tnum b)
|
||||
{
|
||||
struct tnum acc;
|
||||
u64 pi;
|
||||
|
||||
pi = a.value * b.value;
|
||||
acc = hma(TNUM(pi, 0), a.mask, b.mask | b.value);
|
||||
return hma(acc, b.mask, a.value);
|
||||
}
|
||||
|
||||
/* Note that if a and b disagree - i.e. one has a 'known 1' where the other has
|
||||
* a 'known 0' - this will return a 'known 1' for that bit.
|
||||
*/
|
||||
struct tnum tnum_intersect(struct tnum a, struct tnum b)
|
||||
{
|
||||
u64 v, mu;
|
||||
|
||||
v = a.value | b.value;
|
||||
mu = a.mask & b.mask;
|
||||
return TNUM(v & ~mu, mu);
|
||||
}
|
||||
|
||||
struct tnum tnum_cast(struct tnum a, u8 size)
|
||||
{
|
||||
a.value &= (1ULL << (size * 8)) - 1;
|
||||
a.mask &= (1ULL << (size * 8)) - 1;
|
||||
return a;
|
||||
}
|
||||
|
||||
bool tnum_is_aligned(struct tnum a, u64 size)
|
||||
{
|
||||
if (!size)
|
||||
return true;
|
||||
return !((a.value | a.mask) & (size - 1));
|
||||
}
|
||||
|
||||
bool tnum_in(struct tnum a, struct tnum b)
|
||||
{
|
||||
if (b.mask & ~a.mask)
|
||||
return false;
|
||||
b.value &= ~a.mask;
|
||||
return a.value == b.value;
|
||||
}
|
||||
|
||||
int tnum_strn(char *str, size_t size, struct tnum a)
|
||||
{
|
||||
return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tnum_strn);
|
||||
|
||||
int tnum_sbin(char *str, size_t size, struct tnum a)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
for (n = 64; n; n--) {
|
||||
if (n < size) {
|
||||
if (a.mask & 1)
|
||||
str[n - 1] = 'x';
|
||||
else if (a.value & 1)
|
||||
str[n - 1] = '1';
|
||||
else
|
||||
str[n - 1] = '0';
|
||||
}
|
||||
a.mask >>= 1;
|
||||
a.value >>= 1;
|
||||
}
|
||||
str[min(size - 1, (size_t)64)] = 0;
|
||||
return 64;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user