Skip to content

Commit 1516e31

Browse files
manizadagregkh
authored andcommitted
xfrm: ah6: validate routing header segments_left
commit 7bad4bd upstream. AH6 rearranges routing-header addresses before computing or verifying the ICV. ipv6_rearrange_rthdr() assumes that segments_left is not larger than the number of addresses described by the routing header's hdrlen field. That assumption does not hold for raw IPv6 HDRINCL packets. A packet with hdrlen equal to 2 describes one address, but can carry an arbitrary segments_left value. With segments_left equal to 255, the function moves its address pointer 4,064 bytes backwards and passes a 4,064-byte length to memmove(), resulting in an out-of-bounds access. Validate the invariant locally before modifying the routing header or performing any address-pointer arithmetic, and propagate malformed-header errors to the existing AH6 input and output error paths. Fixes: 1da177e ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Assisted-by: avom-custom-harness:gpt-5.5-qwen3.6-mod-mix Signed-off-by: Asim Viladi Oglu Manizada <manizada@pm.me> Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent ea09210 commit 1516e31

1 file changed

Lines changed: 18 additions & 11 deletions

File tree

net/ipv6/ah6.c

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -232,26 +232,28 @@ static void ipv6_rearrange_destopt(struct ipv6hdr *iph, struct ipv6_opt_hdr *des
232232
* Rearrange the destination address in @iph and the addresses in @rthdr
233233
* so that they appear in the order they will at the final destination.
234234
* See Appendix A2 of RFC 2402 for details.
235+
*
236+
* Return: 0 on success, -EINVAL if segments_left exceeds the number of
237+
* addresses described by hdrlen.
235238
*/
236-
static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
239+
static int ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
237240
{
238-
int segments, segments_left;
241+
unsigned int segments, segments_left;
239242
struct in6_addr *addrs;
240243
struct in6_addr final_addr;
241244

242245
segments_left = rthdr->segments_left;
243246
if (segments_left == 0)
244-
return;
245-
rthdr->segments_left = 0;
247+
return 0;
246248

247-
/* The value of rthdr->hdrlen has been verified either by the system
248-
* call if it is locally generated, or by ipv6_rthdr_rcv() for incoming
249-
* packets. So we can assume that it is even and that segments is
250-
* greater than or equal to segments_left.
251-
*
252-
* For the same reason we can assume that this option is of type 0.
249+
/* Raw locally generated packets can reach AH6 without the invariant
250+
* required by the rt0-style address rearrangement below.
253251
*/
254252
segments = rthdr->hdrlen >> 1;
253+
if (segments_left > segments)
254+
return -EINVAL;
255+
256+
rthdr->segments_left = 0;
255257

256258
addrs = ((struct rt0_hdr *)rthdr)->addr;
257259
final_addr = addrs[segments - 1];
@@ -261,6 +263,8 @@ static void ipv6_rearrange_rthdr(struct ipv6hdr *iph, struct ipv6_rt_hdr *rthdr)
261263

262264
addrs[0] = iph->daddr;
263265
iph->daddr = final_addr;
266+
267+
return 0;
264268
}
265269

266270
static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
@@ -273,6 +277,7 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
273277
} exthdr = { .iph = iph };
274278
char *end = exthdr.raw + len;
275279
int nexthdr = iph->nexthdr;
280+
int err;
276281

277282
exthdr.iph++;
278283

@@ -292,7 +297,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
292297
break;
293298

294299
case NEXTHDR_ROUTING:
295-
ipv6_rearrange_rthdr(iph, exthdr.rth);
300+
err = ipv6_rearrange_rthdr(iph, exthdr.rth);
301+
if (err)
302+
return err;
296303
break;
297304

298305
default:

0 commit comments

Comments
 (0)