From: Chris Mason bpf_lwt_seg6_adjust_srh() lets an End.BPF prog change the size of the TLVs in the SRH. The helper keeps the SRH length in srh_state->hdrlen, in bytes and without the first 8 bytes. Later seg6_bpf_has_valid_srh() writes it to srh->hdrlen, an 8-bit field in units of 8 bytes. But srh_state->hdrlen has no upper limit, and the cast to u8 can truncate it. After that, seg6_validate_srh() gets the SRH length from srh->hdrlen, and thus it can accept the SRH. Then srh->hdrlen is too small, and a parser reads TLVs as the subsequent header. Return -EFAULT when the new srh_state->hdrlen is larger than 2040, the largest length that srh->hdrlen can hold. To replace a TLV in the largest SRH, a prog can no longer add bytes first. It must remove bytes first. Fixes: fe94cc290f53 ("bpf: Add IPv6 Segment Routing helpers") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov --- net/core/filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/filter.c b/net/core/filter.c index a69f6817e273..acd1e89351d6 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7179,6 +7179,9 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset, return -EFAULT; if (unlikely(len < 0 && (void *)((char *)ptr - len) > srh_end)) return -EFAULT; + /* The length field of the SRH has 8 bits, in units of 8 bytes. */ + if (unlikely(len > (U8_MAX << 3) - srh_state->hdrlen)) + return -EFAULT; if (len > 0) { ret = skb_cow_head(skb, len); -- 2.55.0