From 2631ea60bbf529bec7c3d15f93e9ae6c403eda2a Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 17 Apr 2003 21:27:24 -0700 Subject: [PATCH] [BRIDGE]: Fix race in br_fdb_get_entries. --- net/bridge/br_fdb.c | 55 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index f97669090983..9589bb066348 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -94,6 +94,7 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) { struct net_bridge *br; int i; + int newhash = br_mac_hash(newaddr); br = p->br; write_lock_bh(&br->hash_lock); @@ -103,9 +104,11 @@ void br_fdb_changeaddr(struct net_bridge_port *p, unsigned char *newaddr) f = br->hash[i]; while (f != NULL) { if (f->dst == p && f->is_local) { - __hash_unlink(f); memcpy(f->addr.addr, newaddr, ETH_ALEN); - __hash_link(br, f, br_mac_hash(newaddr)); + if (newhash != i) { + __hash_unlink(f); + __hash_link(br, f, newhash); + } write_unlock_bh(&br->hash_lock); return; } @@ -212,21 +215,15 @@ int br_fdb_get_entries(struct net_bridge *br, for (i=0;ihash[i]; - while (f != NULL && num < maxnum) { + for (f = br->hash[i]; f != NULL && num < maxnum; + f = f->next_hash) { struct __fdb_entry ent; - int err; - struct net_bridge_fdb_entry *g; - struct net_bridge_fdb_entry **pp; - if (has_expired(br, f)) { - f = f->next_hash; + if (has_expired(br, f)) continue; - } if (offset) { offset--; - f = f->next_hash; continue; } @@ -234,37 +231,33 @@ int br_fdb_get_entries(struct net_bridge *br, atomic_inc(&f->use_count); read_unlock_bh(&br->hash_lock); - err = copy_to_user(walk, &ent, sizeof(struct __fdb_entry)); - read_lock_bh(&br->hash_lock); - - g = f->next_hash; - pp = f->pprev_hash; - br_fdb_put(f); + + if (copy_to_user(walk, &ent, sizeof(struct __fdb_entry))) + return -EFAULT; - if (err) - goto out_fault; + read_lock_bh(&br->hash_lock); + + /* entry was deleted during copy_to_user */ + if (atomic_dec_and_test(&f->use_count)) { + kfree(f); + num = -EAGAIN; + goto out; + } - if (g == NULL && pp == NULL) - goto out_disappeared; + /* entry changed address hash while copying */ + if (br_mac_hash(f->addr.addr) != i) { + num = -EAGAIN; + goto out; + } num++; walk++; - - f = g; } } out: read_unlock_bh(&br->hash_lock); return num; - - out_disappeared: - num = -EAGAIN; - goto out; - - out_fault: - num = -EFAULT; - goto out; } static __inline__ void __fdb_possibly_replace(struct net_bridge_fdb_entry *fdb, -- 2.39.5