@@ -94,6 +94,7 @@ config BPF_HARDENING
help
Enhance bpf interpreter's security
+if BPF_HARDENING
config BPF_NX
bool "Enable bpf NX"
depends on BPF_HARDENING && !DYNAMIC_MEMORY_LAYOUT
@@ -102,6 +103,15 @@ bool "Enable bpf NX"
Allocate eBPF programs in seperate area and make sure the
interpreted programs are in the region.
+config BPF_CFI
+ bool "Enable bpf CFI"
+ depends on BPF_NX
+ default n
+ help
+ Enable alignment checks for eBPF program starting points
+
+endif
+
source "kernel/bpf/preload/Kconfig"
config BPF_LSM
@@ -116,6 +116,75 @@ static void bpf_insn_check_range(const struct bpf_insn *insn)
}
#endif /* CONFIG_BPF_NX */
+#ifdef CONFIG_BPF_CFI
+#define BPF_ON 1
+#define BPF_OFF 0
+
+struct bpf_mode_flag {
+ u8 byte_array[PAGE_SIZE];
+};
+DEFINE_PER_CPU_PAGE_ALIGNED(struct bpf_mode_flag, bpf_exec_mode);
+
+static void __init lock_bpf_exec_mode(void)
+{
+ struct bpf_mode_flag *flag_page;
+ int cpu;
+ for_each_possible_cpu(cpu) {
+ flag_page = per_cpu_ptr(&bpf_exec_mode, cpu);
+ set_memory_ro((unsigned long)flag_page, 1);
+ };
+}
+subsys_initcall(lock_bpf_exec_mode);
+
+static void write_cr0_nocheck(unsigned long val)
+{
+ asm volatile("mov %0,%%cr0": "+r" (val) : : "memory");
+}
+
+/*
+ * Notice that get_cpu_var also disables preemption so no
+ * extra care needed for that.
+ */
+static void enter_bpf_exec_mode(unsigned long *flagsp)
+{
+ struct bpf_mode_flag *flag_page;
+ flag_page = &get_cpu_var(bpf_exec_mode);
+ local_irq_save(*flagsp);
+ write_cr0_nocheck(read_cr0() & ~X86_CR0_WP);
+ flag_page->byte_array[0] = BPF_ON;
+ write_cr0_nocheck(read_cr0() | X86_CR0_WP);
+}
+
+static void leave_bpf_exec_mode(unsigned long *flagsp)
+{
+ struct bpf_mode_flag *flag_page;
+ flag_page = this_cpu_ptr(&bpf_exec_mode);
+ write_cr0_nocheck(read_cr0() & ~X86_CR0_WP);
+ flag_page->byte_array[0] = BPF_OFF;
+ write_cr0_nocheck(read_cr0() | X86_CR0_WP);
+ local_irq_restore(*flagsp);
+ put_cpu_var(bpf_exec_mode);
+}
+
+static void check_bpf_exec_mode(void)
+{
+ struct bpf_mode_flag *flag_page;
+ flag_page = this_cpu_ptr(&bpf_exec_mode);
+ BUG_ON(flag_page->byte_array[0] != BPF_ON);
+}
+
+static void bpf_check_cfi(const struct bpf_insn *insn)
+{
+ const struct bpf_prog *fp;
+ fp = container_of(insn, struct bpf_prog, insnsi[0]);
+ if (!IS_ALIGNED((unsigned long)fp, BPF_MEMORY_ALIGN))
+ BUG();
+}
+
+#else /* CONFIG_BPF_CFI */
+static void check_bpf_exec_mode(void) {}
+#endif /* CONFIG_BPF_CFI */
+
struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flags)
{
gfp_t gfp_flags = bpf_memcg_flags(GFP_KERNEL | __GFP_ZERO | gfp_extra_flags);
@@ -1719,11 +1788,18 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
#undef BPF_INSN_2_LBL
u32 tail_call_cnt = 0;
+#ifdef CONFIG_BPF_CFI
+ unsigned long flags;
+ enter_bpf_exec_mode(&flags);
+ bpf_check_cfi(insn);
+#endif
+
#define CONT ({ insn++; goto select_insn; })
#define CONT_JMP ({ insn++; goto select_insn; })
select_insn:
bpf_insn_check_range(insn);
+ check_bpf_exec_mode();
goto *jumptable[insn->code];
/* Explicitly mask the register-based shift amounts with 63 or 31
@@ -2034,6 +2110,9 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
insn += insn->imm;
CONT;
JMP_EXIT:
+#ifdef CONFIG_BPF_CFI
+ leave_bpf_exec_mode(&flags);
+#endif
return BPF_R0;
/* JMP */
#define COND_JMP(SIGN, OPCODE, CMP_OP) \