From c583c0406215e7652fd508d61f6c1a449cff3f4e Mon Sep 17 00:00:00 2001 From: Sean Tranchetti Date: Wed, 22 Jul 2020 16:22:20 -0600 Subject: [PATCH] core: rmnet: validate ipv6 extension header lengths When calculating the length of the IPv6 header chain, lengths of the IPv6 extension headers are not checked against the overall packet lengths and thus it's possible to parse past the end of the packet when the packet is malformed. This adds the necessary bounds checking to ensure that parsing stops if the end of the packet is reached to avoid the following: Unable to handle kernel paging request at virtual address pc : rmnet_frag_ipv6_skip_exthdr+0xc0/0x108 [rmnet_core] lr : rmnet_frag_ipv6_skip_exthdr+0x68/0x108 [rmnet_core] Call trace: rmnet_frag_ipv6_skip_exthdr+0xc0/0x108 [rmnet_core] DATARMNET29e8d137c4+0x1a0/0x3e0 [rmnet_offload] rmnet_frag_ingress_handler+0x294/0x404 [rmnet_core] rmnet_rx_handler+0x1b4/0x284 [rmnet_core] __netif_receive_skb_core+0x740/0xd2c __netif_receive_skb+0x44/0x158 Change-Id: Ib2e2ebce733bd4d14a3dfc175133638b15015277 Signed-off-by: Sean Tranchetti --- core/rmnet_descriptor.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/rmnet_descriptor.c b/core/rmnet_descriptor.c index 6e2ae12967ae..8d71d89dadbe 100644 --- a/core/rmnet_descriptor.c +++ b/core/rmnet_descriptor.c @@ -103,6 +103,7 @@ EXPORT_SYMBOL(rmnet_descriptor_add_frag); int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc, int start, u8 *nexthdrp, __be16 *fragp) { + u32 frag_size = skb_frag_size(&frag_desc->frag); u8 nexthdr = *nexthdrp; *fragp = 0; @@ -114,11 +115,17 @@ int rmnet_frag_ipv6_skip_exthdr(struct rmnet_frag_descriptor *frag_desc, if (nexthdr == NEXTHDR_NONE) return -EINVAL; - hp = rmnet_frag_data_ptr(frag_desc) + start; + if (start >= frag_size) + return -EINVAL; + hp = rmnet_frag_data_ptr(frag_desc) + start; if (nexthdr == NEXTHDR_FRAGMENT) { __be16 *fp; + if (start + offsetof(struct frag_hdr, frag_off) >= + frag_size) + return -EINVAL; + fp = rmnet_frag_data_ptr(frag_desc) + start + offsetof(struct frag_hdr, frag_off); *fragp = *fp;