]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
net: ipv6: fix field-spanning memcpy warning in AH output
authorCharalampos Mitrodimas <charmitro@posteo.net>
Tue, 12 Aug 2025 15:51:25 +0000 (15:51 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 3 Dec 2025 11:45:09 +0000 (12:45 +0100)
[ Upstream commit 2327a3d6f65ce2fe2634546dde4a25ef52296fec ]

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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/ipv6/ah6.c

index 1c5ecd07a43e16e45cc3af65add0c15211f8ad5c..240f46c7ce1e88a5c4f1397b11bb020655637d7d 100644 (file)
@@ -46,6 +46,34 @@ struct ah_skb_cb {
 
 #define AH_SKB_CB(__skb) ((struct ah_skb_cb *)&((__skb)->cb[0]))
 
+/* Helper to save IPv6 addresses and extension headers to temporary storage */
+static inline void ah6_save_hdrs(struct tmp_ext *iph_ext,
+                                struct ipv6hdr *top_iph, int extlen)
+{
+       if (!extlen)
+               return;
+
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+       iph_ext->saddr = top_iph->saddr;
+#endif
+       iph_ext->daddr = top_iph->daddr;
+       memcpy(&iph_ext->hdrs, top_iph + 1, extlen - sizeof(*iph_ext));
+}
+
+/* Helper to restore IPv6 addresses and extension headers from temporary storage */
+static inline void ah6_restore_hdrs(struct ipv6hdr *top_iph,
+                                   struct tmp_ext *iph_ext, int extlen)
+{
+       if (!extlen)
+               return;
+
+#if IS_ENABLED(CONFIG_IPV6_MIP6)
+       top_iph->saddr = iph_ext->saddr;
+#endif
+       top_iph->daddr = iph_ext->daddr;
+       memcpy(top_iph + 1, &iph_ext->hdrs, extlen - sizeof(*iph_ext));
+}
+
 static void *ah_alloc_tmp(struct crypto_ahash *ahash, int nfrags,
                          unsigned int size)
 {
@@ -307,13 +335,7 @@ static void ah6_output_done(struct crypto_async_request *base, int err)
        memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
        memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
 
-       if (extlen) {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-               memcpy(&top_iph->saddr, iph_ext, extlen);
-#else
-               memcpy(&top_iph->daddr, iph_ext, extlen);
-#endif
-       }
+       ah6_restore_hdrs(top_iph, iph_ext, extlen);
 
        kfree(AH_SKB_CB(skb)->tmp);
        xfrm_output_resume(skb, err);
@@ -384,12 +406,8 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
         */
        memcpy(iph_base, top_iph, IPV6HDR_BASELEN);
 
+       ah6_save_hdrs(iph_ext, top_iph, extlen);
        if (extlen) {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-               memcpy(iph_ext, &top_iph->saddr, extlen);
-#else
-               memcpy(iph_ext, &top_iph->daddr, extlen);
-#endif
                err = ipv6_clear_mutable_options(top_iph,
                                                 extlen - sizeof(*iph_ext) +
                                                 sizeof(*top_iph),
@@ -440,13 +458,7 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
        memcpy(ah->auth_data, icv, ahp->icv_trunc_len);
        memcpy(top_iph, iph_base, IPV6HDR_BASELEN);
 
-       if (extlen) {
-#if IS_ENABLED(CONFIG_IPV6_MIP6)
-               memcpy(&top_iph->saddr, iph_ext, extlen);
-#else
-               memcpy(&top_iph->daddr, iph_ext, extlen);
-#endif
-       }
+       ah6_restore_hdrs(top_iph, iph_ext, extlen);
 
 out_free:
        kfree(iph_base);