Skip to content

Commit 314beb9

Browse files
edumazetdavem330
authored andcommitted
x86: bpf_jit_comp: secure bpf jit against spraying attacks
hpa bringed into my attention some security related issues with BPF JIT on x86. This patch makes sure the bpf generated code is marked read only, as other kernel text sections. It also splits the unused space (we vmalloc() and only use a fraction of the page) in two parts, so that the generated bpf code not starts at a known offset in the page, but a pseudo random one. Refs: http://mainisusuallyafunction.blogspot.com/2012/11/attacking-hardened-linux-systems-with.html Reported-by: H. Peter Anvin <[email protected]> Signed-off-by: Eric Dumazet <[email protected]> Reviewed-by: Daniel Borkmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3e59cb0 commit 314beb9

File tree

1 file changed

+47
-6
lines changed

1 file changed

+47
-6
lines changed

arch/x86/net/bpf_jit_comp.c

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/netdevice.h>
1313
#include <linux/filter.h>
1414
#include <linux/if_vlan.h>
15+
#include <linux/random.h>
1516

1617
/*
1718
* Conventions :
@@ -144,6 +145,39 @@ static int pkt_type_offset(void)
144145
return -1;
145146
}
146147

148+
struct bpf_binary_header {
149+
unsigned int pages;
150+
/* Note : for security reasons, bpf code will follow a randomly
151+
* sized amount of int3 instructions
152+
*/
153+
u8 image[];
154+
};
155+
156+
static struct bpf_binary_header *bpf_alloc_binary(unsigned int proglen,
157+
u8 **image_ptr)
158+
{
159+
unsigned int sz, hole;
160+
struct bpf_binary_header *header;
161+
162+
/* Most of BPF filters are really small,
163+
* but if some of them fill a page, allow at least
164+
* 128 extra bytes to insert a random section of int3
165+
*/
166+
sz = round_up(proglen + sizeof(*header) + 128, PAGE_SIZE);
167+
header = module_alloc(sz);
168+
if (!header)
169+
return NULL;
170+
171+
memset(header, 0xcc, sz); /* fill whole space with int3 instructions */
172+
173+
header->pages = sz / PAGE_SIZE;
174+
hole = sz - (proglen + sizeof(*header));
175+
176+
/* insert a random number of int3 instructions before BPF code */
177+
*image_ptr = &header->image[prandom_u32() % hole];
178+
return header;
179+
}
180+
147181
void bpf_jit_compile(struct sk_filter *fp)
148182
{
149183
u8 temp[64];
@@ -153,6 +187,7 @@ void bpf_jit_compile(struct sk_filter *fp)
153187
int t_offset, f_offset;
154188
u8 t_op, f_op, seen = 0, pass;
155189
u8 *image = NULL;
190+
struct bpf_binary_header *header = NULL;
156191
u8 *func;
157192
int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */
158193
unsigned int cleanup_addr; /* epilogue code offset */
@@ -693,7 +728,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
693728
if (unlikely(proglen + ilen > oldproglen)) {
694729
pr_err("bpb_jit_compile fatal error\n");
695730
kfree(addrs);
696-
module_free(NULL, image);
731+
module_free(NULL, header);
697732
return;
698733
}
699734
memcpy(image + proglen, temp, ilen);
@@ -717,8 +752,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
717752
break;
718753
}
719754
if (proglen == oldproglen) {
720-
image = module_alloc(proglen);
721-
if (!image)
755+
header = bpf_alloc_binary(proglen, &image);
756+
if (!header)
722757
goto out;
723758
}
724759
oldproglen = proglen;
@@ -728,7 +763,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
728763
bpf_jit_dump(flen, proglen, pass, image);
729764

730765
if (image) {
731-
bpf_flush_icache(image, image + proglen);
766+
bpf_flush_icache(header, image + proglen);
767+
set_memory_ro((unsigned long)header, header->pages);
732768
fp->bpf_func = (void *)image;
733769
}
734770
out:
@@ -738,6 +774,11 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i];
738774

739775
void bpf_jit_free(struct sk_filter *fp)
740776
{
741-
if (fp->bpf_func != sk_run_filter)
742-
module_free(NULL, fp->bpf_func);
777+
if (fp->bpf_func != sk_run_filter) {
778+
unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK;
779+
struct bpf_binary_header *header = (void *)addr;
780+
781+
set_memory_rw(addr, header->pages);
782+
module_free(NULL, header);
783+
}
743784
}

0 commit comments

Comments
 (0)