diff options
Diffstat (limited to 'arch/x86/net/bpf_jit_comp.c')
-rw-r--r-- | arch/x86/net/bpf_jit_comp.c | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index f66b54086ce5..79c216aa0e2b 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/netdevice.h> | 12 | #include <linux/netdevice.h> |
13 | #include <linux/filter.h> | 13 | #include <linux/filter.h> |
14 | #include <linux/if_vlan.h> | 14 | #include <linux/if_vlan.h> |
15 | #include <linux/random.h> | ||
15 | 16 | ||
16 | /* | 17 | /* |
17 | * Conventions : | 18 | * Conventions : |
@@ -144,6 +145,39 @@ static int pkt_type_offset(void) | |||
144 | return -1; | 145 | return -1; |
145 | } | 146 | } |
146 | 147 | ||
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 | |||
147 | void bpf_jit_compile(struct sk_filter *fp) | 181 | void bpf_jit_compile(struct sk_filter *fp) |
148 | { | 182 | { |
149 | u8 temp[64]; | 183 | u8 temp[64]; |
@@ -153,6 +187,7 @@ void bpf_jit_compile(struct sk_filter *fp) | |||
153 | int t_offset, f_offset; | 187 | int t_offset, f_offset; |
154 | u8 t_op, f_op, seen = 0, pass; | 188 | u8 t_op, f_op, seen = 0, pass; |
155 | u8 *image = NULL; | 189 | u8 *image = NULL; |
190 | struct bpf_binary_header *header = NULL; | ||
156 | u8 *func; | 191 | u8 *func; |
157 | int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */ | 192 | int pc_ret0 = -1; /* bpf index of first RET #0 instruction (if any) */ |
158 | unsigned int cleanup_addr; /* epilogue code offset */ | 193 | unsigned int cleanup_addr; /* epilogue code offset */ |
@@ -693,7 +728,7 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; | |||
693 | if (unlikely(proglen + ilen > oldproglen)) { | 728 | if (unlikely(proglen + ilen > oldproglen)) { |
694 | pr_err("bpb_jit_compile fatal error\n"); | 729 | pr_err("bpb_jit_compile fatal error\n"); |
695 | kfree(addrs); | 730 | kfree(addrs); |
696 | module_free(NULL, image); | 731 | module_free(NULL, header); |
697 | return; | 732 | return; |
698 | } | 733 | } |
699 | memcpy(image + proglen, temp, ilen); | 734 | memcpy(image + proglen, temp, ilen); |
@@ -717,10 +752,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; | |||
717 | break; | 752 | break; |
718 | } | 753 | } |
719 | if (proglen == oldproglen) { | 754 | if (proglen == oldproglen) { |
720 | image = module_alloc(max_t(unsigned int, | 755 | header = bpf_alloc_binary(proglen, &image); |
721 | proglen, | 756 | if (!header) |
722 | sizeof(struct work_struct))); | ||
723 | if (!image) | ||
724 | goto out; | 757 | goto out; |
725 | } | 758 | } |
726 | oldproglen = proglen; | 759 | oldproglen = proglen; |
@@ -730,7 +763,8 @@ cond_branch: f_offset = addrs[i + filter[i].jf] - addrs[i]; | |||
730 | bpf_jit_dump(flen, proglen, pass, image); | 763 | bpf_jit_dump(flen, proglen, pass, image); |
731 | 764 | ||
732 | if (image) { | 765 | if (image) { |
733 | bpf_flush_icache(image, image + proglen); | 766 | bpf_flush_icache(header, image + proglen); |
767 | set_memory_ro((unsigned long)header, header->pages); | ||
734 | fp->bpf_func = (void *)image; | 768 | fp->bpf_func = (void *)image; |
735 | } | 769 | } |
736 | out: | 770 | out: |
@@ -738,20 +772,13 @@ out: | |||
738 | return; | 772 | return; |
739 | } | 773 | } |
740 | 774 | ||
741 | static void jit_free_defer(struct work_struct *arg) | ||
742 | { | ||
743 | module_free(NULL, arg); | ||
744 | } | ||
745 | |||
746 | /* run from softirq, we must use a work_struct to call | ||
747 | * module_free() from process context | ||
748 | */ | ||
749 | void bpf_jit_free(struct sk_filter *fp) | 775 | void bpf_jit_free(struct sk_filter *fp) |
750 | { | 776 | { |
751 | if (fp->bpf_func != sk_run_filter) { | 777 | if (fp->bpf_func != sk_run_filter) { |
752 | struct work_struct *work = (struct work_struct *)fp->bpf_func; | 778 | unsigned long addr = (unsigned long)fp->bpf_func & PAGE_MASK; |
779 | struct bpf_binary_header *header = (void *)addr; | ||
753 | 780 | ||
754 | INIT_WORK(work, jit_free_defer); | 781 | set_memory_rw(addr, header->pages); |
755 | schedule_work(work); | 782 | module_free(NULL, header); |
756 | } | 783 | } |
757 | } | 784 | } |