summaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2013-07-16 13:25:49 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2013-07-18 12:44:37 +0200
commitaa2d2c73c21f22ce4c643128b671aa7e7bbff54f (patch)
tree2f30547e740f0931dce2b4d864ee952ef05a297a /arch/s390
parentfee1b5488d76396d8f95989624d37f436b3fba44 (diff)
downloadlinux-aa2d2c73c21f22ce4c643128b671aa7e7bbff54f.tar.bz2
s390/bpf,jit: address randomize and write protect jit code
This is the s390 variant of 314beb9b "x86: bpf_jit_comp: secure bpf jit against spraying attacks". With this change the whole jit code and literal pool will be write protected after creation. In addition the start address of the jit code won't be always on a page boundary anymore. Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/net/bpf_jit_comp.c51
1 files changed, 46 insertions, 5 deletions
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index 80828bfee2ec..788e22395acd 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -9,6 +9,7 @@
#include <linux/netdevice.h>
#include <linux/if_vlan.h>
#include <linux/filter.h>
+#include <linux/random.h>
#include <asm/cacheflush.h>
#include <asm/processor.h>
#include <asm/facility.h>
@@ -738,8 +739,41 @@ out:
return -1;
}
+/*
+ * Note: for security reasons, bpf code will follow a randomly
+ * sized amount of illegal instructions.
+ */
+struct bpf_binary_header {
+ unsigned int pages;
+ u8 image[];
+};
+
+static struct bpf_binary_header *bpf_alloc_binary(unsigned int bpfsize,
+ u8 **image_ptr)
+{
+ struct bpf_binary_header *header;
+ unsigned int sz, hole;
+
+ /* Most BPF filters are really small, but if some of them fill a page,
+ * allow at least 128 extra bytes for illegal instructions.
+ */
+ sz = round_up(bpfsize + sizeof(*header) + 128, PAGE_SIZE);
+ header = module_alloc(sz);
+ if (!header)
+ return NULL;
+ memset(header, 0, sz);
+ header->pages = sz / PAGE_SIZE;
+ hole = sz - bpfsize + sizeof(*header);
+ /* Insert random number of illegal instructions before BPF code
+ * and make sure the first instruction starts at an even address.
+ */
+ *image_ptr = &header->image[(prandom_u32() % hole) & -2];
+ return header;
+}
+
void bpf_jit_compile(struct sk_filter *fp)
{
+ struct bpf_binary_header *header = NULL;
unsigned long size, prg_len, lit_len;
struct bpf_jit jit, cjit;
unsigned int *addrs;
@@ -775,8 +809,8 @@ void bpf_jit_compile(struct sk_filter *fp)
size = prg_len + lit_len;
if (size >= BPF_SIZE_MAX)
goto out;
- jit.start = module_alloc(size);
- if (!jit.start)
+ header = bpf_alloc_binary(size, &jit.start);
+ if (!header)
goto out;
jit.prg = jit.mid = jit.start + prg_len;
jit.lit = jit.end = jit.start + prg_len + lit_len;
@@ -791,14 +825,21 @@ void bpf_jit_compile(struct sk_filter *fp)
if (jit.start)
print_fn_code(jit.start, jit.mid - jit.start);
}
- if (jit.start)
+ if (jit.start) {
+ set_memory_ro((unsigned long)header, header->pages);
fp->bpf_func = (void *) jit.start;
+ }
out:
kfree(addrs);
}
void bpf_jit_free(struct sk_filter *fp)
{
- if (fp->bpf_func != sk_run_filter)
- module_free(NULL, fp->bpf_func);
+ unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
+ struct bpf_binary_header *header = (void *)addr;
+
+ if (fp->bpf_func == sk_run_filter)
+ return;
+ set_memory_rw(addr, header->pages);
+ module_free(NULL, header);
}