Last active
March 4, 2022 12:32
-
-
Save mejedi/3984cb30a22d04fcb013b0855f9e4e71 to your computer and use it in GitHub Desktop.
No direct packet access in BPF program with just CAP_BPF?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ uname -r | |
5.13.0-1007-intel | |
$ gcc amain.c -o main | |
$ sudo setcap 39+eip main # CAP_BPF | |
$ ./main | |
Permission denied | |
0: (79) r2 = *(u64 *)(r1 +0) | |
1: (79) r3 = *(u64 *)(r1 +8) | |
2: (bf) r4 = r2 | |
3: (07) r4 += 2 | |
4: (2d) if r4 > r3 goto pc+4 | |
R3 pointer comparison prohibited | |
processed 5 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0 | |
$ sudo setcap 38,39+eip main # CAP_PERFMON,CAP_BPF | |
$ ./main | |
$ echo $? | |
0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <errno.h> | |
#include <linux/bpf.h> | |
#include <stdbool.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/syscall.h> | |
#include <unistd.h> | |
#include "bpf_insn.h" | |
static const struct bpf_insn prog[] = { | |
// BPF_PROG_TYPE_SK_REUSEPORT: gets a pointer to sk_reuseport_md (r1). | |
// Get packet data pointer (r2) and ensure length >= 2, goto Drop otherwise | |
BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, | |
offsetof(struct sk_reuseport_md, data)), | |
BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_1, | |
offsetof(struct sk_reuseport_md, data_end)), | |
BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), | |
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 2), | |
BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, /* Drop: */ +4), | |
// Ensure first 2 bytes are 0, goto Drop otherwise | |
BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_2, 0), | |
BPF_JMP_IMM(BPF_JNE, BPF_REG_4, 0, /* Drop: */ +2), | |
// return SK_PASS | |
BPF_MOV32_IMM(BPF_REG_0, SK_PASS), | |
BPF_EXIT_INSN(), | |
// Drop: return SK_DROP | |
BPF_MOV32_IMM(BPF_REG_0, SK_DROP), | |
BPF_EXIT_INSN() | |
}; | |
int main() { | |
char log_buf[4096] = {}; | |
union bpf_attr attr = { | |
.prog_type = BPF_PROG_TYPE_SK_REUSEPORT, | |
.insns = (uint64_t)prog, | |
.insn_cnt = sizeof(prog) / sizeof(prog[0]), | |
.license = (uint64_t)"GPL", | |
.log_buf = (uint64_t)log_buf, | |
.log_size = sizeof(log_buf), | |
.log_level = 1 | |
}; | |
if (syscall(SYS_bpf, BPF_PROG_LOAD, &attr, sizeof(attr)) == -1) { | |
fprintf(stderr, "%s\n%s", strerror(errno), log_buf); | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* SPDX-License-Identifier: GPL-2.0 */ | |
/* | |
* Linux Socket Filter Data Structures | |
*/ | |
/* a stripped-down include/linux/filter.h from kernel source tree */ | |
/* ArgX, context and stack frame pointer register positions. Note, | |
* Arg1, Arg2, Arg3, etc are used as argument mappings of function | |
* calls in BPF_CALL instruction. | |
*/ | |
#define BPF_REG_ARG1 BPF_REG_1 | |
#define BPF_REG_ARG2 BPF_REG_2 | |
#define BPF_REG_ARG3 BPF_REG_3 | |
#define BPF_REG_ARG4 BPF_REG_4 | |
#define BPF_REG_ARG5 BPF_REG_5 | |
#define BPF_REG_CTX BPF_REG_6 | |
#define BPF_REG_FP BPF_REG_10 | |
/* BPF program can access up to 512 bytes of stack space. */ | |
#define MAX_BPF_STACK 512 | |
/* Helper macros for filter block array initializers. */ | |
/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */ | |
#define BPF_ALU64_REG(OP, DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_ALU32_REG(OP, DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */ | |
#define BPF_ALU64_IMM(OP, DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_ALU32_IMM(OP, DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */ | |
#define BPF_ENDIAN(TYPE, DST, LEN) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_END | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = LEN }) | |
/* Short form of mov, dst_reg = src_reg */ | |
#define BPF_MOV64_REG(DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
#define BPF_MOV32_REG(DST, SRC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = 0 }) | |
/* Short form of mov, dst_reg = imm32 */ | |
#define BPF_MOV64_IMM(DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_MOV32_IMM(DST, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
/* Special form of mov32, used for doing explicit zero extension on dst. */ | |
#define BPF_ZEXT_REG(DST) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = DST, \ | |
.off = 0, \ | |
.imm = 1 }) | |
static inline bool insn_is_zext(const struct bpf_insn *insn) | |
{ | |
return insn->code == (BPF_ALU | BPF_MOV | BPF_X) && insn->imm == 1; | |
} | |
/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */ | |
#define BPF_LD_IMM64(DST, IMM) \ | |
BPF_LD_IMM64_RAW(DST, 0, IMM) | |
#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_DW | BPF_IMM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = (__s32) (IMM) }), \ | |
((struct bpf_insn) { \ | |
.code = 0, /* zero is reserved opcode */ \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = (__s32)(((__u64) (IMM)) >> 32) }) | |
/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */ | |
#define BPF_LD_MAP_FD(DST, MAP_FD) \ | |
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD) | |
/* Short form of mov based on type, BPF_X: dst_reg = src_reg, BPF_K: dst_reg = imm32 */ | |
#define BPF_MOV64_RAW(TYPE, DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU64 | BPF_MOV | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
#define BPF_MOV32_RAW(TYPE, DST, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ALU | BPF_MOV | BPF_SRC(TYPE), \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */ | |
#define BPF_LD_ABS(SIZE, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = IMM }) | |
/* Indirect packet access, R0 = *(uint *) (skb->data + src_reg + imm32) */ | |
#define BPF_LD_IND(SIZE, SRC, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LD | BPF_SIZE(SIZE) | BPF_IND, \ | |
.dst_reg = 0, \ | |
.src_reg = SRC, \ | |
.off = 0, \ | |
.imm = IMM }) | |
/* Memory load, dst_reg = *(uint *) (src_reg + off16) */ | |
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Memory store, *(uint *) (dst_reg + off16) = src_reg */ | |
#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */ | |
#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Memory store, *(uint *) (dst_reg + off16) = imm32 */ | |
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \ | |
((struct bpf_insn) { \ | |
.code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */ | |
#define BPF_JMP_REG(OP, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */ | |
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */ | |
#define BPF_JMP32_REG(OP, DST, SRC, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */ | |
#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \ | |
.dst_reg = DST, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
/* Unconditional jumps, goto pc + off16 */ | |
#define BPF_JMP_A(OFF) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_JA, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = OFF, \ | |
.imm = 0 }) | |
/* Relative call */ | |
#define BPF_CALL_REL(TGT) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_CALL, \ | |
.dst_reg = 0, \ | |
.src_reg = BPF_PSEUDO_CALL, \ | |
.off = 0, \ | |
.imm = TGT }) | |
/* Function call */ | |
#define BPF_CAST_CALL(x) \ | |
((u64 (*)(u64, u64, u64, u64, u64))(x)) | |
#define BPF_EMIT_CALL(FUNC) \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_CALL, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = (FUNC) }) | |
/* Raw code statement block */ | |
#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \ | |
((struct bpf_insn) { \ | |
.code = CODE, \ | |
.dst_reg = DST, \ | |
.src_reg = SRC, \ | |
.off = OFF, \ | |
.imm = IMM }) | |
/* Program exit */ | |
#define BPF_EXIT_INSN() \ | |
((struct bpf_insn) { \ | |
.code = BPF_JMP | BPF_EXIT, \ | |
.dst_reg = 0, \ | |
.src_reg = 0, \ | |
.off = 0, \ | |
.imm = 0 }) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment