Skip to contentSkip to conversation

net: psp: add more validation

PATCH Applied Jakub Kicinski 4 participants 12 messages Apr 28 – Apr 30

Subsystems (3)
lore
Share & tools
lore

Jakub Kicinski

author maintainer
#0 lore raw

cover net: psp: add more validation

Address some AI code-scan issues with the PSP code. I don't think any of these are real bugs, but they may become bugs in the future. The two real bugs discovered were posted separately for net. AI reports 3 more which seem plain wrong (rx SPI "leak" on error etc.).

Jakub Kicinski (3):
psp: validate protocol before mutating skb in psp_dev_encapsulate()
psp: add a comment about a psp_dev add netlink notification
psp: validate IPv4 header fields in psp_dev_rcv()

 net/psp/psp_main.c | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)
Signature · 1 line
2.54.0
author maintainer
#1 lore raw

PATCH 1/3 psp: validate protocol before mutating skb in psp_dev_encapsulate()

Code checkers / AI scans will complain that we have already modified the packet by the time we realize that protocol is not IP.

Move the skb->protocol check to before skb_push()/memmove() so that the skb is not left in a corrupted state when the function returns false for an unsupported protocol. psp_dev_rcv() follows similar pattern.

Today this path is unreachable because both in-tree callers (mlx5 and netdevsim) only reach psp_dev_encapsulate() from TCP socket TX paths where skb->protocol is always ETH_P_IP or ETH_P_IPV6, and both drop the skb on a false return, anyway.

---
 net/psp/psp_main.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/psp/psp_main.c +5 -3
index 9508b6c38003..652ec8a9c8a4 100644
@@ -228,6 +228,10 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
 	u32 ethr_len = skb_mac_header_len(skb);
 	u32 bufflen = ethr_len + network_len;
 
+	if (skb->protocol != htons(ETH_P_IP) &&
+	    skb->protocol != htons(ETH_P_IPV6))
+		return false;
+
 	if (skb_cow_head(skb, PSP_ENCAP_HLEN))
 		return false;
 
@@ -243,11 +247,9 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
 		ip_hdr(skb)->check = 0;
 		ip_hdr(skb)->check =
 			ip_fast_csum((u8 *)ip_hdr(skb), ip_hdr(skb)->ihl);
-	} else if (skb->protocol == htons(ETH_P_IPV6)) {
+	} else {
 		ipv6_hdr(skb)->nexthdr = IPPROTO_UDP;
 		be16_add_cpu(&ipv6_hdr(skb)->payload_len, PSP_ENCAP_HLEN);
-	} else {
-		return false;
 	}
 
 	skb_set_inner_ipproto(skb, IPPROTO_TCP);
Signature · 1 line
2.54.0
Reviewed-by on v1 1/3
#2 lore raw

Re: [PATCH net-next 1/3] psp: validate protocol before mutating skb in psp_dev_encapsulate()

Quoted 9 lines · Jakub Kicinski

Code checkers / AI scans will complain that we have already modified the packet by the time we realize that protocol is not IP.

Move the skb->protocol check to before skb_push()/memmove() so that the skb is not left in a corrupted state when the function returns false for an unsupported protocol. psp_dev_rcv() follows similar pattern.

Quoted 7 lines

Today this path is unreachable because both in-tree callers (mlx5 and netdevsim) only reach psp_dev_encapsulate() from TCP socket TX paths where skb->protocol is always ETH_P_IP or ETH_P_IPV6, and both drop the skb on a false return, anyway.

---

maintainer Reviewed-by on v1 1/3
#3 lore raw

Re: [PATCH net-next 1/3] psp: validate protocol before mutating skb in psp_dev_encapsulate()

In reply to Jakub Kicinski
Quoted 8 lines · Jakub Kicinski

Code checkers / AI scans will complain that we have already modified the packet by the time we realize that protocol is not IP.

Move the skb->protocol check to before skb_push()/memmove() so that the skb is not left in a corrupted state when the function returns false for an unsupported protocol. psp_dev_rcv() follows similar pattern.

Quoted 6 lines

Today this path is unreachable because both in-tree callers (mlx5 and netdevsim) only reach psp_dev_encapsulate() from TCP socket TX paths where skb->protocol is always ETH_P_IP or ETH_P_IPV6, and both drop the skb on a false return, anyway.

Quoted 35 lines
---
 net/psp/psp_main.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/psp/psp_main.c +5 -3
index 9508b6c38003..652ec8a9c8a4 100644
@@ -228,6 +228,10 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
 	u32 ethr_len = skb_mac_header_len(skb);
 	u32 bufflen = ethr_len + network_len;
 
+	if (skb->protocol != htons(ETH_P_IP) &&
+	    skb->protocol != htons(ETH_P_IPV6))
+		return false;
+
 	if (skb_cow_head(skb, PSP_ENCAP_HLEN))
 		return false;
 
