diff options
author | Jack Morgenstein <jackm@dev.mellanox.co.il> | 2009-03-03 22:30:01 (GMT) |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2009-03-03 22:30:01 (GMT) |
commit | 6b708b3dde0ab3a10a0eea7774c1d6482f32f587 (patch) | |
tree | fb3f7451940c0d9569bcc10810bbc993c3366535 /drivers/infiniband | |
parent | f3b8436ad9a8ad36b3c9fa1fe030c7f38e5d3d0b (diff) | |
download | linux-6b708b3dde0ab3a10a0eea7774c1d6482f32f587.tar.xz |
IB/sa_query: Fix AH leak due to update_sm_ah() race
Our testing uncovered a race condition in ib_sa_event():
spin_lock_irqsave(&port->ah_lock, flags);
if (port->sm_ah)
kref_put(&port->sm_ah->ref, free_sm_ah);
port->sm_ah = NULL;
spin_unlock_irqrestore(&port->ah_lock, flags);
schedule_work(&sa_dev->port[event->element.port_num -
sa_dev->start_port].update_task);
If two events occur back-to-back (e.g., client-reregister and LID
change), both may pass the spinlock-protected code above before the
scheduled work updates the port->sm_ah handle. Then if the scheduled
work ends up running twice, the second operation will then find a
non-NULL port->sm_ah, and will simply overwrite it in update_sm_ah --
resulting in an AH leak.
Signed-off-by: Jack Morgenstein <jackm@dev.mellanox.co.il>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
Diffstat (limited to 'drivers/infiniband')
-rw-r--r-- | drivers/infiniband/core/sa_query.c | 2 |
1 files changed, 2 insertions, 0 deletions
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 7863a50..1865049 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -395,6 +395,8 @@ static void update_sm_ah(struct work_struct *work) } spin_lock_irq(&port->ah_lock); + if (port->sm_ah) + kref_put(&port->sm_ah->ref, free_sm_ah); port->sm_ah = new_ah; spin_unlock_irq(&port->ah_lock); |