diff mbox series

[net,2/2] net: ethernet: oa_tc6: fix tx skb race condition between reference pointers

Message ID 20241120135142.586845-3-parthiban.veerasooran@microchip.com (mailing list archive)
State New
Delegated to: Netdev Maintainers
Headers show
Series Fixes on the OPEN Alliance TC6 10BASE-T1x MAC-PHY support generic lib | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag present in non-next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 3 this patch: 3
netdev/build_tools success No tools touched, skip
netdev/cc_maintainers fail 1 blamed authors not CCed: andrew+netdev@lunn.ch; 1 maintainers not CCed: andrew+netdev@lunn.ch
netdev/build_clang success Errors and warnings before: 3 this patch: 3
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 4 this patch: 4
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 8 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2024-11-20--15-00 (tests: 789)

Commit Message

Parthiban Veerasooran Nov. 20, 2024, 1:51 p.m. UTC
There are two skb pointers to manage tx skb's enqueued from n/w stack.
waiting_tx_skb pointer points to the tx skb which needs to be processed
and ongoing_tx_skb pointer points to the tx skb which is being processed.

SPI thread prepares the tx data chunks from the tx skb pointed by the
ongoing_tx_skb pointer. When the tx skb pointed by the ongoing_tx_skb is
processed, the tx skb pointed by the waiting_tx_skb is assigned to
ongoing_tx_skb and the waiting_tx_skb pointer is assigned with NULL.
Whenever there is a new tx skb from n/w stack, it will be assigned to
waiting_tx_skb pointer if it is NULL. Enqueuing and processing of a tx skb
handled in two different threads.

Consider a scenario where the SPI thread processed an ongoing_tx_skb and
it assigns next tx skb from waiting_tx_skb pointer to ongoing_tx_skb
pointer without doing any NULL check. At this time, if the waiting_tx_skb
pointer is NULL then ongoing_tx_skb pointer is also assigned with NULL.
After that, if a new tx skb is assigned to waiting_tx_skb pointer by the
n/w stack and there is a chance to overwrite the tx skb pointer with NULL
in the SPI thread. Finally one of the tx skb will be left as unhandled,
resulting packet missing and memory leak.

To overcome the above issue, check waiting_tx_skb pointer is not NULL
along with ongoing_tx_skb pointer's NULL check before proceeding to assign
the tx skb from waiting_tx_skb pointer to ongoing_tx_skb pointer.

Fixes: 53fbde8ab21e ("net: ethernet: oa_tc6: implement transmit path to transfer tx ethernet frames")
Signed-off-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
---
 drivers/net/ethernet/oa_tc6.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Comments

