mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-05 19:31:57 -04:00
UPSTREAM: seg6: fix seg6_validate_srh() to avoid slab-out-of-bounds
The seg6_validate_srh() is used to validate SRH for three cases: case1: SRH of data-plane SRv6 packets to be processed by the Linux kernel. Case2: SRH of the netlink message received from user-space (iproute2) Case3: SRH injected into packets through setsockopt In case1, the SRH can be encoded in the Reduced way (i.e., first SID is carried in DA only and not represented as SID in the SRH) and the seg6_validate_srh() now handles this case correctly. In case2 and case3, the SRH shouldn’t be encoded in the Reduced way otherwise we lose the first segment (i.e., the first hop). The current implementation of the seg6_validate_srh() allow SRH of case2 and case3 to be encoded in the Reduced way. This leads a slab-out-of-bounds problem. This patch verifies SRH of case1, case2 and case3. Allowing case1 to be reduced while preventing SRH of case2 and case3 from being reduced . Reported-by: syzbot+e8c028b62439eac42073@syzkaller.appspotmail.com Reported-by: YueHaibing <yuehaibing@huawei.com> Fixes: 0cb7498f234e ("seg6: fix SRH processing to comply with RFC8754") Change-Id: I2917c075b16c8887f80f74f380e2f26b12d1c19c Signed-off-by: Ahmed Abdelsalam <ahabdels@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
788f6db706
commit
86527f07f5
6 changed files with 17 additions and 13 deletions
|
|
@ -57,7 +57,7 @@ extern void seg6_iptunnel_exit(void);
|
|||
extern int seg6_local_init(void);
|
||||
extern void seg6_local_exit(void);
|
||||
|
||||
extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len);
|
||||
extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced);
|
||||
extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh,
|
||||
int proto);
|
||||
extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh);
|
||||
|
|
|
|||
|
|
@ -5123,7 +5123,7 @@ static int bpf_push_seg6_encap(struct sk_buff *skb, u32 type, void *hdr, u32 len
|
|||
int err;
|
||||
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)hdr;
|
||||
|
||||
if (!seg6_validate_srh(srh, len))
|
||||
if (!seg6_validate_srh(srh, len, false))
|
||||
return -EINVAL;
|
||||
|
||||
switch (type) {
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
|
|||
struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)
|
||||
opt->srcrt;
|
||||
|
||||
if (!seg6_validate_srh(srh, optlen))
|
||||
if (!seg6_validate_srh(srh, optlen, false))
|
||||
goto sticky_done;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
#include <net/seg6_hmac.h>
|
||||
#endif
|
||||
|
||||
bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
|
||||
bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced)
|
||||
{
|
||||
unsigned int tlv_offset;
|
||||
int max_last_entry;
|
||||
|
|
@ -37,13 +37,17 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len)
|
|||
if (((srh->hdrlen + 1) << 3) != len)
|
||||
return false;
|
||||
|
||||
max_last_entry = (srh->hdrlen / 2) - 1;
|
||||
|
||||
if (srh->first_segment > max_last_entry)
|
||||
if (!reduced && srh->segments_left > srh->first_segment) {
|
||||
return false;
|
||||
} else {
|
||||
max_last_entry = (srh->hdrlen / 2) - 1;
|
||||
|
||||
if (srh->segments_left > srh->first_segment + 1)
|
||||
return false;
|
||||
if (srh->first_segment > max_last_entry)
|
||||
return false;
|
||||
|
||||
if (srh->segments_left > srh->first_segment + 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4);
|
||||
|
||||
|
|
|
|||
|
|
@ -435,7 +435,7 @@ static int seg6_build_state(struct nlattr *nla,
|
|||
}
|
||||
|
||||
/* verify that SRH is consistent */
|
||||
if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo)))
|
||||
if (!seg6_validate_srh(tuninfo->srh, tuninfo_len - sizeof(*tuninfo), false))
|
||||
return -EINVAL;
|
||||
|
||||
newts = lwtunnel_state_alloc(tuninfo_len + sizeof(*slwt));
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb)
|
|||
*/
|
||||
srh = (struct ipv6_sr_hdr *)(skb->data + srhoff);
|
||||
|
||||
if (!seg6_validate_srh(srh, len))
|
||||
if (!seg6_validate_srh(srh, len, true))
|
||||
return NULL;
|
||||
|
||||
return srh;
|
||||
|
|
@ -479,7 +479,7 @@ bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
|
|||
return false;
|
||||
|
||||
srh->hdrlen = (u8)(srh_state->hdrlen >> 3);
|
||||
if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3))
|
||||
if (!seg6_validate_srh(srh, (srh->hdrlen + 1) << 3, true))
|
||||
return false;
|
||||
|
||||
srh_state->valid = true;
|
||||
|
|
@ -654,7 +654,7 @@ static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt)
|
|||
if (len < sizeof(*srh) + sizeof(struct in6_addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (!seg6_validate_srh(srh, len))
|
||||
if (!seg6_validate_srh(srh, len, false))
|
||||
return -EINVAL;
|
||||
|
||||
slwt->srh = kmemdup(srh, len, GFP_KERNEL);
|
||||
|
|
|
|||
Loading…
Reference in a new issue