Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rose: limit sk_filter trim to payload

Sockets can have a filter program attached that drops or trims
incoming packets based on the filter program return value.

Rose requires data packets to have at least ROSE_MIN_LEN bytes. It
verifies this on arrival in rose_route_frame and unconditionally pulls
the bytes in rose_recvmsg. The filter can trim packets to below this
value in-between, causing pull to fail, leaving the partial header at
the time of skb_copy_datagram_msg.

Place a lower bound on the size to which sk_filter may trim packets
by introducing sk_filter_trim_cap and call this for rose packets.

Signed-off-by: Willem de Bruijn <willemb@google.com>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Willem de Bruijn and committed by
David S. Miller
f4979fce 22cb99fb

+12 -7
+5 -1
include/linux/filter.h
··· 467 467 } 468 468 #endif /* CONFIG_DEBUG_SET_MODULE_RONX */ 469 469 470 - int sk_filter(struct sock *sk, struct sk_buff *skb); 470 + int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap); 471 + static inline int sk_filter(struct sock *sk, struct sk_buff *skb) 472 + { 473 + return sk_filter_trim_cap(sk, skb, 1); 474 + } 471 475 472 476 struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err); 473 477 void bpf_prog_free(struct bpf_prog *fp);
+5 -5
net/core/filter.c
··· 53 53 #include <net/sock_reuseport.h> 54 54 55 55 /** 56 - * sk_filter - run a packet through a socket filter 56 + * sk_filter_trim_cap - run a packet through a socket filter 57 57 * @sk: sock associated with &sk_buff 58 58 * @skb: buffer to filter 59 + * @cap: limit on how short the eBPF program may trim the packet 59 60 * 60 61 * Run the eBPF program and then cut skb->data to correct size returned by 61 62 * the program. If pkt_len is 0 we toss packet. If skb->len is smaller ··· 65 64 * be accepted or -EPERM if the packet should be tossed. 66 65 * 67 66 */ 68 - int sk_filter(struct sock *sk, struct sk_buff *skb) 67 + int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap) 69 68 { 70 69 int err; 71 70 struct sk_filter *filter; ··· 86 85 filter = rcu_dereference(sk->sk_filter); 87 86 if (filter) { 88 87 unsigned int pkt_len = bpf_prog_run_save_cb(filter->prog, skb); 89 - 90 - err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM; 88 + err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM; 91 89 } 92 90 rcu_read_unlock(); 93 91 94 92 return err; 95 93 } 96 - EXPORT_SYMBOL(sk_filter); 94 + EXPORT_SYMBOL(sk_filter_trim_cap); 97 95 98 96 static u64 __skb_get_pay_offset(u64 ctx, u64 a, u64 x, u64 r4, u64 r5) 99 97 {
+2 -1
net/rose/rose_in.c
··· 164 164 rose_frames_acked(sk, nr); 165 165 if (ns == rose->vr) { 166 166 rose_start_idletimer(sk); 167 - if (sock_queue_rcv_skb(sk, skb) == 0) { 167 + if (sk_filter_trim_cap(sk, skb, ROSE_MIN_LEN) == 0 && 168 + __sock_queue_rcv_skb(sk, skb) == 0) { 168 169 rose->vr = (rose->vr + 1) % ROSE_MODULUS; 169 170 queued = 1; 170 171 } else {