VYPR
Unrated severityNVD Advisory· Published May 27, 2026· Updated May 27, 2026

CVE-2026-45998

CVE-2026-45998

Description

In the Linux kernel, the following vulnerability has been resolved:

rxrpc: Fix potential UAF after skb_unshare() failure

If skb_unshare() fails to unshare a packet due to allocation failure in rxrpc_input_packet(), the skb pointer in the parent (rxrpc_io_thread()) will be NULL'd out. This will likely cause the call to trace_rxrpc_rx_done() to oops.

Fix this by moving the unsharing down to where rxrpc_input_call_event() calls rxrpc_input_call_packet(). There are a number of places prior to that where we ignore DATA packets for a variety of reasons (such as the call already being complete) for which an unshare is then avoided.

And with that, rxrpc_input_packet() doesn't need to take a pointer to the pointer to the packet, so change that to just a pointer.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A use-after-free vulnerability in the Linux kernel's rxrpc module when skb_unshare() fails, leading to a NULL pointer dereference in trace_rxrpc_rx_done().

Vulnerability

In the Linux kernel, a use-after-free (UAF) vulnerability exists in the rxrpc (AF_RXRPC) packet handling code. When skb_unshare() fails (e.g., due to memory allocation failure) inside rxrpc_input_packet(), the skb pointer in the parent function rxrpc_io_thread() is set to NULL. This NULL pointer is subsequently dereferenced in trace_rxrpc_rx_done(), causing a kernel oops. The affected code path is in versions prior to the fix commit [1].

Exploitation

An attacker does not need special privileges to trigger this vulnerability; it can be caused by a remote peer sending a crafted packet that leads to a memory allocation failure during skb_unshare(). No authentication is required as the packet processing occurs in the receive path before any call state checks that might filter out certain packets. The attacker must be able to send packets to a socket using the AF_RXRPC protocol.

Impact

Successful exploitation leads to a denial of service (DoS) due to a kernel NULL pointer dereference and oops. The crash occurs in the rxrpc I/O thread, which can cause system instability or a full system crash. There is no indication of information disclosure or privilege escalation from this bug.

Mitigation

The fix is included in the Linux kernel commit [1] (996b0487b3cdda4c91811dbb1c9564626bc840bd). Users should apply the patch or update to a kernel version containing this fix. The commit moves the skb_unshare() call to a later point in the packet processing, avoiding the NULL pointer issue when unsharing fails for ignored packets. No workaround is available for unpatched systems.

AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
8fde6296c4d4

rxrpc: Fix potential UAF after skb_unshare() failure

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid HowellsApr 22, 2026Fixed in 7.0.4via kernel-cna
10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
1f2740150f90

rxrpc: Fix potential UAF after skb_unshare() failure

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid HowellsApr 22, 2026Fixed in 7.1-rc1via kernel-cna
10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 5820d7e41ea09a..13b9d017f8e177 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -162,8 +162,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -190,6 +188,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -198,6 +197,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 5820d7e41ea09a..13b9d017f8e177 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -162,8 +162,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -190,6 +188,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -198,6 +197,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
996b0487b3cd

rxrpc: Fix potential UAF after skb_unshare() failure

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid HowellsApr 22, 2026Fixed in 6.18.27via kernel-cna
10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
bf20f46d94f1

rxrpc: Fix potential UAF after skb_unshare() failure

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid HowellsFixed in 6.12.86via kernel-cna
10 files changed · +52 74
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index ec4cabc3e730d6..9377acad0c5f92 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -127,8 +127,7 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
    +	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -153,6 +152,7 @@
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index ec4cabc3e730d6..9377acad0c5f92 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -127,8 +127,7 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
    +	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -153,6 +152,7 @@
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 1494d162444dd6..63cd5217b4ee3f 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1260,7 +1260,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 1494d162444dd6..63cd5217b4ee3f 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1260,7 +1260,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+22 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 7bbb685047667a..81d77b7449f7e8 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -342,8 +342,28 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     	if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
     		goto out;
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 &&
    +		    skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified for
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	/* If we see our async-event poke, check for timeout trippage. */
     	now = ktime_get_real();
    
  • net/rxrpc/call_event.c+22 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 7bbb685047667a..81d77b7449f7e8 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -342,8 +342,28 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     	if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
     		goto out;
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 &&
    +		    skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified for
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	/* If we see our async-event poke, check for timeout trippage. */
     	now = ktime_get_real();
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 83c5f715c6b731..8270cac0488d94 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -178,13 +178,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -230,25 +229,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -490,7 +470,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 83c5f715c6b731..8270cac0488d94 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -178,13 +178,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -230,25 +229,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -490,7 +470,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
e3bf143b1e98

