diff mbox series

FYI 6.4 xfrm_prepare_input/xfrm_inner_mode_encap_remove WARN_ON hit - related to ESPinUDP

Message ID 20230630153759.3349299-1-maze@google.com (mailing list archive)
State RFC
Delegated to: Netdev Maintainers
Headers show
Series FYI 6.4 xfrm_prepare_input/xfrm_inner_mode_encap_remove WARN_ON hit - related to ESPinUDP | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 13 this patch: 13
netdev/cc_maintainers warning 4 maintainers not CCed: kuba@kernel.org edumazet@google.com davem@davemloft.net pabeni@redhat.com
netdev/build_clang fail Errors and warnings before: 58 this patch: 18
netdev/verify_signedoff fail author Signed-off-by missing
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 13 this patch: 13
netdev/checkpatch warning WARNING: Possible repeated word: 'Yan' WARNING: Prefer [subsystem eg: netdev]_warn([subsystem]dev, ... then dev_warn(dev, ... then pr_warn(... to printk(KERN_WARNING ... WARNING: Prefer using '"%s...", __func__' to using 'xfrm_inner_mode_encap_remove', this function's name, in a string WARNING: Prefer using '"%s...", __func__' to using 'xfrm_prepare_input', this function's name, in a string WARNING: line length of 130 exceeds 80 columns WARNING: line length of 164 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Maciej Żenczykowski June 30, 2023, 3:37 p.m. UTC
Steffan, this isn't of course a patch meant for inclusion, instead just a WARN_ON hit report.
The patch is simply what prints the following extra info:

xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17

(note: XFRM_MODE_TUNNEL=1 IPPROTO_UDP=17)

Hit on Linux 6.4 by:
  https://cs.android.com/android/platform/superproject/+/master:kernel/tests/net/test/xfrm_test.py

likely related to line 230:
  encap_sock.setsockopt(IPPROTO_UDP, xfrm.UDP_ENCAP, xfrm.UDP_ENCAP_ESPINUDP)

I'm not the author of these tests, and I know very little about XFRM.
As such, I'm not sure if there isn't a bug in the tests themselves...
maybe we're generating invalid packets that aren't meant to be decapsulated???

Or are we missing some sort of assignment inside of the ESP in UDP decap codepath?

Somewhere in the vicinity of xfrm4_udp_encap_rcv / xfrm4_rcv_encap
(and the v6 equivalents)

$ git log --decorate --oneline --graph -n 3
* c1ae64591391 (HEAD) FYI 6.4 xfrm_prepare_input/xfrm_inner_mode_encap_remove WARN_ON hit - related to ESPinUDP
* da2779e6377a ANDROID: net: xfrm: make PF_KEY SHA256 use RFC-compliant truncation.  [trivial: .icv_truncbits = 96 -> 128 -- just ignore it]
* 6995e2de6891 (tag: remotes/linux/v6.4) Linux 6.4

Full call stack from running the Android net test suite on a 6.4 UML via:
  ARCH=um SUBARCH=x86_64 /aosp-tests/net/test/run_net_test.sh --builder all_tests.sh

*#### ./xfrm_test.py (11/24)

.........xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
------------[ cut here ]------------
WARNING: CPU: 0 PID: 326 at net/xfrm/xfrm_input.c:380 xfrm_input+0x7fc/0x115d
Modules linked in:
CPU: 0 PID: 326 Comm: xfrm_test.py Not tainted 6.4.0-gc1ae64591391 #8
Stack:
 604bc85a 605bc4ee 818ef5b0 604a3aea
 818ef650 605bc4ee 60037b59 604bc85a
 00000001 605ae79b 818ef5e0 604c3327
Call Trace:
 [<604bc85a>] ? _printk+0x0/0x94
 [<60025a79>] show_stack+0x113/0x18b
 [<604bc85a>] ? _printk+0x0/0x94
 [<604a3aea>] ? dump_stack_print_info+0xe1/0xf0
 [<60037b59>] ? um_set_signals+0x0/0x3e
 [<604bc85a>] ? _printk+0x0/0x94
 [<604c3327>] dump_stack_lvl+0x4a/0x58
 [<604c32dd>] ? dump_stack_lvl+0x0/0x58
 [<60041747>] ? check_panic_on_warn+0x0/0x6f
 [<604c334f>] dump_stack+0x1a/0x1c
 [<600419b4>] __warn+0xcd/0x118
 [<604bbc1e>] warn_slowpath_fmt+0xe4/0xf2
 [<604bbb3a>] ? warn_slowpath_fmt+0x0/0xf2
 [<604bc85a>] ? _printk+0x0/0x94
 [<603e2958>] ? esp_input+0x28f/0x2ac
 [<603fbae9>] ? xfrm_aevent_is_on+0x21/0x26
 [<603fc087>] ? xfrm_replay_advance+0xf2/0x26a
 [<603f99cf>] xfrm_input+0x7fc/0x115d
 [<6040334e>] xfrmi_input+0xa3/0xb2
 [<60403373>] xfrmi4_input+0x16/0x18
 [<603eb9f0>] xfrm4_rcv_encap+0xb5/0xea
 [<603eb102>] ? xfrm4_dst_destroy+0x67/0xeb
 [<603eb39c>] xfrm4_udp_encap_rcv+0x1c6/0x1e6
 [<603eb1d6>] ? xfrm4_udp_encap_rcv+0x0/0x1e6
 [<603b60f8>] udp_queue_rcv_one_skb+0x77/0x339
 [<603b7957>] udp_queue_rcv_skb+0x5c/0x298
 [<603b7c01>] udp_unicast_rcv_skb+0x6e/0x7c
 [<603b962a>] __udp4_lib_rcv+0x613/0x6fe
 [<603b3641>] ? raw_local_deliver+0x0/0x1c7
 [<603b9a0c>] udp_rcv+0x27/0x29
 [<603809eb>] ip_protocol_deliver_rcu+0x77/0x104
 [<60380b4c>] ip_local_deliver_finish+0xd4/0xdb
 [<60380a78>] ? ip_local_deliver_finish+0x0/0xdb
 [<6037fe91>] NF_HOOK.constprop.0+0x76/0x81
 [<60380a78>] ? ip_local_deliver_finish+0x0/0xdb
 [<6037feed>] ip_local_deliver+0x51/0x72
 [<60380371>] ? ip_rcv_finish+0x0/0x3d
 [<603803a9>] ip_rcv_finish+0x38/0x3d
 [<6037fe91>] NF_HOOK.constprop.0+0x76/0x81
 [<60380371>] ? ip_rcv_finish+0x0/0x3d
 [<60380b9e>] ip_rcv+0x4b/0x50
 [<60318d32>] __netif_receive_skb_one_core+0x46/0x4d
 [<60318da2>] __netif_receive_skb+0x55/0x5c
 [<60318df4>] netif_receive_skb+0x4b/0x4f
 [<602c9838>] tun_rx_batched+0x199/0x1b4
 [<602cf02e>] tun_get_user+0xba5/0xcd8
 [<6003a100>] ? userspace_tramp+0x1a6/0x242
 [<60039b1c>] ? do_syscall_stub+0xfa/0x24a
 [<602cf1d7>] tun_chr_write_iter+0x76/0x9a
 [<601410aa>] new_sync_write+0x8f/0x101
 [<601423d2>] vfs_write+0xe7/0x12a
 [<6016239e>] ? __fdget_pos+0x12/0x4c
 [<60142555>] ksys_write+0x79/0xb5
 [<601425a1>] sys_write+0x10/0x12
 [<60027b34>] handle_syscall+0x79/0xa7
 [<6003a9af>] userspace+0x4cf/0x60f
 [<600248bf>] fork_handler+0x92/0x94
---[ end trace 0000000000000000 ]---
xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17
------------[ cut here ]------------
WARNING: CPU: 0 PID: 326 at net/xfrm/xfrm_input.c:352 xfrm_input+0xf2f/0x115d
...

Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Cc: Benedict Wong <benedictwong@google.com>
Cc: Lorenzo Colitti <lorenzo@google.com>
Cc: Yan Yan <evitayan@google.com>
---
 net/xfrm/xfrm_input.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

Maciej Żenczykowski June 30, 2023, 5:05 p.m. UTC | #1
On Fri, Jun 30, 2023 at 5:38 PM Maciej Żenczykowski <maze@google.com> wrote:
> Steffan, this isn't of course a patch meant for inclusion, instead just a WARN_ON hit report.

Sorry for the name typo (it's Stefan in Polish).

> The patch is simply what prints the following extra info:
>
> xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
> xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17
>
> (note: XFRM_MODE_TUNNEL=1 IPPROTO_UDP=17)
>
> Hit on Linux 6.4 by:
>   https://cs.android.com/android/platform/superproject/+/master:kernel/tests/net/test/xfrm_test.py
>
> likely related to line 230:
>   encap_sock.setsockopt(IPPROTO_UDP, xfrm.UDP_ENCAP, xfrm.UDP_ENCAP_ESPINUDP)
>
> I'm not the author of these tests, and I know very little about XFRM.
> As such, I'm not sure if there isn't a bug in the tests themselves...
> maybe we're generating invalid packets that aren't meant to be decapsulated???
>
> Or are we missing some sort of assignment inside of the ESP in UDP decap codepath?
>
> Somewhere in the vicinity of xfrm4_udp_encap_rcv / xfrm4_rcv_encap
> (and the v6 equivalents)

I've done some bisection (well more like educated guesswork)
and the regression (if one should call it that?) is caused by 6.4

commit 5f24f41e8ea62a6a9095f9bbafb8b3aebe265c68
Author: Herbert Xu <herbert@gondor.apana.org.au>
    xfrm: Remove inner/outer modes from input path

The xfrm tests pass either way, but with the above reverted it no
longer triggers the WARN_ON.

$ git log --decorate --oneline --graph -n 3
* da7dc0870b19 (HEAD) Revert "xfrm: Remove inner/outer modes from
input path"  <-- passes, doesn't warn
* 51d5381c5809 ANDROID: net: xfrm: make PF_KEY SHA256 use
RFC-compliant truncation.  <-- passes, does warn
* 5f24f41e8ea6 xfrm: Remove inner/outer modes from input path  <--
passes xfrm, fails pf_key, warns
Herbert Xu July 1, 2023, 7:51 a.m. UTC | #2
On Fri, Jun 30, 2023 at 08:37:58AM -0700, Maciej Żenczykowski wrote:
> Steffan, this isn't of course a patch meant for inclusion, instead just a WARN_ON hit report.
> The patch is simply what prints the following extra info:
> 
> xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
> xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17

This seems to make no sense.  UDP encapsulation is supposed to sit
on the outside of ESP.  So by the time we hit xfrm_input it should
be lone gone.  On the inside of the packet, as it's tunnel mode we
should have either IPIP or IPV6, definitely not UDP.

Are you able to reduce this to a set of "ip xfrm" commands that I
can use to reproduce this?

Thanks,
Maciej Żenczykowski July 1, 2023, 12:27 p.m. UTC | #3
On Sat, Jul 1, 2023 at 9:51 AM Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
> > xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17
>
> This seems to make no sense.  UDP encapsulation is supposed to sit
> on the outside of ESP.  So by the time we hit xfrm_input it should
> be lone gone.  On the inside of the packet, as it's tunnel mode we
> should have either IPIP or IPV6, definitely not UDP.

It's triggering in testIPv4UDPEncapRecvTunnel() in xfrm_test.py.
Specifically, it's the self.ReceivePacketOn(netid, input_pkt) a dozen
lines higher.

The packet we end up writing into the tap fd is
02 00 00 00 C8 01 02 00 00 00 C8 00 08 00
45 00 00 44 00 01 00 00 40 11 98 96 08 08 08 08 0A 00 C8 02
11 94 BF 12 00 30 1C D0
00 00 12 34 00 00 00 01
45 00 00 20 00 01 00 00 40 11 98 BA 08 08 08 08 0A 00 C8 02
01 BB 7D 7B 00 0C 9B 7A
01 02 02 11

You can decode this with https://hpd.gasmi.net/ or https://packetor.com/

You can decode the inner packet (this is null esp crypto) by passing in
00 00 00 00 00 00 00 00 00 00 00 00 08 00
45 00 00 20 00 01 00 00 40 11 98 BA 08 08 08 08 0A 00 C8 02
01 BB 7D 7B 00 0C 9B 7A
01 02 02 11
instead.

Note that the protocol the kernel's printk I added prints is the
*outer* encap UDP protocol, not the inner UDP.
ie. you can change the scapy.UDP to scapy.TCP in the 'inner_pkt =' assignment,
and the warning still triggers.  The resulting packet is:
02 00 00 00 FA 01 02 00 00 00 FA 00 08 00
45 00 00 50 00 01 00 00 40 11 66 8A 08 08 08 08 0A 00 FA 02
11 94 A7 EB 00 3C 33 E0
00 00 12 34 00 00 00 01
45 00 00 2C 00 01 00 00 40 06 66 B9 08 08 08 08 0A 00 FA 02
01 BB 7D 7B 00 00 00 00 00 00 00 00 50 02 20 00 F9 82 00 00
01 02 02 11

ie. the inner packet is IPv4/TCP:
00 00 00 00 00 00 00 00 00 00 00 00 08 00
45 00 00 2C 00 01 00 00 40 06 66 B9 08 08 08 08 0A 00 FA 02
01 BB 7D 7B 00 00 00 00 00 00 00 00 50 02 20 00 F9 82 00 00
01 02 02 11

> Are you able to reduce this to a set of "ip xfrm" commands that I
> can use to reproduce this?
Maciej Żenczykowski July 1, 2023, 12:39 p.m. UTC | #4
On Sat, Jul 1, 2023 at 2:27 PM Maciej Żenczykowski <maze@google.com> wrote:
> On Sat, Jul 1, 2023 at 9:51 AM Herbert Xu <herbert@gondor.apana.org.au> wrote:
> > > xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: 17
> > > xfrm_inner_mode_encap_remove: x->props.mode: 1 XFRM_MODE_SKB_SB(skb)->protocol:17
> >
> > This seems to make no sense.  UDP encapsulation is supposed to sit
> > on the outside of ESP.  So by the time we hit xfrm_input it should
> > be lone gone.  On the inside of the packet, as it's tunnel mode we
> > should have either IPIP or IPV6, definitely not UDP.
>
> It's triggering in testIPv4UDPEncapRecvTunnel() in xfrm_test.py.
> Specifically, it's the self.ReceivePacketOn(netid, input_pkt) a dozen
> lines higher.
>
> The packet we end up writing into the tap fd is
> 02 00 00 00 C8 01 02 00 00 00 C8 00 08 00
> 45 00 00 44 00 01 00 00 40 11 98 96 08 08 08 08 0A 00 C8 02
> 11 94 BF 12 00 30 1C D0
> 00 00 12 34 00 00 00 01
> 45 00 00 20 00 01 00 00 40 11 98 BA 08 08 08 08 0A 00 C8 02
> 01 BB 7D 7B 00 0C 9B 7A
> 01 02 02 11
>
> You can decode this with https://hpd.gasmi.net/ or https://packetor.com/
>
> You can decode the inner packet (this is null esp crypto) by passing in
> 00 00 00 00 00 00 00 00 00 00 00 00 08 00
> 45 00 00 20 00 01 00 00 40 11 98 BA 08 08 08 08 0A 00 C8 02
> 01 BB 7D 7B 00 0C 9B 7A
> 01 02 02 11
> instead.
>
> Note that the protocol the kernel's printk I added prints is the
> *outer* encap UDP protocol, not the inner UDP.
> ie. you can change the scapy.UDP to scapy.TCP in the 'inner_pkt =' assignment,
> and the warning still triggers.  The resulting packet is:
> 02 00 00 00 FA 01 02 00 00 00 FA 00 08 00
> 45 00 00 50 00 01 00 00 40 11 66 8A 08 08 08 08 0A 00 FA 02
> 11 94 A7 EB 00 3C 33 E0
> 00 00 12 34 00 00 00 01
> 45 00 00 2C 00 01 00 00 40 06 66 B9 08 08 08 08 0A 00 FA 02
> 01 BB 7D 7B 00 00 00 00 00 00 00 00 50 02 20 00 F9 82 00 00
> 01 02 02 11
>
> ie. the inner packet is IPv4/TCP:
> 00 00 00 00 00 00 00 00 00 00 00 00 08 00
> 45 00 00 2C 00 01 00 00 40 06 66 B9 08 08 08 08 0A 00 FA 02
> 01 BB 7D 7B 00 00 00 00 00 00 00 00 50 02 20 00 F9 82 00 00
> 01 02 02 11

It looks like the problem is that final '11', and thus the fix is in
the python test itself:

https://android-review.googlesource.com/c/kernel/tests/+/2647762

-      data += xfrm_base.GetEspTrailer(len(data), IPPROTO_UDP)
+      data += xfrm_base.GetEspTrailer(len(data), {4: IPPROTO_IPIP, 6:
IPPROTO_IPV6}[version])

I guess it's OK that this WARN_ON is remotely triggerable?
Herbert Xu July 1, 2023, 12:45 p.m. UTC | #5
On Sat, Jul 01, 2023 at 02:39:36PM +0200, Maciej Żenczykowski wrote:
>
> I guess it's OK that this WARN_ON is remotely triggerable?

That's a good point.

We should audit all the WARN_ONs in net/xfrm and get rid of the
ones that can be triggered by a remote peer.

Cheers,
Maciej Żenczykowski July 1, 2023, 12:59 p.m. UTC | #6
> That's a good point.
>
> We should audit all the WARN_ONs in net/xfrm and get rid of the
> ones that can be triggered by a remote peer.

While I'm looking at this code...  I noticed the comment:

     if mode == xfrm.XFRM_MODE_TRANSPORT:
       # Due to a bug in the IPv6 UDP encap code, there must be at least 32
       # bytes after the ESP header or the packet will be dropped.
       # 8 (UDP header) + 18 (payload) + 2 (ESP trailer) = 28, dropped
       # 8 (UDP header) + 19 (payload) + 4 (ESP trailer) = 32, received
       # There is a similar bug in IPv4 encap, but the minimum is only 12 bytes,
       # which is much less likely to occur. This doesn't affect tunnel mode
       # because IP headers are always at least 20 bytes long.
       data = 19 * b"a"
       datalen = len(data)
       data += xfrm_base.GetEspTrailer(len(data), IPPROTO_UDP)

and indeed reducing the 19 to 18 results in the test failing.
(I'm guessing 'encap' in the comment really should be 'decap'...)

I guess this means short IPv6/ESP transport/UDP fail?

I'll dig a little deeper later...
diff mbox series

Patch

diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 815b38080401..bd10605b211d 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -348,6 +348,7 @@  xfrm_inner_mode_encap_remove(struct xfrm_state *x,
 		}
 	}
 
+	printk(KERN_WARNING "xfrm_inner_mode_encap_remove: x->props.mode: %d XFRM_MODE_SKB_SB(skb)->protocol:%d\n", x->props.mode, XFRM_MODE_SKB_CB(skb)->protocol);
 	WARN_ON_ONCE(1);
 	return -EOPNOTSUPP;
 }
@@ -375,6 +376,7 @@  static int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb)
 		skb->protocol = htons(ETH_P_IPV6);
 		break;
 	default:
+		printk(KERN_WARNING "xfrm_prepare_input: XFRM_MODE_SKB_CB(skb)->protocol: %d\n", XFRM_MODE_SKB_CB(skb)->protocol);
 		WARN_ON_ONCE(1);
 		break;
 	}