@@ -243,11 +247,9 @@ bool psp_dev_encapsulate(struct net *net, struct sk_buff *skb, __be32 spi,
 		ip_hdr(skb)->check = 0;
 		ip_hdr(skb)->check =
 			ip_fast_csum((u8 *)ip_hdr(skb), ip_hdr(skb)->ihl);
-	} else if (skb->protocol == htons(ETH_P_IPV6)) {
+	} else {
 		ipv6_hdr(skb)->nexthdr = IPPROTO_UDP;
 		be16_add_cpu(&ipv6_hdr(skb)->payload_len, PSP_ENCAP_HLEN);
-	} else {
-		return false;
 	}
 
 	skb_set_inner_ipproto(skb, IPPROTO_TCP);
Signature · 1 line
2.54.0
author maintainer
#4 lore raw

PATCH 2/3 psp: add a comment about a psp_dev add netlink notification

In reply to Jakub Kicinski

In psp_dev_create(), the DEV_ADD_NTF netlink notification is sent before the device is published to the netdev via rcu_assign_pointer(). IIRC this is intentional because a single PSP device is expected to be shared with multiple netdevs. So we are trying to default to not having the netdev info. We can change it if someone complains but for now just add a comment that it's intentional.

---
 net/psp/psp_main.c | 4 ++++
 1 file changed, 4 insertions(+)
net/psp/psp_main.c +4 -0
index 652ec8a9c8a4..f069117c867a 100644
@@ -90,6 +90,10 @@ psp_dev_create(struct net_device *netdev,
 	mutex_lock(&psd->lock);
 	mutex_unlock(&psp_devs_lock);
 
+	/* notify before netdev assignment
+	 * There's no strong reason for it, but thinking is to avoid creating
+	 * implicit expectations about the PSP dev <> netdev relationship.
+	 */
 	psp_nl_notify_dev(psd, PSP_CMD_DEV_ADD_NTF);
 
 	rcu_assign_pointer(netdev->psp_dev, psd);
Signature · 1 line
2.54.0
maintainer Reviewed-by on v1 2/3
#5 lore raw

Re: [PATCH net-next 2/3] psp: add a comment about a psp_dev add netlink notification

Quoted 8 lines · Jakub Kicinski

In psp_dev_create(), the DEV_ADD_NTF netlink notification is sent before the device is published to the netdev via rcu_assign_pointer(). IIRC this is intentional because a single PSP device is expected to be shared with multiple netdevs. So we are trying to default to not having the netdev info. We can change it if someone complains but for now just add a comment that it's intentional.

author maintainer
#6 lore raw

PATCH 3/3 psp: validate IPv4 header fields in psp_dev_rcv()

In reply to Jakub Kicinski

psp_dev_rcv() is called from the NIC driver's RX completion path before the frame reaches ip_rcv_core(), so the IP header has not been validated in SW, yet. We expect that the device has done all this validation, but let's also add the SW checks, to avoid surprises.

---
 net/psp/psp_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)
net/psp/psp_main.c +9 -0
index f069117c867a..524978dfb8fd 100644
@@ -300,6 +300,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	if (proto == htons(ETH_P_IP)) {
 		struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen);
 
+		if (unlikely(iph->ihl < 5))
+			return -EINVAL;
+
 		is_udp = iph->protocol == IPPROTO_UDP;
 		l3_hlen = iph->ihl * 4;
 		if (l3_hlen != sizeof(struct iphdr) &&
@@ -335,6 +338,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	if (proto == htons(ETH_P_IP)) {
 		struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen);
 
+		if (unlikely(ntohs(iph->tot_len) < l3_hlen + encap))
+			return -EINVAL;
+
 		iph->protocol = psph->nexthdr;
 		iph->tot_len = htons(ntohs(iph->tot_len) - encap);
 		iph->check = 0;
@@ -342,6 +348,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	} else {
 		struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + l2_hlen);
 
+		if (unlikely(ntohs(ipv6h->payload_len) < encap))
+			return -EINVAL;
+
 		ipv6h->nexthdr = psph->nexthdr;
 		ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) - encap);
 	}
Signature · 1 line
2.54.0
Reviewed-by on v1 3/3
#7 lore raw

Re: [PATCH net-next 3/3] psp: validate IPv4 header fields in psp_dev_rcv()

Quoted 8 lines · Jakub Kicinski

psp_dev_rcv() is called from the NIC driver's RX completion path before the frame reaches ip_rcv_core(), so the IP header has not been validated in SW, yet. We expect that the device has done all this validation, but let's also add the SW checks, to avoid surprises.

maintainer
#8 lore raw

Re: [PATCH net-next 3/3] psp: validate IPv4 header fields in psp_dev_rcv()

In reply to Jakub Kicinski
Quoted 5 lines · Jakub Kicinski

psp_dev_rcv() is called from the NIC driver's RX completion path before the frame reaches ip_rcv_core(), so the IP header has not been validated in SW, yet. We expect that the device has done all this validation, but let's also add the SW checks, to avoid surprises.