rxrpc: Fix potential UAF after skb_unshare() failure

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid HowellsFixed in 6.6.140via kernel-cna
10 files changed · +50 74
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 539801f8ee2822..f0560087637ede 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -126,8 +126,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -146,12 +144,14 @@
     	EM(rxrpc_skb_put_jumbo_subpacket,	"PUT jumbo-sub") \
     	EM(rxrpc_skb_put_last_nack,		"PUT last-nack") \
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 539801f8ee2822..f0560087637ede 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -126,8 +126,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -146,12 +144,14 @@
     	EM(rxrpc_skb_put_jumbo_subpacket,	"PUT jumbo-sub") \
     	EM(rxrpc_skb_put_last_nack,		"PUT last-nack") \
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index f4512761f572d6..1db479f3d6d3c0 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1269,7 +1269,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index f4512761f572d6..1db479f3d6d3c0 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1269,7 +1269,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+21 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 0f78544d043be9..c8a4a4c979eb67 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -456,8 +456,27 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     		resend = true;
     	}
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 && skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified by
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	rxrpc_transmit_some_data(call);
     
    
  • net/rxrpc/call_event.c+21 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 0f78544d043be9..c8a4a4c979eb67 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -456,8 +456,27 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     		resend = true;
     	}
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 && skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified by
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	rxrpc_transmit_some_data(call);
     
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 0491f2bbf61e08..f542eda13ff0bf 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -167,13 +167,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -219,25 +218,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -479,7 +459,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 0491f2bbf61e08..f542eda13ff0bf 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -167,13 +167,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -219,25 +218,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -479,7 +459,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
1f2740150f90

rxrpc: Fix potential UAF after skb_unshare() failure

10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 5820d7e41ea09a..13b9d017f8e177 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -162,8 +162,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -190,6 +188,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -198,6 +197,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 5820d7e41ea09a..13b9d017f8e177 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -162,8 +162,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -190,6 +188,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -198,6 +197,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
996b0487b3cd

rxrpc: Fix potential UAF after skb_unshare() failure

10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
8fde6296c4d4

rxrpc: Fix potential UAF after skb_unshare() failure

10 files changed · +44 72
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 578b8038b21178..8d77828b751551 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -161,8 +161,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
    @@ -189,6 +187,7 @@
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
     	EM(rxrpc_skb_put_purge_oob,		"PUT purge-oob") \
     	EM(rxrpc_skb_put_response,		"PUT response ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
    @@ -197,6 +196,7 @@
     	EM(rxrpc_skb_see_recvmsg_oob,		"SEE recvm-oob") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 96ecb83c907153..27c2aa2dd023c5 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1486,7 +1486,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/call_event.c+18 1 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index fec59d9338b9fb..cc8f9dfa44e8a0 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -332,7 +332,24 @@ bool rxrpc_input_call_event(struct rxrpc_call *call)
     
     			saw_ack |= sp->hdr.type == RXRPC_PACKET_TYPE_ACK;
     
    -			rxrpc_input_call_packet(call, skb);
    +			if (sp->hdr.securityIndex != 0 &&
    +			    skb_cloned(skb)) {
    +				/* Unshare the packet so that it can be
    +				 * modified by in-place decryption.
    +				 */
    +				struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +				if (nskb) {
    +					rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +					rxrpc_input_call_packet(call, nskb);
    +					rxrpc_free_skb(nskb, rxrpc_skb_put_call_rx);
    +				} else {
    +					/* OOM - Drop the packet. */
    +					rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +				}
    +			} else {
    +				rxrpc_input_call_packet(call, skb);
    +			}
     			rxrpc_free_skb(skb, rxrpc_skb_put_call_rx);
     			did_receive = true;
     		}
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 69795693192521..dc5184a2fa9d1a 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -192,13 +192,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -244,25 +243,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -494,7 +474,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
bf20f46d94f1

