LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH] net: 8390: Fix possible data races in __ei_get_stats
@ 2018-05-07 14:08 Jia-Ju Bai
  2018-05-07 14:15 ` Eric Dumazet
  0 siblings, 1 reply; 7+ messages in thread
From: Jia-Ju Bai @ 2018-05-07 14:08 UTC (permalink / raw)
  To: davem, fthain, joe; +Cc: netdev, linux-kernel, Jia-Ju Bai

The write operations to "dev->stats" are protected by 
the spinlock on line 862-864, but the read operations to
this data on line 858 and 867 are not protected by the spinlock.
Thus, there may exist data races for "dev->stats".

To fix the data races, the read operations to "dev->stats" are 
protected by the spinlock, and a local variable is used for return.

Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
---
 drivers/net/ethernet/8390/lib8390.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
index c9c55c9eab9f..198952247d30 100644
--- a/drivers/net/ethernet/8390/lib8390.c
+++ b/drivers/net/ethernet/8390/lib8390.c
@@ -852,19 +852,25 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
 	unsigned long ioaddr = dev->base_addr;
 	struct ei_device *ei_local = netdev_priv(dev);
 	unsigned long flags;
+	struct net_device_stats *stats;
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
 
 	/* If the card is stopped, just return the present stats. */
-	if (!netif_running(dev))
-		return &dev->stats;
+	if (!netif_running(dev)) {
+		stats = &dev->stats;
+		spin_unlock_irqrestore(&ei_local->page_lock, flags);
+		return stats;
+	}
 
-	spin_lock_irqsave(&ei_local->page_lock, flags);
 	/* Read the counter registers, assuming we are in page 0. */
 	dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
 	dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
 	dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
+	stats = &dev->stats;
 	spin_unlock_irqrestore(&ei_local->page_lock, flags);
 
-	return &dev->stats;
+	return stats;
 }
 
 /*
-- 
2.17.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-07 14:08 [PATCH] net: 8390: Fix possible data races in __ei_get_stats Jia-Ju Bai
@ 2018-05-07 14:15 ` Eric Dumazet
  2018-05-08  0:51   ` Jia-Ju Bai
  0 siblings, 1 reply; 7+ messages in thread
From: Eric Dumazet @ 2018-05-07 14:15 UTC (permalink / raw)
  To: Jia-Ju Bai, davem, fthain, joe; +Cc: netdev, linux-kernel



On 05/07/2018 07:08 AM, Jia-Ju Bai wrote:
> The write operations to "dev->stats" are protected by 
> the spinlock on line 862-864, but the read operations to
> this data on line 858 and 867 are not protected by the spinlock.
> Thus, there may exist data races for "dev->stats".
> 
> To fix the data races, the read operations to "dev->stats" are 
> protected by the spinlock, and a local variable is used for return.
> 
> Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
> ---
>  drivers/net/ethernet/8390/lib8390.c | 14 ++++++++++----
>  1 file changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
> index c9c55c9eab9f..198952247d30 100644
> --- a/drivers/net/ethernet/8390/lib8390.c
> +++ b/drivers/net/ethernet/8390/lib8390.c
> @@ -852,19 +852,25 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
>  	unsigned long ioaddr = dev->base_addr;
>  	struct ei_device *ei_local = netdev_priv(dev);
>  	unsigned long flags;
> +	struct net_device_stats *stats;
> +
> +	spin_lock_irqsave(&ei_local->page_lock, flags);
>  
>  	/* If the card is stopped, just return the present stats. */
> -	if (!netif_running(dev))
> -		return &dev->stats;
> +	if (!netif_running(dev)) {
> +		stats = &dev->stats;
> +		spin_unlock_irqrestore(&ei_local->page_lock, flags);
> +		return stats;
> +	}
>  
> -	spin_lock_irqsave(&ei_local->page_lock, flags);
>  	/* Read the counter registers, assuming we are in page 0. */
>  	dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>  	dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>  	dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
> +	stats = &dev->stats;
>  	spin_unlock_irqrestore(&ei_local->page_lock, flags);
>  
> -	return &dev->stats;
> +	return stats;
>  }
>  
>  /*
> 

dev->stats is not a pointer, it is an array embedded in the 
struct net_device

So this patch is not needed, since dev->stats can not change.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-07 14:15 ` Eric Dumazet
@ 2018-05-08  0:51   ` Jia-Ju Bai
  2018-05-08  1:56     ` Eric Dumazet
  0 siblings, 1 reply; 7+ messages in thread
