Implement the `BPF_MOD` and `BPF_XOR` ALU operations and reject
unknown BPF instructions before they reach the filter interpreter.
* libbpf/bpf_impl.c: Add `bpf_valid_insn` for validate BPF
instructions and implement BPF_MOD and BPF_XOR
---
libbpf/bpf_impl.c | 123 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 120 insertions(+), 3 deletions(-)
diff --git a/libbpf/bpf_impl.c b/libbpf/bpf_impl.c
index c78176c2..60980b43 100644
--- a/libbpf/bpf_impl.c
+++ b/libbpf/bpf_impl.c
@@ -288,6 +288,16 @@ load_byte:
A >>= X;
continue;
+ case BPF_ALU|BPF_MOD|BPF_X:
+ if (X == 0)
+ return 0;
+ A %= X;
+ continue;
+
+ case BPF_ALU|BPF_XOR|BPF_X:
+ A ^= X;
+ continue;
+
case BPF_ALU|BPF_ADD|BPF_K:
A += pc->k;
continue;
@@ -320,6 +330,14 @@ load_byte:
A >>= pc->k;
continue;
+ case BPF_ALU|BPF_MOD|BPF_K:
+ A %= pc->k;
+ continue;
+
+ case BPF_ALU|BPF_XOR|BPF_K:
+ A ^= pc->k;
+ continue;
+
case BPF_ALU|BPF_NEG:
A = -A;
continue;
@@ -337,6 +355,97 @@ load_byte:
return 0;
}
+/*
+ * Return 1 if 'code' is a valid BPF instruction.
+ */
+static int
+bpf_valid_insn (unsigned short code)
+{
+ switch (BPF_CLASS(code)) {
+ /*
+ * Check BPF_LD and BPF_LDX modes.
+ */
+ case BPF_LD:
+ case BPF_LDX:
+ if (BPF_SRC(code) != BPF_K)
+ return 0;
+ switch (BPF_MODE(code)) {
+ case BPF_IMM:
+ return BPF_SIZE (code) == BPF_W;
+ case BPF_ABS:
+ case BPF_IND:
+ return BPF_CLASS (code) == BPF_LD
+ && (BPF_SIZE (code) == BPF_W
+ || BPF_SIZE (code) == BPF_H
+ || BPF_SIZE (code) == BPF_B);
+ case BPF_MEM:
+ return BPF_SIZE (code) == BPF_W;
+ case BPF_LEN:
+ return BPF_SIZE (code) == BPF_W;
+ case BPF_MSH:
+ return BPF_CLASS (code) == BPF_LDX
+ && BPF_SIZE (code) == BPF_B;
+ default:
+ return 0;
+ }
+ case BPF_ST:
+ case BPF_STX:
+ return code == BPF_ST || code == BPF_STX;
+ case BPF_ALU:
+ switch (BPF_OP (code)) {
+ case BPF_ADD:
+ case BPF_SUB:
+ case BPF_MUL:
+ case BPF_DIV:
+ case BPF_OR:
+ case BPF_AND:
+ case BPF_LSH:
+ case BPF_RSH:
+ case BPF_MOD:
+ case BPF_XOR:
+ return BPF_SRC (code) == BPF_K || BPF_SRC (code) ==
BPF_X;
+ case BPF_NEG:
+ /* BPF_NEG has no second operand. */
+ return BPF_SRC (code) == BPF_K;
+ default:
+ return 0;
+ }
+ case BPF_JMP:
+ switch (BPF_OP (code)) {
+ case BPF_JA:
+ return BPF_SRC (code) == BPF_K;
+ case BPF_JEQ:
+ case BPF_JGT:
+ case BPF_JGE:
+ case BPF_JSET:
+ return BPF_SRC (code) == BPF_K || BPF_SRC (code) ==
BPF_X;
+ default:
+ return 0;
+ }
+ case BPF_RET:
+ if (code & 0xe0)
+ return 0;
+ switch (BPF_RVAL (code)) {
+ case BPF_K:
+ case BPF_A:
+ case BPF_MATCH_IMM:
+ return 1;
+ default:
+ return 0;
+ }
+ case BPF_MISC:
+ switch (BPF_MISCOP (code)) {
+ case BPF_TAX:
+ case BPF_TXA:
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
/*
* Return 1 if the 'f' is a valid filter program without a MATCH
* instruction. Return 2 if it is a valid filter program with a MATCH
@@ -363,11 +472,16 @@ bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
*/
for (i = 1; i < len; ++i) {
+ p = &f[i];
+ /*
+ * Check that the instruction is valid.
+ */
+ if (!bpf_valid_insn (p->code))
+ return 0;
/*
- * Check that that jumps are forward, and within
+ * Check that jumps are forward, and within
* the code block.
*/
- p = &f[i];
if (BPF_CLASS(p->code) == BPF_JMP) {
int from = i + 1;
@@ -388,11 +502,14 @@ bpf_validate(bpf_insn_t f, int bytes, bpf_insn_t *match)
return 0;
}
/*
- * Check for constant division by 0.
+ * Check for constant division or modulo by 0.
*/
if (p->code == (BPF_ALU|BPF_DIV|BPF_K) && p->k == 0) {
return 0;
}
+ if (p->code == (BPF_ALU|BPF_MOD|BPF_K) && p->k == 0) {
+ return 0;
+ }
/*
* Check for match instruction.
* Only one match instruction per filter is allowed.
--
2.55.0