rxrpc: Fix potential UAF after skb_unshare() failure

10 files changed · +52 74
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index ec4cabc3e730d6..9377acad0c5f92 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -127,8 +127,7 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
    +	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -153,6 +152,7 @@
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index ec4cabc3e730d6..9377acad0c5f92 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -127,8 +127,7 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
    +	EM(rxrpc_skb_get_call_rx,		"GET call-rx  ") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -153,6 +152,7 @@
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 1494d162444dd6..63cd5217b4ee3f 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1260,7 +1260,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index 1494d162444dd6..63cd5217b4ee3f 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1260,7 +1260,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+22 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 7bbb685047667a..81d77b7449f7e8 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -342,8 +342,28 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     	if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
     		goto out;
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 &&
    +		    skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified for
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	/* If we see our async-event poke, check for timeout trippage. */
     	now = ktime_get_real();
    
  • net/rxrpc/call_event.c+22 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 7bbb685047667a..81d77b7449f7e8 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -342,8 +342,28 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     	if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
     		goto out;
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 &&
    +		    skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified for
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	/* If we see our async-event poke, check for timeout trippage. */
     	now = ktime_get_real();
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 83c5f715c6b731..8270cac0488d94 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -178,13 +178,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -230,25 +229,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -490,7 +470,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 83c5f715c6b731..8270cac0488d94 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -178,13 +178,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -230,25 +229,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -490,7 +470,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
e3bf143b1e98

rxrpc: Fix potential UAF after skb_unshare() failure

