[net,v2] ipv4: ip_gre: Avoid skb_pull() failure in ipgre_xmit()

Message ID 20231202161441.221135-1-syoshida@redhat.com
State New
Headers
Series [net,v2] ipv4: ip_gre: Avoid skb_pull() failure in ipgre_xmit() |

Commit Message

Shigeru Yoshida Dec. 2, 2023, 4:14 p.m. UTC
  In ipgre_xmit(), skb_pull() may fail even if pskb_inet_may_pull() returns
true. For example, applications can use PF_PACKET to create a malformed
packet with no IP header. This type of packet causes a problem such as
uninit-value access.

This patch ensures that skb_pull() can pull the required size by checking
the skb with pskb_network_may_pull() before skb_pull().

Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
Signed-off-by: Shigeru Yoshida <syoshida@redhat.com>
---
v1 -> v2:
- Change the title
- Update the code with Eric's suggestion
  https://lore.kernel.org/all/20231126151652.372783-1-syoshida@redhat.com/
---
 net/ipv4/ip_gre.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)
  

Comments

Suman Ghosh Dec. 3, 2023, 6:58 a.m. UTC | #1
Hi Shigeru,

>diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index
>22a26d1d29a0..5169c3c72cff 100644
>--- a/net/ipv4/ip_gre.c
>+++ b/net/ipv4/ip_gre.c
>@@ -635,15 +635,18 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
> 	}
>
> 	if (dev->header_ops) {
>+		int pull_len = tunnel->hlen + sizeof(struct iphdr);
>+
> 		if (skb_cow_head(skb, 0))
> 			goto free_skb;
>
> 		tnl_params = (const struct iphdr *)skb->data;
>
>-		/* Pull skb since ip_tunnel_xmit() needs skb->data pointing
>-		 * to gre header.
>-		 */
>-		skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
>+		if (!pskb_network_may_pull(skb, pull_len))
[Suman] Since this is transmit path, should we add unlikely() here?
>+			goto free_skb;
>+
>+		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
>+		skb_pull(skb, pull_len);
> 		skb_reset_mac_header(skb);
>
> 		if (skb->ip_summed == CHECKSUM_PARTIAL &&
>--
>2.41.0
>
  
Eric Dumazet Dec. 3, 2023, 11:03 a.m. UTC | #2
On Sun, Dec 3, 2023 at 7:58 AM Suman Ghosh <sumang@marvell.com> wrote:
>
> Hi Shigeru,
>
> >diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index
> >22a26d1d29a0..5169c3c72cff 100644
> >--- a/net/ipv4/ip_gre.c
> >+++ b/net/ipv4/ip_gre.c
> >@@ -635,15 +635,18 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
> >       }
> >
> >       if (dev->header_ops) {
> >+              int pull_len = tunnel->hlen + sizeof(struct iphdr);
> >+
> >               if (skb_cow_head(skb, 0))
> >                       goto free_skb;
> >
> >               tnl_params = (const struct iphdr *)skb->data;
> >
> >-              /* Pull skb since ip_tunnel_xmit() needs skb->data pointing
> >-               * to gre header.
> >-               */
> >-              skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
> >+              if (!pskb_network_may_pull(skb, pull_len))
> [Suman] Since this is transmit path, should we add unlikely() here?

Adding unlikely() is not needed, it is already done generically from
the inline helpers.

Reviewed-by: Eric Dumazet <edumazet@google.com>
  
Shigeru Yoshida Dec. 3, 2023, 11:54 a.m. UTC | #3
Hi Suman,

On Sun, 3 Dec 2023 06:58:19 +0000, Suman Ghosh wrote:
> Hi Shigeru,
> 
>>diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index
>>22a26d1d29a0..5169c3c72cff 100644
>>--- a/net/ipv4/ip_gre.c
>>+++ b/net/ipv4/ip_gre.c
>>@@ -635,15 +635,18 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
>> 	}
>>
>> 	if (dev->header_ops) {
>>+		int pull_len = tunnel->hlen + sizeof(struct iphdr);
>>+
>> 		if (skb_cow_head(skb, 0))
>> 			goto free_skb;
>>
>> 		tnl_params = (const struct iphdr *)skb->data;
>>
>>-		/* Pull skb since ip_tunnel_xmit() needs skb->data pointing
>>-		 * to gre header.
>>-		 */
>>-		skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
>>+		if (!pskb_network_may_pull(skb, pull_len))
> [Suman] Since this is transmit path, should we add unlikely() here?

Thanks for your comment.

I traced this function and found that pskb_may_pull_reason() seems to
have appropriate likely() and unlikely() as Eric says.

I'm new to Linux networking. Could you kindly explain the background
of your suggestion?

I understand that a transmit path must be as fast as possible, so we
should use unlikely() for rare cases such like this error path. Am I
correct?

Thanks,
Shigeru

>>+			goto free_skb;
>>+
>>+		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
>>+		skb_pull(skb, pull_len);
>> 		skb_reset_mac_header(skb);
>>
>> 		if (skb->ip_summed == CHECKSUM_PARTIAL &&
>>--
>>2.41.0
>>
>
  
Suman Ghosh Dec. 3, 2023, 3:17 p.m. UTC | #4
>>> 	}
>>>
>>> 	if (dev->header_ops) {
>>>+		int pull_len = tunnel->hlen + sizeof(struct iphdr);
>>>+
>>> 		if (skb_cow_head(skb, 0))
>>> 			goto free_skb;
>>>
>>> 		tnl_params = (const struct iphdr *)skb->data;
>>>
>>>-		/* Pull skb since ip_tunnel_xmit() needs skb->data pointing
>>>-		 * to gre header.
>>>-		 */
>>>-		skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
>>>+		if (!pskb_network_may_pull(skb, pull_len))
>> [Suman] Since this is transmit path, should we add unlikely() here?
>
>Thanks for your comment.
>
>I traced this function and found that pskb_may_pull_reason() seems to
>have appropriate likely() and unlikely() as Eric says.
>
>I'm new to Linux networking. Could you kindly explain the background of
>your suggestion?
>
>I understand that a transmit path must be as fast as possible, so we
>should use unlikely() for rare cases such like this error path. Am I
>correct?
>
>Thanks,
>Shigeru
[Suman] Yes. Likely()/unlikely() helps the compiler for branch prediction and we use it mostly on the data path.
But I cross checked that this is static inline and the function pskb_may_pull() already have likely()/unlikely() in place.
So, you can ignore my comment here.
>
>>>+			goto free_skb;
>>>+
>>>+		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
>>>+		skb_pull(skb, pull_len);
>>> 		skb_reset_mac_header(skb);
>>>
>>> 		if (skb->ip_summed == CHECKSUM_PARTIAL &&
>>>--
>>>2.41.0
>>>
>>
  
Suman Ghosh Dec. 3, 2023, 3:18 p.m. UTC | #5
>In ipgre_xmit(), skb_pull() may fail even if pskb_inet_may_pull()
>returns true. For example, applications can use PF_PACKET to create a
>malformed packet with no IP header. This type of packet causes a problem
>such as uninit-value access.
>
>This patch ensures that skb_pull() can pull the required size by
>checking the skb with pskb_network_may_pull() before skb_pull().
>
>Fixes: c54419321455 ("GRE: Refactor GRE tunneling code.")
>Signed-off-by: Shigeru Yoshida <syoshida@redhat.com>
>---
Reviewed-by: Suman Ghosh <sumang@marvell.com>

>v1 -> v2:
>- Change the title
>- Update the code with Eric's suggestion
>
  
Shigeru Yoshida Dec. 3, 2023, 3:38 p.m. UTC | #6
On Sun, 3 Dec 2023 15:17:09 +0000, Suman Ghosh wrote:
>>>> 	}
>>>>
>>>> 	if (dev->header_ops) {
>>>>+		int pull_len = tunnel->hlen + sizeof(struct iphdr);
>>>>+
>>>> 		if (skb_cow_head(skb, 0))
>>>> 			goto free_skb;
>>>>
>>>> 		tnl_params = (const struct iphdr *)skb->data;
>>>>
>>>>-		/* Pull skb since ip_tunnel_xmit() needs skb->data pointing
>>>>-		 * to gre header.
>>>>-		 */
>>>>-		skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
>>>>+		if (!pskb_network_may_pull(skb, pull_len))
>>> [Suman] Since this is transmit path, should we add unlikely() here?
>>
>>Thanks for your comment.
>>
>>I traced this function and found that pskb_may_pull_reason() seems to
>>have appropriate likely() and unlikely() as Eric says.
>>
>>I'm new to Linux networking. Could you kindly explain the background of
>>your suggestion?
>>
>>I understand that a transmit path must be as fast as possible, so we
>>should use unlikely() for rare cases such like this error path. Am I
>>correct?
>>
>>Thanks,
>>Shigeru
> [Suman] Yes. Likely()/unlikely() helps the compiler for branch prediction and we use it mostly on the data path.
> But I cross checked that this is static inline and the function pskb_may_pull() already have likely()/unlikely() in place.
> So, you can ignore my comment here.

Thank you for your explanation. It is very informative. And thanks for
the review as well.

Shigeru

>>
>>>>+			goto free_skb;
>>>>+
>>>>+		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
>>>>+		skb_pull(skb, pull_len);
>>>> 		skb_reset_mac_header(skb);
>>>>
>>>> 		if (skb->ip_summed == CHECKSUM_PARTIAL &&
>>>>--
>>>>2.41.0
>>>>
>>>
>
  
patchwork-bot+netdevbpf@kernel.org Dec. 6, 2023, 9:20 a.m. UTC | #7
Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Sun,  3 Dec 2023 01:14:41 +0900 you wrote:
> In ipgre_xmit(), skb_pull() may fail even if pskb_inet_may_pull() returns
> true. For example, applications can use PF_PACKET to create a malformed
> packet with no IP header. This type of packet causes a problem such as
> uninit-value access.
> 
> This patch ensures that skb_pull() can pull the required size by checking
> the skb with pskb_network_may_pull() before skb_pull().
> 
> [...]

Here is the summary with links:
  - [net,v2] ipv4: ip_gre: Avoid skb_pull() failure in ipgre_xmit()
    https://git.kernel.org/netdev/net/c/80d875cfc9d3

You are awesome, thank you!
  

Patch

diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 22a26d1d29a0..5169c3c72cff 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -635,15 +635,18 @@  static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
 	}
 
 	if (dev->header_ops) {
+		int pull_len = tunnel->hlen + sizeof(struct iphdr);
+
 		if (skb_cow_head(skb, 0))
 			goto free_skb;
 
 		tnl_params = (const struct iphdr *)skb->data;
 
-		/* Pull skb since ip_tunnel_xmit() needs skb->data pointing
-		 * to gre header.
-		 */
-		skb_pull(skb, tunnel->hlen + sizeof(struct iphdr));
+		if (!pskb_network_may_pull(skb, pull_len))
+			goto free_skb;
+
+		/* ip_tunnel_xmit() needs skb->data pointing to gre header. */
+		skb_pull(skb, pull_len);
 		skb_reset_mac_header(skb);
 
 		if (skb->ip_summed == CHECKSUM_PARTIAL &&