Skip to content

Commit

Permalink
net: bpf: arm64: address randomize and write protect JIT code
Browse files Browse the repository at this point in the history
This is the ARM64 variant for 314beb9 ("x86: bpf_jit_comp: secure bpf
jit against spraying attacks").

Thanks to commit 11d91a7 ("arm64: Add CONFIG_DEBUG_SET_MODULE_RONX
support") which added necessary infrastructure, we can now implement
RO marking of eBPF generated JIT image pages and randomize start offset
for the JIT code, so that it does not reside directly on a page boundary
anymore. Likewise, the holes are filled with illegal instructions: here
we use BRK #0x100 (opcode 0xd4202000) to trigger a fault in the kernel
(unallocated BRKs would trigger a fault through do_debug_exception). This
seems more reliable as we don't have a guaranteed undefined instruction
space on ARM64.

This is basically the ARM64 variant of what we already have in ARM via
commit 55309dd ("net: bpf: arm: address randomize and write protect
JIT code"). Moreover, this commit also presents a merge resolution due to
conflicts with commit 60a3b22 ("net: bpf: make eBPF interpreter images
read-only") as we don't use kfree() in bpf_jit_free() anymore to release
the locked bpf_prog structure, but instead bpf_prog_unlock_free() through
a different allocator.

JIT tested on aarch64 with BPF test suite.

Reference: http://mainisusuallyafunction.blogspot.com/2012/11/attacking-hardened-linux-systems-with.html
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Reviewed-by: Zi Shen Lim <zlim.lnx@gmail.com>
Acked-by: Will Deacon <will.deacon@arm.com>
Cc: David S. Miller <davem@davemloft.net>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
  • Loading branch information
Daniel Borkmann authored and Catalin Marinas committed Oct 20, 2014
1 parent c0260ba commit b569c1c
Showing 1 changed file with 30 additions and 9 deletions.
39 changes: 30 additions & 9 deletions arch/arm64/net/bpf_jit_comp.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
#define pr_fmt(fmt) "bpf_jit: " fmt

#include <linux/filter.h>
#include <linux/moduleloader.h>
#include <linux/printk.h>
#include <linux/skbuff.h>
#include <linux/slab.h>

#include <asm/byteorder.h>
#include <asm/cacheflush.h>
#include <asm/debug-monitors.h>

#include "bpf_jit.h"

Expand Down Expand Up @@ -119,6 +120,14 @@ static inline int bpf2a64_offset(int bpf_to, int bpf_from,
return to - from;
}

static void jit_fill_hole(void *area, unsigned int size)
{
u32 *ptr;
/* We are guaranteed to have aligned memory. */
for (ptr = area; size >= sizeof(u32); size -= sizeof(u32))
*ptr++ = cpu_to_le32(AARCH64_BREAK_FAULT);
}

static inline int epilogue_offset(const struct jit_ctx *ctx)
{
int to = ctx->offset[ctx->prog->len - 1];
Expand Down Expand Up @@ -613,8 +622,10 @@ void bpf_jit_compile(struct bpf_prog *prog)

void bpf_int_jit_compile(struct bpf_prog *prog)
{
struct bpf_binary_header *header;
struct jit_ctx ctx;
int image_size;
u8 *image_ptr;

if (!bpf_jit_enable)
return;
Expand All @@ -636,23 +647,25 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
goto out;

build_prologue(&ctx);

build_epilogue(&ctx);

/* Now we know the actual image size. */
image_size = sizeof(u32) * ctx.idx;
ctx.image = module_alloc(image_size);
if (unlikely(ctx.image == NULL))
header = bpf_jit_binary_alloc(image_size, &image_ptr,
sizeof(u32), jit_fill_hole);
if (header == NULL)
goto out;

/* 2. Now, the actual pass. */

ctx.image = (u32 *)image_ptr;
ctx.idx = 0;

build_prologue(&ctx);

ctx.body_offset = ctx.idx;
if (build_body(&ctx)) {
module_free(NULL, ctx.image);
bpf_jit_binary_free(header);
goto out;
}

Expand All @@ -663,17 +676,25 @@ void bpf_int_jit_compile(struct bpf_prog *prog)
bpf_jit_dump(prog->len, image_size, 2, ctx.image);

bpf_flush_icache(ctx.image, ctx.image + ctx.idx);

set_memory_ro((unsigned long)header, header->pages);
prog->bpf_func = (void *)ctx.image;
prog->jited = 1;

out:
kfree(ctx.offset);
}

void bpf_jit_free(struct bpf_prog *prog)
{
if (prog->jited)
module_free(NULL, prog->bpf_func);
unsigned long addr = (unsigned long)prog->bpf_func & PAGE_MASK;
struct bpf_binary_header *header = (void *)addr;

if (!prog->jited)
goto free_filter;

set_memory_rw(addr, header->pages);
bpf_jit_binary_free(header);

kfree(prog);
free_filter:
bpf_prog_unlock_free(prog);
}

0 comments on commit b569c1c

Please sign in to comment.