summaryrefslogtreecommitdiff
path: root/kernel/bpf
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/bpf')
-rw-r--r--kernel/bpf/verifier.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 743fc2f3c24b..f8e6b40c2121 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/cpumask.h>
#include <linux/cnum.h>
+#include <linux/gcd.h>
#include <linux/bpf_mem_alloc.h>
#include <net/xdp.h>
#include <linux/trace_events.h>
@@ -17291,6 +17292,51 @@ int bpf_set_reg_range(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
return reg_bounds_sanity_check(env, reg, "bpf_set_reg_range");
}
+/*
+ * Checks if pointer and packet metadata matches between two registers.
+ * For use with bpf_reg_union().
+ */
+bool bpf_reg_union_compatible(const struct bpf_reg_state *a, const struct bpf_reg_state *b)
+{
+ if (a->type != b->type)
+ return false;
+ if (a->type == SCALAR_VALUE)
+ return true;
+ return !memcmp(a, b, offsetof(struct bpf_reg_state, var_off)) &&
+ a->id == b->id && a->parent_id == b->parent_id && a->map_uid == b->map_uid;
+}
+
+/*
+ * Extends acc's scalar range to include src's scalar range.
+ * Caller must ensure that other kinds of metadata is compatible.
+ * Caller must clear acc's scalar ID.
+ */
+int bpf_reg_union(struct bpf_verifier_env *env, struct bpf_reg_state *acc,
+ const struct bpf_reg_state *src)
+{
+ u16 base, step;
+
+ if (!bpf_reg_union_compatible(acc, src)) {
+ verifier_bug(env, "union of incompatible registers");
+ return -EFAULT;
+ }
+ acc->r64 = cnum64_union(acc->r64, src->r64);
+ acc->r32 = cnum32_union(acc->r32, src->r32);
+ acc->var_off = tnum_union(acc->var_off, src->var_off);
+
+ /* Retain a common congruence if the bases agree modulo the gcd. */
+ step = gcd(acc->step, src->step);
+ base = acc->base % step;
+ if (base != src->base % step) {
+ reg_step_reset(acc);
+ } else {
+ acc->base = base;
+ acc->step = step;
+ }
+ reg_bounds_sync(acc);
+ return reg_bounds_sanity_check(env, acc, "bpf_reg_union");
+}
+
/* check validity of 32-bit and 64-bit arithmetic operations */
static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
{