10 files changed · +50 74
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 539801f8ee2822..f0560087637ede 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -126,8 +126,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -146,12 +144,14 @@
     	EM(rxrpc_skb_put_jumbo_subpacket,	"PUT jumbo-sub") \
     	EM(rxrpc_skb_put_last_nack,		"PUT last-nack") \
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • include/trace/events/rxrpc.h+2 2 modified
    diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
    index 539801f8ee2822..f0560087637ede 100644
    --- a/include/trace/events/rxrpc.h
    +++ b/include/trace/events/rxrpc.h
    @@ -126,8 +126,6 @@
     	E_(rxrpc_call_poke_timer_now,		"Timer-now")
     
     #define rxrpc_skb_traces \
    -	EM(rxrpc_skb_eaten_by_unshare,		"ETN unshare  ") \
    -	EM(rxrpc_skb_eaten_by_unshare_nomem,	"ETN unshar-nm") \
     	EM(rxrpc_skb_get_conn_secured,		"GET conn-secd") \
     	EM(rxrpc_skb_get_conn_work,		"GET conn-work") \
     	EM(rxrpc_skb_get_last_nack,		"GET last-nack") \
    @@ -146,12 +144,14 @@
     	EM(rxrpc_skb_put_jumbo_subpacket,	"PUT jumbo-sub") \
     	EM(rxrpc_skb_put_last_nack,		"PUT last-nack") \
     	EM(rxrpc_skb_put_purge,			"PUT purge    ") \
    +	EM(rxrpc_skb_put_response_copy,		"PUT resp-cpy ") \
     	EM(rxrpc_skb_put_rotate,		"PUT rotate   ") \
     	EM(rxrpc_skb_put_unknown,		"PUT unknown  ") \
     	EM(rxrpc_skb_see_conn_work,		"SEE conn-work") \
     	EM(rxrpc_skb_see_recvmsg,		"SEE recvmsg  ") \
     	EM(rxrpc_skb_see_reject,		"SEE reject   ") \
     	EM(rxrpc_skb_see_rotate,		"SEE rotate   ") \
    +	EM(rxrpc_skb_see_unshare_nomem,		"SEE unshar-nm") \
     	E_(rxrpc_skb_see_version,		"SEE version  ")
     
     #define rxrpc_local_traces \
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index f4512761f572d6..1db479f3d6d3c0 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1269,7 +1269,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/ar-internal.h+0 1 modified
    diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
    index f4512761f572d6..1db479f3d6d3c0 100644
    --- a/net/rxrpc/ar-internal.h
    +++ b/net/rxrpc/ar-internal.h
    @@ -1269,7 +1269,6 @@ int rxrpc_server_keyring(struct rxrpc_sock *, sockptr_t, int);
     void rxrpc_kernel_data_consumed(struct rxrpc_call *, struct sk_buff *);
     void rxrpc_new_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_see_skb(struct sk_buff *, enum rxrpc_skb_trace);
    -void rxrpc_eaten_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_get_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_free_skb(struct sk_buff *, enum rxrpc_skb_trace);
     void rxrpc_purge_queue(struct sk_buff_head *);
    
  • net/rxrpc/call_event.c+21 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 0f78544d043be9..c8a4a4c979eb67 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -456,8 +456,27 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     		resend = true;
     	}
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 && skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified by
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	rxrpc_transmit_some_data(call);
     
    
  • net/rxrpc/call_event.c+21 2 modified
    diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
    index 0f78544d043be9..c8a4a4c979eb67 100644
    --- a/net/rxrpc/call_event.c
    +++ b/net/rxrpc/call_event.c
    @@ -456,8 +456,27 @@ bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
     		resend = true;
     	}
     
    -	if (skb)
    -		rxrpc_input_call_packet(call, skb);
    +	if (skb) {
    +		struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
    +
    +		if (sp->hdr.securityIndex != 0 && skb_cloned(skb)) {
    +			/* Unshare the packet so that it can be modified by
    +			 * in-place decryption.
    +			 */
    +			struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC);
    +
    +			if (nskb) {
    +				rxrpc_new_skb(nskb, rxrpc_skb_new_unshared);
    +				rxrpc_input_call_packet(call, nskb);
    +				rxrpc_free_skb(nskb, rxrpc_skb_put_input);
    +			} else {
    +				/* OOM - Drop the packet. */
    +				rxrpc_see_skb(skb, rxrpc_skb_see_unshare_nomem);
    +			}
    +		} else {
    +			rxrpc_input_call_packet(call, skb);
    +		}
    +	}
     
     	rxrpc_transmit_some_data(call);
     
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 0491f2bbf61e08..f542eda13ff0bf 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -167,13 +167,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -219,25 +218,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -479,7 +459,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/io_thread.c+2 22 modified
    diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
    index 0491f2bbf61e08..f542eda13ff0bf 100644
    --- a/net/rxrpc/io_thread.c
    +++ b/net/rxrpc/io_thread.c
    @@ -167,13 +167,12 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
     /*
      * Process packets received on the local endpoint
      */
    -static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
    +static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff *skb)
     {
     	struct rxrpc_connection *conn;
     	struct sockaddr_rxrpc peer_srx;
     	struct rxrpc_skb_priv *sp;
     	struct rxrpc_peer *peer = NULL;
    -	struct sk_buff *skb = *_skb;
     	bool ret = false;
     
     	skb_pull(skb, sizeof(struct udphdr));
    @@ -219,25 +218,6 @@ static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
     		if (sp->hdr.seq == 0)
     			return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
    -
    -		/* Unshare the packet so that it can be modified for in-place
    -		 * decryption.
    -		 */
    -		if (sp->hdr.securityIndex != 0) {
    -			skb = skb_unshare(skb, GFP_ATOMIC);
    -			if (!skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
    -				*_skb = NULL;
    -				return just_discard;
    -			}
    -
    -			if (skb != *_skb) {
    -				rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare);
    -				*_skb = skb;
    -				rxrpc_new_skb(skb, rxrpc_skb_new_unshared);
    -				sp = rxrpc_skb(skb);
    -			}
    -		}
     		break;
     
     	case RXRPC_PACKET_TYPE_CHALLENGE:
    @@ -479,7 +459,7 @@ int rxrpc_io_thread(void *data)
     			switch (skb->mark) {
     			case RXRPC_SKB_MARK_PACKET:
     				skb->priority = 0;
    -				if (!rxrpc_input_packet(local, &skb))
    +				if (!rxrpc_input_packet(local, skb))
     					rxrpc_reject_packet(local, skb);
     				trace_rxrpc_rx_done(skb->mark, skb->priority);
     				rxrpc_free_skb(skb, rxrpc_skb_put_input);
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    
  • net/rxrpc/skbuff.c+0 10 modified
    diff --git a/net/rxrpc/skbuff.c b/net/rxrpc/skbuff.c
    index 3bcd6ee803960b..e2169d1a14b5fd 100644
    --- a/net/rxrpc/skbuff.c
    +++ b/net/rxrpc/skbuff.c
    @@ -46,15 +46,6 @@ void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
     	skb_get(skb);
     }
     
    -/*
    - * Note the dropping of a ref on a socket buffer by the core.
    - */
    -void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace why)
    -{
    -	int n = atomic_inc_return(&rxrpc_n_rx_skbs);
    -	trace_rxrpc_skb(skb, 0, n, why);
    -}
    -
     /*
      * Note the destruction of a socket buffer.
      */
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing NULL-check after skb_unshare() failure allows the caller's skb pointer to be NULL'd out, leading to a NULL-pointer dereference in trace_rxrpc_rx_done()."

