Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ipv6: fix field-spanning memcpy warning in AH output

Fix field-spanning memcpy warnings in ah6_output() and
ah6_output_done() where extension headers are copied to/from IPv6
address fields, triggering fortify-string warnings about writes beyond
the 16-byte address fields.

memcpy: detected field-spanning write (size 40) of single field "&top_iph->saddr" at net/ipv6/ah6.c:439 (size 16)
WARNING: CPU: 0 PID: 8838 at net/ipv6/ah6.c:439 ah6_output+0xe7e/0x14e0 net/ipv6/ah6.c:439

The warnings are false positives as the extension headers are
intentionally placed after the IPv6 header in memory. Fix by properly
copying addresses and extension headers separately, and introduce
helper functions to avoid code duplication.

Reported-by: syzbot+01b0667934cdceb4451c@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=01b0667934cdceb4451c
Signed-off-by: Charalampos Mitrodimas <charmitro@posteo.net>
Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com>

authored by

Charalampos Mitrodimas and committed by
Steffen Klassert
2327a3d6 3b5ca25e

+31 -19
+31 -19
net/ipv6/ah6.c
··· 46 46 47 47 #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0])) 48 48 49 + /* Helper to save IPv6 addresses and extension headers to temporary storage */ 50 + static inline void ah6_save_hdrs(struct tmp_ext *iph_ext, 51 + struct ipv6hdr *top_iph, int extlen) 52 + { 53 + if (!extlen) 54 + return; 55 + 56 + #if IS_ENABLED(CONFIG_IPV6_MIP6) 57 + iph_ext->saddr = top_iph->saddr; 58 + #endif 59 + iph_ext->daddr = top_iph->daddr; 60 + memcpy(&iph_ext->hdrs, top_iph + 1, extlen - sizeof(*iph_ext)); 61 + } 62 + 63 + /* Helper to restore IPv6 addresses and extension headers from temporary storage */ 64 + static inline void ah6_restore_hdrs(struct ipv6hdr *top_iph, 65 + struct tmp_ext *iph_ext, int extlen) 66 + { 67 + if (!extlen) 68 + return; 69 + 70 + #if IS_ENABLED(CONFIG_IPV6_MIP6) 71 + top_iph->saddr = iph_ext->saddr; 72 + #endif 73 + top_iph->daddr = iph_ext->daddr; 74 + memcpy(top_iph + 1, &iph_ext->hdrs, extlen - sizeof(*iph_ext)); 75 + } 76 + 49 77 static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags, 50 78 unsigned int size) 51 79 { ··· 329 301 memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 330 302 memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 331 303 332 - if (extlen) { 333 - #if IS_ENABLED(CONFIG_IPV6_MIP6) 334 - memcpy(&top_iph->saddr, iph_ext, extlen); 335 - #else 336 - memcpy(&top_iph->daddr, iph_ext, extlen); 337 - #endif 338 - } 304 + ah6_restore_hdrs(top_iph, iph_ext, extlen); 339 305 340 306 kfree(AH_SKB_CB(skb)->tmp); 341 307 xfrm_output_resume(skb->sk, skb, err); ··· 400 378 */ 401 379 memcpy(iph_base, top_iph, IPV6HDR_BASELEN); 402 380 381 + ah6_save_hdrs(iph_ext, top_iph, extlen); 403 382 if (extlen) { 404 - #if IS_ENABLED(CONFIG_IPV6_MIP6) 405 - memcpy(iph_ext, &top_iph->saddr, extlen); 406 - #else 407 - memcpy(iph_ext, &top_iph->daddr, extlen); 408 - #endif 409 383 err = ipv6_clear_mutable_options(top_iph, 410 384 extlen - sizeof(*iph_ext) + 411 385 sizeof(*top_iph), ··· 452 434 memcpy(ah->auth_data, icv, ahp->icv_trunc_len); 453 435 memcpy(top_iph, iph_base, IPV6HDR_BASELEN); 454 436 455 - if (extlen) { 456 - #if IS_ENABLED(CONFIG_IPV6_MIP6) 457 - memcpy(&top_iph->saddr, iph_ext, extlen); 458 - #else 459 - memcpy(&top_iph->daddr, iph_ext, extlen); 460 - #endif 461 - } 437 + ah6_restore_hdrs(top_iph, iph_ext, extlen); 462 438 463 439 out_free: 464 440 kfree(iph_base);