From: Josef Bacik <josef@toxicpanda.com>
input_action_end_bpf() validates the SRH of a packet and pulls it into the
head before the program runs. If the program decapsulates the packet with
bpf_lwt_seg6_action(SEG6_LOCAL_ACTION_END_DT6), bpf_update_srh_state()
looks for a routing header in the inner packet and takes it for the new
SRH as it is.
Nobody has looked at that header, and whoever has sent the packet has
written it. ipv6_find_hdr() finds it in a fragment of the skb as well, but
hdrlen is then read at that offset from skb->data, past the end of the
head. The header need not be an SRH, and its hdrlen need not fit into the
packet, but it is marked as valid, and bpf_lwt_seg6_adjust_srh() and
bpf_lwt_seg6_store_bytes() check their offsets against it.
A program which then adds a TLV at the end of the SRH, where hdrlen has it,
makes bpf_lwt_seg6_adjust_srh() move and write memory behind the head of
the skb, as far as the sender likes within the 2 KB that hdrlen can say:
BUG: KASAN: slab-out-of-bounds in bpf_skb_net_hdr_push+0x185/0x240
Read of size 2088 at addr ffff888107d6f09e by task srh/102
BUG: KASAN: slab-out-of-bounds in bpf_skb_net_hdr_push+0x1b5/0x240
Write of size 8 at addr ffff888107d6f8be by task srh/102
Use seg6_get_srh(), which pulls and validates the header. Without a valid
SRH the state says that there is none, and the packet goes its way as
before. The pull can move the head, so compute the data pointers behind
it.
Fixes: 486cdf21583e ("bpf: add End.DT6 action to bpf_lwt_seg6_action helper")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
---
net/core/filter.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/net/core/filter.c b/net/core/filter.c
index a18702f89feb..6ac386dea4e8 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -7022,15 +7022,15 @@ static void bpf_update_srh_state(struct sk_buff *skb)
{
struct seg6_bpf_srh_state *srh_state =
this_cpu_ptr(&seg6_bpf_srh_states);
- int srhoff = 0;
- if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) {
- srh_state->srh = NULL;
- } else {
- srh_state->srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
+ /* Behind a decapsulation this is a header that nobody has looked at */
+ srh_state->srh = seg6_get_srh(skb, 0);
+ if (srh_state->srh) {
srh_state->hdrlen = srh_state->srh->hdrlen << 3;
srh_state->valid = true;
}
+ /* it may have been pulled into the head */
+ bpf_compute_data_pointers(skb);
}
BPF_CALL_4(bpf_lwt_seg6_action, struct sk_buff *, skb,--
2.55.0