Attack vector

An attacker sends a crafted RxRPC DATA packet with a non-zero securityIndex to a system running a vulnerable kernel. When the kernel's `rxrpc_io_thread()` processes the packet, `rxrpc_input_packet()` calls `skb_unshare()` to obtain a writable copy for in-place decryption. If memory allocation fails, `skb_unshare()` returns NULL, and the old code sets the caller's skb pointer to NULL. The subsequent call to `trace_rxrpc_rx_done(skb->mark, skb->priority)` then dereferences the NULL pointer, causing a kernel oops (use-after-free style NULL dereference). No authentication is required; the attacker only needs to send a valid UDP packet to the RxRPC port.

Affected code

The vulnerability is in `net/rxrpc/io_thread.c` in the `rxrpc_input_packet()` function and its caller in `rxrpc_io_thread()`. The old code passed `&skb` (pointer-to-pointer) to `rxrpc_input_packet()`, which could NULL out the caller's skb pointer on `skb_unshare()` failure, leading to a NULL dereference in `trace_rxrpc_rx_done()`. The fix also modifies `net/rxrpc/call_event.c` in `rxrpc_input_call_event()` to perform the unshare/copy there instead [patch_id=2660568].

What the fix does

The patch removes the `skb_unshare()` call from `rxrpc_input_packet()` in `net/rxrpc/io_thread.c` and moves the unsharing logic into `rxrpc_input_call_event()` in `net/rxrpc/call_event.c`. Instead of using `skb_unshare()` (which can NULL the original pointer on failure), the fix uses `skb_copy()` to create a private copy only when the skb is cloned and the packet has a non-zero securityIndex. If `skb_copy()` fails (OOM), the packet is simply dropped via `rxrpc_see_skb()` without modifying the caller's skb pointer. This eliminates the NULL-dereference path because the original skb pointer in `rxrpc_io_thread()` is never overwritten [patch_id=2660568]. The function signature of `rxrpc_input_packet()` is also simplified from `struct sk_buff **_skb` to `struct sk_buff *skb`.

Preconditions

  • networkThe attacker must be able to send UDP packets to the RxRPC port on the target machine.
  • inputThe kernel must be low on memory (GFP_ATOMIC allocation failure) at the time of packet processing.

Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.