diff options
author | fabient <fabient@FreeBSD.org> | 2015-12-02 17:26:37 +0000 |
---|---|---|
committer | fabient <fabient@FreeBSD.org> | 2015-12-02 17:26:37 +0000 |
commit | ccce6feaa419fbc5fc1c0f617f6ad974b07a58c4 (patch) | |
tree | a1cd026229b0606e80d7222dcdfa2c995e639a3f | |
parent | 904bdb8bc249483f4b14b6df43fb361e87b438da (diff) | |
download | FreeBSD-src-ccce6feaa419fbc5fc1c0f617f6ad974b07a58c4.zip FreeBSD-src-ccce6feaa419fbc5fc1c0f617f6ad974b07a58c4.tar.gz |
MFC r291301:
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: glebius@
Sponsored by: Stormshield
Tested by: Cassiano Peixoto, Stormshield
-rw-r--r-- | sys/netinet/in_pcb.c | 11 |
1 files changed, 10 insertions, 1 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index 4e75f13..0b296e0 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1148,8 +1148,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__)); |