Fix infinite loop on dev_mc_unsync()
Subsystems (2)
PATCH 1/1 Fix infinite loop on dev_mc_unsync()
While reviewing net/core/dev_mcast.c I found what I think is an infinite loop on dev_mc_unsync(). This fixes it. We make use of this guy on mac80211 in ieee80211_stop(). This is untested.
net/core/dev_mcast.c +3 -1
@@ -168,8 +168,10 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da = from->mc_list;while (da !=NULL ) { next = da->next; -if (!da->da_synced) +if (!da->da_synced) { + da = next;continue ; + }__dev_addr_delete (&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen,0 ); da->da_synced =0 ;
Sorry, forgot to CC David.
On Fri, Nov 09, 2007 at 10:11:35AM -0500, Luis R. Rodriguez wrote:
While reviewing net/core/dev_mcast.c I found what I think is an infinite loop on dev_mc_unsync(). This fixes it. We make use of this guy on mac80211 in ieee80211_stop(). This is untested.
net/core/dev_mcast.c +3 -1
@@ -168,8 +168,10 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da = from->mc_list;while (da !=NULL ) { next = da->next; -if (!da->da_synced) +if (!da->da_synced) { + da = next;continue ; + }__dev_addr_delete (&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen,0 ); da->da_synced =0 ;
Quoted 23 lines · Luis R. Rodriguez
While reviewing
net/core/dev_mcast.cI found what I think is an infinite loop ondev_mc_unsync(). This fixes it. We make use of this guy on mac80211 inieee80211_stop(). This is untested.net/core/dev_mcast.c +3
-2@@ -168,8 +168,10 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da = from->mc_list;while (da !=NULL ) { next = da->next; -if (!da->da_synced) +if (!da->da_synced) { + da = next;continue ; + }__dev_addr_delete (&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen,0 ); da->da_synced =0 ; -
Quoted 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Perhaps this is clearer as:
void dev_mc_unsync(struct net_device *to, struct net_device *from)
{
struct dev_addr_list *da;
netif_tx_lock_bh(from);
netif_tx_lock_bh(to);
da = from->mc_list;
while (da) {
if (da->da_synced) {
__dev_addr_delete(&to->mc_list, &to->mc_count,
da->da_addr, da->da_addrlen, 0);
__dev_addr_delete(&from->mc_list, &from->mc_count,
da->da_addr, da->da_addrlen, 0);
da->da_synced = 0;
}
da = da->next;
}
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
netif_tx_unlock_bh(from);
}EXPORT_SYMBOL(dev_mc_unsync);
Quoted 54 lines · Joe Perches
Quoted 23 lines · Luis R. Rodriguez
While reviewing
net/core/dev_mcast.cI found what I think is an infinite loop ondev_mc_unsync(). This fixes it. We make use of this guy on mac80211 inieee80211_stop(). This is untested.net/core/dev_mcast.c +3
-2@@ -168,8 +168,10 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da = from->mc_list;while (da !=NULL ) { next = da->next; -if (!da->da_synced) +if (!da->da_synced) { + da = next;continue ; + }__dev_addr_delete (&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen,0 ); da->da_synced =0 ; -Quoted 3 lines
To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Perhaps this is clearer as:
void
dev_mc_unsync(struct net_device *to, struct net_device *from){ struct dev_addr_list *da; netif_tx_lock_bh(from); netif_tx_lock_bh(to); da = from->mc_list; while (da) { if (da->da_synced) { __dev_addr_delete(&to->mc_list, &to->mc_count, da->da_addr, da->da_addrlen, 0); __dev_addr_delete(&from->mc_list, &from->mc_count, da->da_addr, da->da_addrlen, 0); da->da_synced = 0; } da = da->next; } __dev_set_rx_mode(to); netif_tx_unlock_bh(to); netif_tx_unlock_bh(from); }
Quoted 1 line
EXPORT_SYMBOL(dev_mc_unsync);
Sure, or better with a for loop and do away with next pointer then:
void dev_mc_unsync(struct net_device *to, struct net_device *from)
{
struct dev_addr_list *da;
netif_tx_lock_bh(from);
netif_tx_lock_bh(to);
for (da = from->mc_list; da; da = da->next) {
if (!da->da_synced)
continue;
__dev_addr_delete(&to->mc_list, &to->mc_count,
da->da_addr, da->da_addrlen, 0);
da->da_synced = 0;
__dev_addr_delete(&from->mc_list, &from->mc_count,
da->da_addr, da->da_addrlen, 0);
}
__dev_set_rx_mode(to);
netif_tx_unlock_bh(to);
netif_tx_unlock_bh(from);
}EXPORT_SYMBOL(dev_mc_unsync);
Patch below.
net/core/dev_mcast.c +2 -5
@@ -160,14 +160,12 @@ EXPORT_SYMBOL(dev_mc_sync);*/ void dev_mc_unsync (struct net_device *to,struct net_device *from) { -struct dev_addr_list *da, *next; +struct dev_addr_list *da;netif_tx_lock_bh (from);netif_tx_lock_bh (to); - da = from->mc_list; -while (da !=NULL ) { - next = da->next; +for (da = from->mc_list; da; da = da->next) {if (!da->da_synced)continue ; __dev_addr_delete(&to->mc_list, &to->mc_count,
@@ -175,7 +173,6 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da->da_synced =0 ;__dev_addr_delete (&from->mc_list, &from->mc_count, da->da_addr, da->da_addrlen,0 ); - da = next; }__dev_set_rx_mode (to);
Quoted 9 lines · Luis R. Rodriguez
net/core/dev_mcast.c +0
-1@@ -160,14 +160,12 @@ EXPORT_SYMBOL(dev_mc_sync);*/ void dev_mc_unsync (struct net_device *to,struct net_device *from) { -struct dev_addr_list *da, *next;
net/core/dev_mcast.c EXPORT_SYMBOL(dev_mc_sync); · line 169
Quoted 10 lines
net/core/dev_mcast.c +2
-3+struct dev_addr_list *da;netif_tx_lock_bh (from);netif_tx_lock_bh (to); - da = from->mc_list; -while (da !=NULL ) { - next = da->next; +for (da = from->mc_list; da; da = da->next) {
This may cause a use-after-free since __dev_addr_delete frees the address when all references are gone.
Quoted 10 lines
net/core/dev_mcast.c +0
-0if (!da->da_synced)continue ;net/core/dev_mcast.c +0
-1__dev_addr_delete(&to->mc_list, &to->mc_count,@@ -175,7 +173,6 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) da->da_synced =0 ;__dev_addr_delete (&from->mc_list, &from->mc_count, da->da_addr, da->da_addrlen,0 ); - da = next; }
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 2 lines · Patrick McHardy
This may cause a use-after-free since
__dev_addr_deletefrees the address when all references are gone.
How about a comment then? Perhaps:
net/core/dev_mcast.c +14 -7
@@ -165,16 +165,23 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from)netif_tx_lock_bh (from);netif_tx_lock_bh (to); +/* +This while loop can't be written as +for (da = from->mc_list; da; da = da->next) +da = from->mc_list and __dev_addr_delete can kfree(from->mc_list) +which could cause a use-after-free of da->next +*/ + da = from->mc_list;while (da !=NULL ) { next = da->next; -if (!da->da_synced) -continue ; -__dev_addr_delete (&to->mc_list, &to->mc_count, - da->da_addr, da->da_addrlen,0 ); - da->da_synced =0 ; -__dev_addr_delete (&from->mc_list, &from->mc_count, - da->da_addr, da->da_addrlen,0 ); +if (da->da_synced) { +__dev_addr_delete (&to->mc_list, &to->mc_count, + da->da_addr, da->da_addrlen,0 ); + da->da_synced =0 ; +__dev_addr_delete (&from->mc_list, &from->mc_count, + da->da_addr, da->da_addrlen,0 ); + } da = next; }__dev_set_rx_mode (to);
Quoted 13 lines · Joe Perches
Quoted 3 lines · Patrick McHardy
This may cause a use-after-free since
__dev_addr_deletefrees the address when all references are gone.How about a comment then? Perhaps:
net/core/dev_mcast.c +0
-0
net/core/dev_mcast.c void dev_mc_unsync(struct net_device *to, struct net_device *from) · line 174
Quoted 10 lines
net/core/dev_mcast.c +6
-0@@ -165,16 +165,23 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from)netif_tx_lock_bh (from);netif_tx_lock_bh (to); +/* +This while loop can't be written as +for (da = from->mc_list; da; da = da->next) +da = from->mc_list and __dev_addr_delete can kfree(from->mc_list) +which could cause a use-after-free of da->next +*/
Seems unnecessary to me, we also don't comment each list_for_each_entry_safe iteration. I consider the use of a seperate next variable self-explanatory.
A message that is not in the archive (sent off-list or not yet received): [email protected]
From: Patrick McHardy <[email protected]>
Date: Sat, 10 Nov 2007 01:13:42 +0100
Quoted 17 lines
Quoted 16 lines · Joe Perches
Quoted 3 lines · Patrick McHardy
This may cause a use-after-free since
__dev_addr_deletefrees the address when all references are gone.How about a comment then? Perhaps:
net/core/dev_mcast.c +0
-0@@ -165,16 +165,23 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from)netif_tx_lock_bh (from);netif_tx_lock_bh (to);
Quoted 10 lines
net/core/dev_mcast.c void dev_mc_unsync(struct net_device *to, struct net_device *from)
Quoted 7 lines
net/core/dev_mcast.c +6
-0+/* +This while loop can't be written as +for (da = from->mc_list; da; da = da->next) +da = from->mc_list and __dev_addr_delete can kfree(from->mc_list) +which could cause a use-after-free of da->next +*/ Seems unnecessary to me, we also don't comment each
list_for_each_entry_safeiteration. I consider the use of a seperate next variable self-explanatory.
Agreed, this comment is pointless.
I'll apply Joe's patch without the comment.