The r241129 description was wrong that the scenario is possible

only for read locks on pcbs. The same race can happen with write
lock semantics as well.

The race scenario:

- Two threads (1 and 2) locate pcb with writer semantics (INPLOOKUP_WLOCKPCB)
 and do in_pcbref() on it.
- 1 and 2 both drop the inp hash lock.
- Another thread (3) grabs the inp hash lock. Then it runs in_pcbfree(),
 which wlocks the pcb. They must happen faster than 1 or 2 come INP_WLOCK()!
- 1 and 2 congest in INP_WLOCK().
- 3 does in_pcbremlists(), drops hash lock, and runs in_pcbrele_wlocked(),
 which doesn't free the pcb due to two references on it.
 Then it unlocks the pcb.
- 1 (or 2) gets wlock on the pcb, runs in_pcbrele_wlocked(), which doesn't
 report inp as freed, due to 2 (or 1) still helding extra reference on it.
 The thread tries to do smth with a disconnected pcb and crashes.

Submitted by:	emeric.poupon@stormshield.eu
Reviewed by:	gleb@
MFC after:	1 week
Sponsored by: Stormshield
Tested by: Cassiano Peixoto, Stormshield
This commit is contained in:
Fabien Thomas 2015-11-25 14:45:43 +00:00
parent 10ac20335c
commit edd0e0b098
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=291301

View File

@ -1220,8 +1220,17 @@ in_pcbrele_wlocked(struct inpcb *inp)
INP_WLOCK_ASSERT(inp);
if (refcount_release(&inp->inp_refcount) == 0)
if (refcount_release(&inp->inp_refcount) == 0) {
/*
* If the inpcb has been freed, let the caller know, even if
* this isn't the last reference.
*/
if (inp->inp_flags2 & INP_FREED) {
INP_WUNLOCK(inp);
return (1);
}
return (0);
}
KASSERT(inp->inp_socket == NULL, ("%s: inp_socket != NULL", __func__));