aboutsummaryrefslogtreecommitdiffstats
path: root/net/core
diff options
context:
space:
mode:
authorDaniel Borkmann <dborkman@redhat.com>2012-12-28 05:50:17 -0500
committerDavid S. Miller <davem@davemloft.net>2012-12-30 05:30:28 -0500
commitaa1113d9f85da59dcbdd32aeb5d71da566e46def (patch)
treea2c534b2ed9a2df216626b254d379329af8b89d1 /net/core
parent61c5e88aecd6fbf2480f39394bb495964e6d9984 (diff)
net: filter: return -EINVAL if BPF_S_ANC* operation is not supported
Currently, we return -EINVAL for malformed or wrong BPF filters. However, this is not done for BPF_S_ANC* operations, which makes it more difficult to detect if it's actually supported or not by the BPF machine. Therefore, we should also return -EINVAL if K is within the SKF_AD_OFF universe and the ancillary operation did not match. Why exactly is it needed? If tools such as libpcap/tcpdump want to make use of new ancillary operations (like filtering VLAN in kernel space), there is currently no sane way to test if this feature / BPF_S_ANC* op is present or not, since no error is returned. This patch will make life easier for that and allow for a proper usage for user space applications. There was concern, if this patch will break userland. Short answer: Yes and no. Long answer: It will "break" only for code that calls ... { BPF_LD | BPF_(W|H|B) | BPF_ABS, 0, 0, <K> }, ... where <K> is in [0xfffff000, 0xffffffff] _and_ <K> is *not* an ancillary. And here comes the BUT: assuming some *old* code will have such an instruction where <K> is between [0xfffff000, 0xffffffff] and it doesn't know ancillary operations, then this will give a non-expected / unwanted behavior as well (since we do not return the BPF machine with 0 after a failed load_pointer(), which was the case before introducing ancillary operations, but load sth. into the accumulator instead, and continue with the next instruction, for instance). Thus, user space code would already have been broken by introducing ancillary operations into the BPF machine per se. Code that does such a direct load, e.g. "load word at packet offset 0xffffffff into accumulator" ("ld [0xffffffff]") is quite broken, isn't it? The whole assumption of ancillary operations is that no-one intentionally calls things like "ld [0xffffffff]" and expect this word to be loaded from such a packet offset. Hence, we can also safely make use of this feature testing patch and facilitate application development. Therefore, at least from this patch onwards, we have *for sure* a check whether current or in future implemented BPF_S_ANC* ops are supported in the kernel. Patch was tested on x86_64. (Thanks to Eric for the previous review.) Cc: Eric Dumazet <eric.dumazet@gmail.com> Reported-by: Ani Sinha <ani@aristanetworks.com> Signed-off-by: Daniel Borkmann <dborkman@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core')
-rw-r--r--net/core/filter.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/net/core/filter.c b/net/core/filter.c
index c23543cba132..2ead2a9b1859 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -532,6 +532,7 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
532 [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X, 532 [BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X,
533 }; 533 };
534 int pc; 534 int pc;
535 bool anc_found;
535 536
536 if (flen == 0 || flen > BPF_MAXINSNS) 537 if (flen == 0 || flen > BPF_MAXINSNS)
537 return -EINVAL; 538 return -EINVAL;
@@ -592,8 +593,10 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
592 case BPF_S_LD_W_ABS: 593 case BPF_S_LD_W_ABS:
593 case BPF_S_LD_H_ABS: 594 case BPF_S_LD_H_ABS:
594 case BPF_S_LD_B_ABS: 595 case BPF_S_LD_B_ABS:
596 anc_found = false;
595#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \ 597#define ANCILLARY(CODE) case SKF_AD_OFF + SKF_AD_##CODE: \
596 code = BPF_S_ANC_##CODE; \ 598 code = BPF_S_ANC_##CODE; \
599 anc_found = true; \
597 break 600 break
598 switch (ftest->k) { 601 switch (ftest->k) {
599 ANCILLARY(PROTOCOL); 602 ANCILLARY(PROTOCOL);
@@ -610,6 +613,10 @@ int sk_chk_filter(struct sock_filter *filter, unsigned int flen)
610 ANCILLARY(VLAN_TAG); 613 ANCILLARY(VLAN_TAG);
611 ANCILLARY(VLAN_TAG_PRESENT); 614 ANCILLARY(VLAN_TAG_PRESENT);
612 } 615 }
616
617 /* ancillary operation unknown or unsupported */
618 if (anc_found == false && ftest->k >= SKF_AD_OFF)
619 return -EINVAL;
613 } 620 }
614 ftest->code = code; 621 ftest->code = code;
615 } 622 }