Jacob Keller Nov. 20, 2024, 7:57 p.m. UTC | #1
On 11/20/2024 5:51 AM, Parthiban Veerasooran wrote:
> There are two skb pointers to manage tx skb's enqueued from n/w stack.
> waiting_tx_skb pointer points to the tx skb which needs to be processed
> and ongoing_tx_skb pointer points to the tx skb which is being processed.
> 
> SPI thread prepares the tx data chunks from the tx skb pointed by the
> ongoing_tx_skb pointer. When the tx skb pointed by the ongoing_tx_skb is
> processed, the tx skb pointed by the waiting_tx_skb is assigned to
> ongoing_tx_skb and the waiting_tx_skb pointer is assigned with NULL.
> Whenever there is a new tx skb from n/w stack, it will be assigned to
> waiting_tx_skb pointer if it is NULL. Enqueuing and processing of a tx skb
> handled in two different threads.
> 
> Consider a scenario where the SPI thread processed an ongoing_tx_skb and
> it assigns next tx skb from waiting_tx_skb pointer to ongoing_tx_skb
> pointer without doing any NULL check. At this time, if the waiting_tx_skb
> pointer is NULL then ongoing_tx_skb pointer is also assigned with NULL.
> After that, if a new tx skb is assigned to waiting_tx_skb pointer by the
> n/w stack and there is a chance to overwrite the tx skb pointer with NULL
> in the SPI thread. Finally one of the tx skb will be left as unhandled,
> resulting packet missing and memory leak.
> 
> To overcome the above issue, check waiting_tx_skb pointer is not NULL
> along with ongoing_tx_skb pointer's NULL check before proceeding to assign
> the tx skb from waiting_tx_skb pointer to ongoing_tx_skb pointer.
> 
> Fixes: 53fbde8ab21e ("net: ethernet: oa_tc6: implement transmit path to transfer tx ethernet frames")
> Signed-off-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
> ---
>  drivers/net/ethernet/oa_tc6.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
> index 4c8b0ca922b7..e1e7c6e07966 100644
> --- a/drivers/net/ethernet/oa_tc6.c
> +++ b/drivers/net/ethernet/oa_tc6.c
> @@ -1003,7 +1003,7 @@ static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6)
>  	 */
>  	for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits;
>  	     used_tx_credits++) {
> -		if (!tc6->ongoing_tx_skb) {
> +		if (!tc6->ongoing_tx_skb && tc6->waiting_tx_skb) {
>  			tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
>  			tc6->waiting_tx_skb = NULL;

It is unclear to me how this additional check completely resolves race
conditions? Is there some other locking or synchronization such the
second thread cannot have updated waiting_tx_skb either prior or after
this check?

This feels like you want some sort of atomic exchange operation...

>  		}
Parthiban Veerasooran Nov. 21, 2024, 4:54 a.m. UTC | #2
Hi Jacob Keller,

On 21/11/24 1:27 am, Jacob Keller wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On 11/20/2024 5:51 AM, Parthiban Veerasooran wrote:
>> There are two skb pointers to manage tx skb's enqueued from n/w stack.
>> waiting_tx_skb pointer points to the tx skb which needs to be processed
>> and ongoing_tx_skb pointer points to the tx skb which is being processed.
>>
>> SPI thread prepares the tx data chunks from the tx skb pointed by the
>> ongoing_tx_skb pointer. When the tx skb pointed by the ongoing_tx_skb is
>> processed, the tx skb pointed by the waiting_tx_skb is assigned to
>> ongoing_tx_skb and the waiting_tx_skb pointer is assigned with NULL.
>> Whenever there is a new tx skb from n/w stack, it will be assigned to
>> waiting_tx_skb pointer if it is NULL. Enqueuing and processing of a tx skb
>> handled in two different threads.
>>
>> Consider a scenario where the SPI thread processed an ongoing_tx_skb and
>> it assigns next tx skb from waiting_tx_skb pointer to ongoing_tx_skb
>> pointer without doing any NULL check. At this time, if the waiting_tx_skb
>> pointer is NULL then ongoing_tx_skb pointer is also assigned with NULL.
>> After that, if a new tx skb is assigned to waiting_tx_skb pointer by the
>> n/w stack and there is a chance to overwrite the tx skb pointer with NULL
>> in the SPI thread. Finally one of the tx skb will be left as unhandled,
>> resulting packet missing and memory leak.
>>
>> To overcome the above issue, check waiting_tx_skb pointer is not NULL
>> along with ongoing_tx_skb pointer's NULL check before proceeding to assign
>> the tx skb from waiting_tx_skb pointer to ongoing_tx_skb pointer.
>>
>> Fixes: 53fbde8ab21e ("net: ethernet: oa_tc6: implement transmit path to transfer tx ethernet frames")
>> Signed-off-by: Parthiban Veerasooran <parthiban.veerasooran@microchip.com>
>> ---
>>   drivers/net/ethernet/oa_tc6.c | 2 +-
>>   1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
>> index 4c8b0ca922b7..e1e7c6e07966 100644
>> --- a/drivers/net/ethernet/oa_tc6.c
>> +++ b/drivers/net/ethernet/oa_tc6.c
>> @@ -1003,7 +1003,7 @@ static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6)
>>         */
>>        for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits;
>>             used_tx_credits++) {
>> -             if (!tc6->ongoing_tx_skb) {
>> +             if (!tc6->ongoing_tx_skb && tc6->waiting_tx_skb) {
>>                        tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
>>                        tc6->waiting_tx_skb = NULL;
> 
> It is unclear to me how this additional check completely resolves race
> conditions? Is there some other locking or synchronization such the
> second thread cannot have updated waiting_tx_skb either prior or after
> this check?
> 
> This feels like you want some sort of atomic exchange operation...
Ok, thanks for your input. I think protecting this code section with a 
mutex lock will prevent this race condition. So the code will become 
like below,

if (!tc6->ongoing_tx_skb) {
	mutex_lock(&tx_skb_lock);
	tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
	tc6->waiting_tx_skb = NULL;
	mutex_unlock(&tx_skb_lock);
}

Hope this is what you expected right?

Best regards,
Parthiban V
> 
>>                }
>
diff mbox series

Patch

diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c
index 4c8b0ca922b7..e1e7c6e07966 100644
--- a/drivers/net/ethernet/oa_tc6.c
+++ b/drivers/net/ethernet/oa_tc6.c
@@ -1003,7 +1003,7 @@  static u16 oa_tc6_prepare_spi_tx_buf_for_tx_skbs(struct oa_tc6 *tc6)
 	 */
 	for (used_tx_credits = 0; used_tx_credits < tc6->tx_credits;
 	     used_tx_credits++) {
-		if (!tc6->ongoing_tx_skb) {
+		if (!tc6->ongoing_tx_skb && tc6->waiting_tx_skb) {
 			tc6->ongoing_tx_skb = tc6->waiting_tx_skb;
 			tc6->waiting_tx_skb = NULL;
 		}