[PATCH net-next v3] af_unix: Fix use-after-free crashes
flat view
STALE5496d
REVIEWED: 2 (0M)
From: Eric Dumazet <hidden>
Date: 2011-09-08 13:21:39
Subsystem:
networking [general], networking [unix sockets], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kuniyuki Iwashima, Linus Torvalds
2 review trailers.
Le jeudi 08 septembre 2011 à 11:59 +0200, Sedat Dilek a écrit :
I have tested this fixup patch on i386. Can we have a separate patch with corrected descriptive text? Thanks to all involved people.
Here it is : [PATCH net-next v3] af_unix: Fix use-after-free crashes Commit 0856a30409 (Scm: Remove unnecessary pid & credential references in Unix socket's send and receive path) introduced an use-after-free bug. We are allowed to steal the references to pid/cred only in the last skb sent from unix_stream_sendmsg(), because first skbs might be consumed by the receiver before we finish our sendmsg() call. Remove scm_release() helper, since its cleaner to clear pid/cred fields in unix_scm_to_skb() when we steal them. Based on prior patches from Yan Zheng and Tim Chen Signed-off-by: Eric Dumazet <redacted> Reported-by: Jiri Slaby <redacted> Tested-by: Sedat Dilek <redacted> Tested-by: Valdis Kletnieks <redacted> --- include/net/scm.h | 9 --------- net/unix/af_unix.c | 32 +++++++++++++++++--------------- 2 files changed, 17 insertions(+), 24 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 68e1e48..2a5b42f 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h@@ -78,15 +78,6 @@ static __inline__ void scm_destroy(struct scm_cookie *scm) __scm_destroy(scm); } -static __inline__ void scm_release(struct scm_cookie *scm) -{ - /* keep ref on pid and cred */ - scm->pid = NULL; - scm->cred = NULL; - if (scm->fp) - __scm_destroy(scm); -} - static __inline__ int scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm) {
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e6d9d10..c8a08ba 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c@@ -1379,15 +1379,18 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) } static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, - bool send_fds, bool ref) + bool send_fds, bool steal_refs) { int err = 0; - if (ref) { + + if (!steal_refs) { UNIXCB(skb).pid = get_pid(scm->pid); UNIXCB(skb).cred = get_cred(scm->cred); } else { UNIXCB(skb).pid = scm->pid; UNIXCB(skb).cred = scm->cred; + scm->pid = NULL; + scm->cred = NULL; } UNIXCB(skb).fp = NULL; if (scm->fp && send_fds)
@@ -1454,7 +1457,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, if (skb == NULL) goto out; - err = unix_scm_to_skb(siocb->scm, skb, true, false); + err = unix_scm_to_skb(siocb->scm, skb, true, true); if (err < 0) goto out_free; max_level = err + 1;
@@ -1550,7 +1553,7 @@ restart: unix_state_unlock(other); other->sk_data_ready(other, len); sock_put(other); - scm_release(siocb->scm); + scm_destroy(siocb->scm); return len; out_unlock:
@@ -1577,6 +1580,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, int sent = 0; struct scm_cookie tmp_scm; bool fds_sent = false; + bool steal_refs = false; int max_level; if (NULL == siocb->scm)
@@ -1638,11 +1642,14 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, size = min_t(int, size, skb_tailroom(skb)); - /* Only send the fds and no ref to pid in the first buffer */ - err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, fds_sent); + /* Only send the fds in first buffer + * Last buffer can steal our references to pid/cred + */ + steal_refs = (sent + size >= len); + err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, steal_refs); if (err < 0) { kfree_skb(skb); - goto out; + goto out_err; } max_level = err + 1; fds_sent = true;
@@ -1650,7 +1657,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size); if (err) { kfree_skb(skb); - goto out; + goto out_err; } unix_state_lock(other);
@@ -1667,10 +1674,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock, sent += size; } - if (skb) - scm_release(siocb->scm); - else - scm_destroy(siocb->scm); + scm_destroy(siocb->scm); siocb->scm = NULL; return sent;
@@ -1683,9 +1687,7 @@ pipe_err: send_sig(SIGPIPE, current, 0); err = -EPIPE; out_err: - if (skb == NULL) - scm_destroy(siocb->scm); -out: + scm_destroy(siocb->scm); siocb->scm = NULL; return sent ? : err; }