mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-30 20:35:03 +01:00
splice: Fix leaks that can happen when initiating a splice
- change the state to SPLICE_EXCEPTION to allow so_unsplice() to work
to cleanup failed splices (fixes socket reference leak)
- NULL out sp->dst when unsplicing from so_splice() before so2 has been
been referenced.
- Deal with a null sp->dst / so2 in so_unsplice
- Fix asserts that talked about sp->state == SPLICE_INIT; that state
is not possible here.
Differential Revision: https://reviews.freebsd.org/D54157
Reviewed by: markj
Sponsored by: Netflix
Fixes: c0c5d01e53 ("so_splice: Synchronize so_unsplice() with so_splice()")
MFC after: 3 days
This commit is contained in:
1 parent
f04b23ce35
commit
a837d1fe49
1 file changed
+27
-17
+27
-17
@@ -1726,6 +1726,10 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
|
||||
error = EBUSY;
|
||||
if (error != 0) {
|
||||
SOCK_UNLOCK(so2);
|
||||
mtx_lock(&sp->mtx);
|
||||
sp->dst = NULL;
|
||||
sp->state = SPLICE_EXCEPTION;
|
||||
mtx_unlock(&sp->mtx);
|
||||
so_unsplice(so, false);
|
||||
return (error);
|
||||
}
|
||||
@@ -1733,6 +1737,10 @@ so_splice(struct socket *so, struct socket *so2, struct splice *splice)
|
||||
if (so->so_snd.sb_tls_info != NULL) {
|
||||
SOCK_SENDBUF_UNLOCK(so2);
|
||||
SOCK_UNLOCK(so2);
|
||||
mtx_lock(&sp->mtx);
|
||||
sp->dst = NULL;
|
||||
sp->state = SPLICE_EXCEPTION;
|
||||
mtx_unlock(&sp->mtx);
|
||||
so_unsplice(so, false);
|
||||
return (EINVAL);
|
||||
}
|
||||
@@ -1799,20 +1807,20 @@ so_unsplice(struct socket *so, bool timeout)
|
||||
SOCK_UNLOCK(so);
|
||||
|
||||
so2 = sp->dst;
|
||||
SOCK_LOCK(so2);
|
||||
KASSERT(!SOLISTENING(so2), ("%s: so2 is listening", __func__));
|
||||
SOCK_SENDBUF_LOCK(so2);
|
||||
KASSERT(sp->state == SPLICE_INIT ||
|
||||
(so2->so_snd.sb_flags & SB_SPLICED) != 0,
|
||||
("%s: so2 is not spliced", __func__));
|
||||
KASSERT(sp->state == SPLICE_INIT ||
|
||||
so2->so_splice_back == sp,
|
||||
("%s: so_splice_back != sp", __func__));
|
||||
so2->so_snd.sb_flags &= ~SB_SPLICED;
|
||||
so2rele = so2->so_splice_back != NULL;
|
||||
so2->so_splice_back = NULL;
|
||||
SOCK_SENDBUF_UNLOCK(so2);
|
||||
SOCK_UNLOCK(so2);
|
||||
if (so2 != NULL) {
|
||||
SOCK_LOCK(so2);
|
||||
KASSERT(!SOLISTENING(so2), ("%s: so2 is listening", __func__));
|
||||
SOCK_SENDBUF_LOCK(so2);
|
||||
KASSERT((so2->so_snd.sb_flags & SB_SPLICED) != 0,
|
||||
("%s: so2 is not spliced", __func__));
|
||||
KASSERT(so2->so_splice_back == sp,
|
||||
("%s: so_splice_back != sp", __func__));
|
||||
so2->so_snd.sb_flags &= ~SB_SPLICED;
|
||||
so2rele = so2->so_splice_back != NULL;
|
||||
so2->so_splice_back = NULL;
|
||||
SOCK_SENDBUF_UNLOCK(so2);
|
||||
SOCK_UNLOCK(so2);
|
||||
}
|
||||
|
||||
/*
|
||||
* No new work is being enqueued. The worker thread might be
|
||||
@@ -1852,9 +1860,11 @@ so_unsplice(struct socket *so, bool timeout)
|
||||
sorwakeup(so);
|
||||
CURVNET_SET(so->so_vnet);
|
||||
sorele(so);
|
||||
sowwakeup(so2);
|
||||
if (so2rele)
|
||||
sorele(so2);
|
||||
if (so2 != NULL) {
|
||||
sowwakeup(so2);
|
||||
if (so2rele)
|
||||
sorele(so2);
|
||||
}
|
||||
CURVNET_RESTORE();
|
||||
so_splice_free(sp);
|
||||
return (0);
|
||||
|
||||
Reference in new issue
Block a user