If devices are expected to have verified this, should these be more noisy checks, similar to netdev_rx_csum_fault?

Quoted 42 lines
---
 net/psp/psp_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)
net/psp/psp_main.c +9 -0
index f069117c867a..524978dfb8fd 100644
@@ -300,6 +300,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	if (proto == htons(ETH_P_IP)) {
 		struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen);
 
+		if (unlikely(iph->ihl < 5))
+			return -EINVAL;
+
 		is_udp = iph->protocol == IPPROTO_UDP;
 		l3_hlen = iph->ihl * 4;
 		if (l3_hlen != sizeof(struct iphdr) &&
@@ -335,6 +338,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	if (proto == htons(ETH_P_IP)) {
 		struct iphdr *iph = (struct iphdr *)(skb->data + l2_hlen);
 
+		if (unlikely(ntohs(iph->tot_len) < l3_hlen + encap))
+			return -EINVAL;
+
 		iph->protocol = psph->nexthdr;
 		iph->tot_len = htons(ntohs(iph->tot_len) - encap);
 		iph->check = 0;
@@ -342,6 +348,9 @@ int psp_dev_rcv(struct sk_buff *skb, u16 dev_id, u8 generation, bool strip_icv)
 	} else {
 		struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + l2_hlen);
 
+		if (unlikely(ntohs(ipv6h->payload_len) < encap))
+			return -EINVAL;
+
 		ipv6h->nexthdr = psph->nexthdr;
 		ipv6h->payload_len = htons(ntohs(ipv6h->payload_len) - encap);
 	}
Signature · 1 line
2.54.0
author maintainer
#9 lore raw

Re: [PATCH net-next 3/3] psp: validate IPv4 header fields in psp_dev_rcv()

Quoted 9 lines · On Tue, 28 Apr 2026 20:22:34 -0400 Willem de Bruijn wrote:
Quoted 5 lines · Jakub Kicinski

psp_dev_rcv() is called from the NIC driver's RX completion path before the frame reaches ip_rcv_core(), so the IP header has not been validated in SW, yet. We expect that the device has done all this validation, but let's also add the SW checks, to avoid surprises.

If devices are expected to have verified this, should these be more noisy checks, similar to netdev_rx_csum_fault?

Maybe "expect" is a bit of a strong word, I meant "anticipate" / "suspect". Dropping invalid packet in SW doesn't seem like a huge problem, other paths in this function already do. For rx csum the problem is that we got a incorrectly math'ed out value for what is likely a valid packet.

That's just to explain my thinking, if you prefer we warn / dump skb I can respin.

maintainer Reviewed-by
#10 lore raw

Re: [PATCH net-next 3/3] psp: validate IPv4 header fields in psp_dev_rcv()

Quoted 11 lines · Jakub Kicinski
Quoted 9 lines · On Tue, 28 Apr 2026 20:22:34 -0400 Willem de Bruijn wrote:
Quoted 5 lines · Jakub Kicinski

psp_dev_rcv() is called from the NIC driver's RX completion path before the frame reaches ip_rcv_core(), so the IP header has not been validated in SW, yet. We expect that the device has done all this validation, but let's also add the SW checks, to avoid surprises.

If devices are expected to have verified this, should these be more noisy checks, similar to netdev_rx_csum_fault?

Quoted 8 lines

Maybe "expect" is a bit of a strong word, I meant "anticipate" / "suspect". Dropping invalid packet in SW doesn't seem like a huge problem, other paths in this function already do. For rx csum the problem is that we got a incorrectly math'ed out value for what is likely a valid packet.

That's just to explain my thinking, if you prefer we warn / dump skb I can respin.

No, sounds good.

#11 lore raw
In reply to Jakub Kicinski
This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Hello:

This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Quoted 6 lines · On Tue, 28 Apr 2026 13:53:49 -0700 you wrote:

Address some AI code-scan issues with the PSP code. I don't think any of these are real bugs, but they may become bugs in the future. The two real bugs discovered were posted separately for net. AI reports 3 more which seem plain wrong (rx SPI "leak" on error etc.).

Quoted 6 lines

Jakub Kicinski (3):
psp: validate protocol before mutating skb in psp_dev_encapsulate()
psp: add a comment about a psp_dev add netlink notification
psp: validate IPv4 header fields in psp_dev_rcv()

[...]

Here is the summary with links:
- [net-next,1/3] psp: validate protocol before mutating skb in psp_dev_encapsulate()

    https://git.kernel.org/netdev/net-next/c/28e71cb51cdf
  - [net-next,2/3] psp: add a comment about a psp_dev add netlink notification
    https://git.kernel.org/netdev/net-next/c/5637fcb11c91
  - [net-next,3/3] psp: validate IPv4 header fields in psp_dev_rcv()
    https://git.kernel.org/netdev/net-next/c/c2b22277ad89

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html