diff options
author | Eric Dumazet <edumazet@google.com> | 2014-01-15 09:50:07 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-06 14:08:16 -0500 |
commit | cd7361dc9fa60c35d011d674d48eababcc3eb767 (patch) | |
tree | 51c5b5cdfabf829671688e9df26623fa64feb5ea /net | |
parent | 8c035b62e21a2d26c1a5181ee0d27a76c8996be8 (diff) |
bpf: do not use reciprocal divide
[ Upstream commit aee636c4809fa54848ff07a899b326eb1f9987a2 ]
At first Jakub Zawadzki noticed that some divisions by reciprocal_divide
were not correct. (off by one in some cases)
http://www.wireshark.org/~darkjames/reciprocal-buggy.c
He could also show this with BPF:
http://www.wireshark.org/~darkjames/set-and-dump-filter-k-bug.c
The reciprocal divide in linux kernel is not generic enough,
lets remove its use in BPF, as it is not worth the pain with
current cpus.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Cc: Mircea Gherzan <mgherzan@gmail.com>
Cc: Daniel Borkmann <dxchgb@gmail.com>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: Matt Evans <matt@ozlabs.org>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/core/filter.c | 30 |
1 files changed, 2 insertions, 28 deletions
diff --git a/net/core/filter.c b/net/core/filter.c index 6438f29ff266..52f01229ee01 100644 --- a/net/core/filter.c +++ b/net/core/filter.c | |||
@@ -36,7 +36,6 @@ | |||
36 | #include <asm/uaccess.h> | 36 | #include <asm/uaccess.h> |
37 | #include <asm/unaligned.h> | 37 | #include <asm/unaligned.h> |
38 | #include <linux/filter.h> | 38 | #include <linux/filter.h> |
39 | #include <linux/reciprocal_div.h> | ||
40 | #include <linux/ratelimit.h> | 39 | #include <linux/ratelimit.h> |
41 | #include <linux/seccomp.h> | 40 | #include <linux/seccomp.h> |
42 | #include <linux/if_vlan.h> | 41 | #include <linux/if_vlan.h> |
@@ -166,7 +165,7 @@ unsigned int sk_run_filter(const struct sk_buff *skb, | |||
166 | A /= X; | 165 | A /= X; |
167 | continue; | 166 | continue; |
168 | case BPF_S_ALU_DIV_K: | 167 | case BPF_S_ALU_DIV_K: |
169 | A = reciprocal_divide(A, K); | 168 | A /= K; |
170 | continue; | 169 | continue; |
171 | case BPF_S_ALU_MOD_X: | 170 | case BPF_S_ALU_MOD_X: |
172 | if (X == 0) | 171 | if (X == 0) |
@@ -553,11 +552,6 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen) | |||
553 | /* Some instructions need special checks */ | 552 | /* Some instructions need special checks */ |
554 | switch (code) { | 553 | switch (code) { |
555 | case BPF_S_ALU_DIV_K: | 554 | case BPF_S_ALU_DIV_K: |
556 | /* check for division by zero */ | ||
557 | if (ftest->k == 0) | ||
558 | return -EINVAL; | ||
559 | ftest->k = reciprocal_value(ftest->k); | ||
560 | break; | ||
561 | case BPF_S_ALU_MOD_K: | 555 | case BPF_S_ALU_MOD_K: |
562 | /* check for division by zero */ | 556 | /* check for division by zero */ |
563 | if (ftest->k == 0) | 557 | if (ftest->k == 0) |
@@ -853,27 +847,7 @@ void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to) | |||
853 | to->code = decodes[code]; | 847 | to->code = decodes[code]; |
854 | to->jt = filt->jt; | 848 | to->jt = filt->jt; |
855 | to->jf = filt->jf; | 849 | to->jf = filt->jf; |
856 | 850 | to->k = filt->k; | |
857 | if (code == BPF_S_ALU_DIV_K) { | ||
858 | /* | ||
859 | * When loaded this rule user gave us X, which was | ||
860 | * translated into R = r(X). Now we calculate the | ||
861 | * RR = r(R) and report it back. If next time this | ||
862 | * value is loaded and RRR = r(RR) is calculated | ||
863 | * then the R == RRR will be true. | ||
864 | * | ||
865 | * One exception. X == 1 translates into R == 0 and | ||
866 | * we can't calculate RR out of it with r(). | ||
867 | */ | ||
868 | |||
869 | if (filt->k == 0) | ||
870 | to->k = 1; | ||
871 | else | ||
872 | to->k = reciprocal_value(filt->k); | ||
873 | |||
874 | BUG_ON(reciprocal_value(to->k) != filt->k); | ||
875 | } else | ||
876 | to->k = filt->k; | ||
877 | } | 851 | } |
878 | 852 | ||
879 | int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len) | 853 | int sk_get_filter(struct sock *sk, struct sock_filter __user *ubuf, unsigned int len) |