From: Jia-Ju Bai @ 2018-05-08  0:51 UTC (permalink / raw)
  To: Eric Dumazet, davem, fthain, joe; +Cc: netdev, linux-kernel



On 2018/5/7 22:15, Eric Dumazet wrote:
>
> On 05/07/2018 07:08 AM, Jia-Ju Bai wrote:
>> The write operations to "dev->stats" are protected by
>> the spinlock on line 862-864, but the read operations to
>> this data on line 858 and 867 are not protected by the spinlock.
>> Thus, there may exist data races for "dev->stats".
>>
>> To fix the data races, the read operations to "dev->stats" are
>> protected by the spinlock, and a local variable is used for return.
>>
>> Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
>> ---
>>   drivers/net/ethernet/8390/lib8390.c | 14 ++++++++++----
>>   1 file changed, 10 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
>> index c9c55c9eab9f..198952247d30 100644
>> --- a/drivers/net/ethernet/8390/lib8390.c
>> +++ b/drivers/net/ethernet/8390/lib8390.c
>> @@ -852,19 +852,25 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
>>   	unsigned long ioaddr = dev->base_addr;
>>   	struct ei_device *ei_local = netdev_priv(dev);
>>   	unsigned long flags;
>> +	struct net_device_stats *stats;
>> +
>> +	spin_lock_irqsave(&ei_local->page_lock, flags);
>>   
>>   	/* If the card is stopped, just return the present stats. */
>> -	if (!netif_running(dev))
>> -		return &dev->stats;
>> +	if (!netif_running(dev)) {
>> +		stats = &dev->stats;
>> +		spin_unlock_irqrestore(&ei_local->page_lock, flags);
>> +		return stats;
>> +	}
>>   
>> -	spin_lock_irqsave(&ei_local->page_lock, flags);
>>   	/* Read the counter registers, assuming we are in page 0. */
>>   	dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>>   	dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>>   	dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
>> +	stats = &dev->stats;
>>   	spin_unlock_irqrestore(&ei_local->page_lock, flags);
>>   
>> -	return &dev->stats;
>> +	return stats;
>>   }
>>   
>>   /*
>>
> dev->stats is not a pointer, it is an array embedded in the
> struct net_device
>
> So this patch is not needed, since dev->stats can not change.

Thanks for your reply :)

I do not understand that why "dev->stats can not change".
Its data is indeed changed by the code:
      dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
      dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
      dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);

So I think a data race may occur when returning "dev->stats" without 
lock protection.

By the way, I find this possible data race is similar to the previous 
commit 7b31b4deda76 for the tg3 driver.


Best wishes,
Jia-Ju Bai

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-08  0:51   ` Jia-Ju Bai
@ 2018-05-08  1:56     ` Eric Dumazet
  2018-05-08  2:16       ` Jia-Ju Bai
  0 siblings, 1 reply; 7+ messages in thread
From: Eric Dumazet @ 2018-05-08  1:56 UTC (permalink / raw)
  To: Jia-Ju Bai, Eric Dumazet, davem, fthain, joe; +Cc: netdev, linux-kernel



On 05/07/2018 05:51 PM, Jia-Ju Bai wrote:
> 
> 
> On 2018/5/7 22:15, Eric Dumazet wrote:
>>
>> On 05/07/2018 07:08 AM, Jia-Ju Bai wrote:
>>> The write operations to "dev->stats" are protected by
>>> the spinlock on line 862-864, but the read operations to
>>> this data on line 858 and 867 are not protected by the spinlock.
>>> Thus, there may exist data races for "dev->stats".
>>>
>>> To fix the data races, the read operations to "dev->stats" are
>>> protected by the spinlock, and a local variable is used for return.
>>>
>>> Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
>>> ---
>>>   drivers/net/ethernet/8390/lib8390.c | 14 ++++++++++----
>>>   1 file changed, 10 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
>>> index c9c55c9eab9f..198952247d30 100644
>>> --- a/drivers/net/ethernet/8390/lib8390.c
>>> +++ b/drivers/net/ethernet/8390/lib8390.c
>>> @@ -852,19 +852,25 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
>>>       unsigned long ioaddr = dev->base_addr;
>>>       struct ei_device *ei_local = netdev_priv(dev);
>>>       unsigned long flags;
>>> +    struct net_device_stats *stats;
>>> +
>>> +    spin_lock_irqsave(&ei_local->page_lock, flags);
>>>         /* If the card is stopped, just return the present stats. */
>>> -    if (!netif_running(dev))
>>> -        return &dev->stats;
>>> +    if (!netif_running(dev)) {
>>> +        stats = &dev->stats;
>>> +        spin_unlock_irqrestore(&ei_local->page_lock, flags);
>>> +        return stats;
>>> +    }
>>>   -    spin_lock_irqsave(&ei_local->page_lock, flags);
>>>       /* Read the counter registers, assuming we are in page 0. */
>>>       dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>>>       dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>>>       dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
>>> +    stats = &dev->stats;
>>>       spin_unlock_irqrestore(&ei_local->page_lock, flags);
>>>   -    return &dev->stats;
>>> +    return stats;
>>>   }
>>>     /*
>>>
>> dev->stats is not a pointer, it is an array embedded in the
>> struct net_device
>>
>> So this patch is not needed, since dev->stats can not change.
> 
> Thanks for your reply :)
> 
> I do not understand that why "dev->stats can not change".
> Its data is indeed changed by the code:
>      dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>      dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>      dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);

So ?

> 
> So I think a data race may occur when returning "dev->stats" without lock protection.

&dev->stats is a stable value.

It wont change over the lifetime of net_device object.

Adding a barrier before or after getting &dev->stats is useless, confusing and really not needed.



> 
> By the way, I find this possible data race is similar to the previous commit 7b31b4deda76 for the tg3 driver.

Very different things really.

This does a copy of the whole stats, not the pointer :

*stats = tp->net_stats_prev;


I guess you are confusing simple C semantics about returning the address of a structure,
instead of returning a whole structure.

If __ei_get_stats(struct net_device *dev) prototype was :

struct net_device_stats __ei_get_stats(struct net_device *dev) 

instead of :

struct net_device_stats *__ei_get_stats(struct net_device *dev) 

Then sure, your patch might been needed.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-08  1:56     ` Eric Dumazet
@ 2018-05-08  2:16       ` Jia-Ju Bai
  2018-05-08  5:04         ` Eric Dumazet
  0 siblings, 1 reply; 7+ messages in thread
From: Jia-Ju Bai @ 2018-05-08  2:16 UTC (permalink / raw)
  To: Eric Dumazet, davem, fthain, joe; +Cc: netdev, linux-kernel



On 2018/5/8 9:56, Eric Dumazet wrote:
>
> On 05/07/2018 05:51 PM, Jia-Ju Bai wrote:
>>
>> On 2018/5/7 22:15, Eric Dumazet wrote:
>>> On 05/07/2018 07:08 AM, Jia-Ju Bai wrote:
>>>> The write operations to "dev->stats" are protected by
>>>> the spinlock on line 862-864, but the read operations to
>>>> this data on line 858 and 867 are not protected by the spinlock.
>>>> Thus, there may exist data races for "dev->stats".
>>>>
>>>> To fix the data races, the read operations to "dev->stats" are
>>>> protected by the spinlock, and a local variable is used for return.
>>>>
>>>> Signed-off-by: Jia-Ju Bai <baijiaju1990@gmail.com>
>>>> ---
>>>>    drivers/net/ethernet/8390/lib8390.c | 14 ++++++++++----
>>>>    1 file changed, 10 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/drivers/net/ethernet/8390/lib8390.c b/drivers/net/ethernet/8390/lib8390.c
>>>> index c9c55c9eab9f..198952247d30 100644
>>>> --- a/drivers/net/ethernet/8390/lib8390.c
>>>> +++ b/drivers/net/ethernet/8390/lib8390.c
>>>> @@ -852,19 +852,25 @@ static struct net_device_stats *__ei_get_stats(struct net_device *dev)
>>>>        unsigned long ioaddr = dev->base_addr;
>>>>        struct ei_device *ei_local = netdev_priv(dev);
>>>>        unsigned long flags;
>>>> +    struct net_device_stats *stats;
>>>> +
>>>> +    spin_lock_irqsave(&ei_local->page_lock, flags);
>>>>          /* If the card is stopped, just return the present stats. */
>>>> -    if (!netif_running(dev))
>>>> -        return &dev->stats;
>>>> +    if (!netif_running(dev)) {
>>>> +        stats = &dev->stats;
>>>> +        spin_unlock_irqrestore(&ei_local->page_lock, flags);
>>>> +        return stats;
>>>> +    }
>>>>    -    spin_lock_irqsave(&ei_local->page_lock, flags);
>>>>        /* Read the counter registers, assuming we are in page 0. */
>>>>        dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>>>>        dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>>>>        dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
>>>> +    stats = &dev->stats;
>>>>        spin_unlock_irqrestore(&ei_local->page_lock, flags);
>>>>    -    return &dev->stats;
>>>> +    return stats;
>>>>    }
>>>>      /*
>>>>
>>> dev->stats is not a pointer, it is an array embedded in the
>>> struct net_device
>>>
>>> So this patch is not needed, since dev->stats can not change.
>> Thanks for your reply :)
>>
>> I do not understand that why "dev->stats can not change".
>> Its data is indeed changed by the code:
>>       dev->stats.rx_frame_errors  += ei_inb_p(ioaddr + EN0_COUNTER0);
>>       dev->stats.rx_crc_errors    += ei_inb_p(ioaddr + EN0_COUNTER1);
>>       dev->stats.rx_missed_errors += ei_inb_p(ioaddr + EN0_COUNTER2);
> So ?
>
>> So I think a data race may occur when returning "dev->stats" without lock protection.
> &dev->stats is a stable value.
>
> It wont change over the lifetime of net_device object.
>
> Adding a barrier before or after getting &dev->stats is useless, confusing and really not needed.
>

Yes, "&dev->stats" will not change, because it is a fixed address.
But the field data in "dev->stats" is changed (rx_frame_errors, 
rx_crc_errors and rx_missed_errors).
So if the driver returns "&dev->stats" without lock protection (like on 
line 858), the field data value of this return value can be the changed 
field data value or unchanged field data value.


Best wishes,
Jia-Ju Bai

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-08  2:16       ` Jia-Ju Bai
@ 2018-05-08  5:04         ` Eric Dumazet
  2018-05-08  6:47           ` Jia-Ju Bai
  0 siblings, 1 reply; 7+ messages in thread
From: Eric Dumazet @ 2018-05-08  5:04 UTC (permalink / raw)
  To: Jia-Ju Bai, davem, fthain, joe; +Cc: netdev, linux-kernel



On 05/07/2018 07:16 PM, Jia-Ju Bai wrote:

> Yes, "&dev->stats" will not change, because it is a fixed address.
> But the field data in "dev->stats" is changed (rx_frame_errors, rx_crc_errors and rx_missed_errors).
> So if the driver returns "&dev->stats" without lock protection (like on line 858), the field data value of this return value can be the changed field data value or unchanged field data value.


We do not care.

This function can be called by multiple cpus at the same time.

As soon as one cpu returns from it, another cpu can happily modify dev->stats.ANYFIELD.

Your patch fixes nothing at all.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] net: 8390: Fix possible data races in __ei_get_stats
  2018-05-08  5:04         ` Eric Dumazet
@ 2018-05-08  6:47           ` Jia-Ju Bai
  0 siblings, 0 replies; 7+ messages in thread
From: Jia-Ju Bai @ 2018-05-08  6:47 UTC (permalink / raw)
  To: Eric Dumazet, davem, fthain, joe; +Cc: netdev, linux-kernel



On 2018/5/8 13:04, Eric Dumazet wrote:
>
> On 05/07/2018 07:16 PM, Jia-Ju Bai wrote:
>
>> Yes, "&dev->stats" will not change, because it is a fixed address.
>> But the field data in "dev->stats" is changed (rx_frame_errors, rx_crc_errors and rx_missed_errors).
>> So if the driver returns "&dev->stats" without lock protection (like on line 858), the field data value of this return value can be the changed field data value or unchanged field data value.
>
> We do not care.
>
> This function can be called by multiple cpus at the same time.
>
> As soon as one cpu returns from it, another cpu can happily modify dev->stats.ANYFIELD.
>
> Your patch fixes nothing at all.
>

Okay, thanks.
I also find that my patch does not work...


Best wishes,
Jia-Ju Bai

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-05-08  6:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-05-07 14:08 [PATCH] net: 8390: Fix possible data races in __ei_get_stats Jia-Ju Bai
2018-05-07 14:15 ` Eric Dumazet
2018-05-08  0:51   ` Jia-Ju Bai
2018-05-08  1:56     ` Eric Dumazet
2018-05-08  2:16       ` Jia-Ju Bai
2018-05-08  5:04         ` Eric Dumazet
2018-05-08  6:47           ` Jia-Ju Bai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).