Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

Subsystems: networking [general], networking [unix sockets], the rest

35 messages, 7 authors, 2011-09-28 · open the first message on its own page

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-07 19:01:45

On Wed, 2011-09-07 at 09:45 +0200, Eric Dumazet wrote:
Le mercredi 07 septembre 2011 à 13:20 +0800, Yan, Zheng a écrit :
quoted
Is code like this OK? Thanks
---
	if (sent + size < len) { 
		/* Only send the fds in the first buffer */
		/* get additional ref if more skbs will be created */
		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, true);
	} else {
		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, false);
		ref_avail = false;
	}
Whats wrong with using ref_avail in the unix_scm_to_skb() call itself ?

something like :
Eric,

Your updated patch looks good when I tested it on my side.  It makes the
patch much more readable.  If this patch looks good with you and Yan
Zheng, can you and Yan Zheng add your Signed-off-by to the patch?

Jiri, Sedat or Valdis, if you can verify that the patch fixed commit
0856a30409, that will be appreciated.

Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?

Thanks.

Tim

---

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..4a324a0 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,8 +1644,11 @@ 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;
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Sedat Dilek <hidden>
Date: 2011-09-07 20:12:11

On Wed, Sep 7, 2011 at 2:01 PM, Tim Chen [off-list ref] wrote:
quoted hunk
On Wed, 2011-09-07 at 09:45 +0200, Eric Dumazet wrote:
quoted
Le mercredi 07 septembre 2011 à 13:20 +0800, Yan, Zheng a écrit :
quoted
Is code like this OK? Thanks
---
    if (sent + size < len) {
            /* Only send the fds in the first buffer */
            /* get additional ref if more skbs will be created */
            err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, true);
    } else {
            err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, false);
            ref_avail = false;
    }
Whats wrong with using ref_avail in the unix_scm_to_skb() call itself ?

something like :
Eric,

Your updated patch looks good when I tested it on my side.  It makes the
patch much more readable.  If this patch looks good with you and Yan
Zheng, can you and Yan Zheng add your Signed-off-by to the patch?

Jiri, Sedat or Valdis, if you can verify that the patch fixed commit
0856a30409, that will be appreciated.

Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?

Thanks.

Tim

---

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed
by the receive side, removing all references to the credentials,
before the send side has finished sending out all
packets. However, send side could continue to consturct new packets in the
stream, using credentials that have lost its last reference and been
freed.

In this fix, we don't steal the reference to credentials we have obtained
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..4a324a0 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,8 +1644,11 @@ 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;
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
               sent += size;
       }

-       if (skb)
+       if (steal_refs)
               scm_release(siocb->scm);
       else
               scm_destroy(siocb->scm);

Replaced v2 with this patch (against next-20110831), I see now some
different call-traces which I did not see with v1 or v2.
Can't say if it's related to the new patch or not.
( dmesg attached. )

- Sedat -

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Sedat Dilek <hidden>
Date: 2011-09-07 20:30:44

On Wed, Sep 7, 2011 at 10:12 PM, Sedat Dilek [off-list ref] wrote:
On Wed, Sep 7, 2011 at 2:01 PM, Tim Chen [off-list ref] wrote:
quoted
On Wed, 2011-09-07 at 09:45 +0200, Eric Dumazet wrote:
quoted
Le mercredi 07 septembre 2011 à 13:20 +0800, Yan, Zheng a écrit :
quoted
Is code like this OK? Thanks
---
    if (sent + size < len) {
            /* Only send the fds in the first buffer */
            /* get additional ref if more skbs will be created */
            err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, true);
    } else {
            err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, false);
            ref_avail = false;
    }
Whats wrong with using ref_avail in the unix_scm_to_skb() call itself ?

something like :
Eric,

Your updated patch looks good when I tested it on my side.  It makes the
patch much more readable.  If this patch looks good with you and Yan
Zheng, can you and Yan Zheng add your Signed-off-by to the patch?

Jiri, Sedat or Valdis, if you can verify that the patch fixed commit
0856a30409, that will be appreciated.

Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?

Thanks.

Tim

---

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed
by the receive side, removing all references to the credentials,
before the send side has finished sending out all
packets. However, send side could continue to consturct new packets in the
stream, using credentials that have lost its last reference and been
freed.

In this fix, we don't steal the reference to credentials we have obtained
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..4a324a0 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,8 +1644,11 @@ 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;
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
               sent += size;
       }

-       if (skb)
+       if (steal_refs)
               scm_release(siocb->scm);
       else
               scm_destroy(siocb->scm);

Replaced v2 with this patch (against next-20110831), I see now some
different call-traces which I did not see with v1 or v2.
Can't say if it's related to the new patch or not.
( dmesg attached. )

- Sedat -
Call-traces seem to go away when adding "irqpoll" to Kernel command line.
( See dmesg_irqpoll.txt )

- Sedat -

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-07 21:26:12

Le mercredi 07 septembre 2011 à 05:01 -0700, Tim Chen a écrit :
On Wed, 2011-09-07 at 09:45 +0200, Eric Dumazet wrote:
quoted
Le mercredi 07 septembre 2011 à 13:20 +0800, Yan, Zheng a écrit :
quoted
Is code like this OK? Thanks
---
	if (sent + size < len) { 
		/* Only send the fds in the first buffer */
		/* get additional ref if more skbs will be created */
		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, true);
	} else {
		err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, false);
		ref_avail = false;
	}
Whats wrong with using ref_avail in the unix_scm_to_skb() call itself ?

something like :
Eric,

Your updated patch looks good when I tested it on my side.  It makes the
patch much more readable.  If this patch looks good with you and Yan
Zheng, can you and Yan Zheng add your Signed-off-by to the patch?

Jiri, Sedat or Valdis, if you can verify that the patch fixed commit
0856a30409, that will be appreciated.

Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?
Yes, I'll try to cook a patch.
Thanks.

Tim

---

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
Signed-off-by: Eric Dumazet <redacted>

Thanks !

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-07 21:37:38

On Wed, 2011-09-07 at 22:30 +0200, Sedat Dilek wrote:
quoted
Replaced v2 with this patch (against next-20110831), I see now some
different call-traces which I did not see with v1 or v2.
Can't say if it's related to the new patch or not.
( dmesg attached. )

- Sedat -
Call-traces seem to go away when adding "irqpoll" to Kernel command line.
( See dmesg_irqpoll.txt )

- Sedat -
Sedat,
 
The previous patch should use the new steal_refs to check for the
release of scm references in the error handling at the end.  I've
updated the patch to take care of it.  Hopefully the traces you see will
go away. Can you verify?

Thanks.

Tim

----
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all referentials to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Signed-off-by: Eric Dumazet <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..be712ae 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,8 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Yan, Zheng <hidden>
Date: 2011-09-08 00:27:40

On 09/07/2011 10:37 PM, Tim Chen wrote:
quoted hunk
On Wed, 2011-09-07 at 22:30 +0200, Sedat Dilek wrote:
quoted
quoted
Replaced v2 with this patch (against next-20110831), I see now some
different call-traces which I did not see with v1 or v2.
Can't say if it's related to the new patch or not.
( dmesg attached. )

- Sedat -
Call-traces seem to go away when adding "irqpoll" to Kernel command line.
( See dmesg_irqpoll.txt )

- Sedat -
Sedat,
 
The previous patch should use the new steal_refs to check for the
release of scm references in the error handling at the end.  I've
updated the patch to take care of it.  Hopefully the traces you see will
go away. Can you verify?

Thanks.

Tim

----
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all referentials to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Signed-off-by: Eric Dumazet <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..be712ae 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,8 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
quoted hunk
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-08 04:06:26

On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim

---
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..47780dc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,10 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (steal_refs)
+		scm_release(siocb->scm);
+	else
 		scm_destroy(siocb->scm);
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-08 04:15:17

On Wed, 2011-09-07 at 14:06 -0700, Tim Chen wrote:
On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
quoted
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim
Oops, I've forgotten to add Eric's previous Signed-off-by in my latest
patch log.  David, please add that when you pick up the patch.  
Once Yan Zheng added his sign off and Sedat tested the patch, I think it
will be good to go.

Thanks.

Tim 

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Yan, Zheng <hidden>
Date: 2011-09-08 04:18:46

On 09/08/2011 05:06 AM, Tim Chen wrote:
quoted hunk
On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
quoted
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim

---
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..47780dc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,10 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (steal_refs)
+		scm_release(siocb->scm);
+	else
 		scm_destroy(siocb->scm);
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

Signed-off-by: Zheng Yan <redacted>

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 05:59:40

Le mercredi 07 septembre 2011 à 14:06 -0700, Tim Chen a écrit :
quoted hunk
On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
quoted
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim

---
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..47780dc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,10 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (steal_refs)
+		scm_release(siocb->scm);
+	else
 		scm_destroy(siocb->scm);
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

I dont think this patch is good.

Sedat traces have nothing to do with af_unix.

Once unix_scm_to_skb() was called and successful, and steal_refs is true
our refs are attached to this skb. They will be released by
skb_free(skb). Same for fp : They either were sent in a previous skb or
this one.

This is why the "goto out;" was OK.

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 06:21:52

Le mercredi 07 septembre 2011 à 14:15 -0700, Tim Chen a écrit :
Oops, I've forgotten to add Eric's previous Signed-off-by in my latest
patch log.  David, please add that when you pick up the patch.  
Once Yan Zheng added his sign off and Sedat tested the patch, I think it
will be good to go.
Tim, as soon as you post another patch version, you must remove all
prior Signed-off-by, and only add yours.

So it was fine to do so.

By the way your last version introduce a new bug, so I would rather NACK
it ;)

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Yan, Zheng <hidden>
Date: 2011-09-08 06:22:20

On 09/08/2011 01:59 PM, Eric Dumazet wrote:
Le mercredi 07 septembre 2011 à 14:06 -0700, Tim Chen a écrit :
quoted
On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
quoted
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (!steal_refs)
 		scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim

---
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed 
by the receive side, removing all references to the credentials, 
before the send side has finished sending out all 
packets. However, send side could continue to consturct new packets in the 
stream, using credentials that have lost its last reference and been
freed.  

In this fix, we don't steal the reference to credentials we have obtained 
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..47780dc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		sent += size;
 	}
 
-	if (skb)
+	if (steal_refs)
 		scm_release(siocb->scm);
 	else
 		scm_destroy(siocb->scm);
@@ -1687,9 +1692,10 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
+	if (steal_refs)
+		scm_release(siocb->scm);
+	else
 		scm_destroy(siocb->scm);
-out:
 	siocb->scm = NULL;
 	return sent ? : err;
 }

I dont think this patch is good.

Sedat traces have nothing to do with af_unix.

Once unix_scm_to_skb() was called and successful, and steal_refs is true
our refs are attached to this skb. They will be released by
skb_free(skb). Same for fp : They either were sent in a previous skb or
this one.

This is why the "goto out;" was OK.
I don't think so. unix_scm_to_skb() calls unix_attach_fds(), it
always duplicates scm->fp. 

Regards

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Sedat Dilek <hidden>
Date: 2011-09-08 07:02:44

On Thu, Sep 8, 2011 at 7:59 AM, Eric Dumazet [off-list ref] wrote:
Le mercredi 07 septembre 2011 à 14:06 -0700, Tim Chen a écrit :
quoted
On Thu, 2011-09-08 at 08:27 +0800, Yan, Zheng wrote:
quoted
quoted
  err = -EPIPE;
 out_err:
- if (skb == NULL)
+ if (!steal_refs)
          scm_destroy(siocb->scm);
I think we should call scm_release() here in the case of
steal_refs == true. Otherwise siocb->scm->fp may leak.
Yan Zheng,

I've updated the patch.  If it looks good to you now, can you add your
Signed-off-by to the patch.  Pending Sedat's testing on this patch,
I think it is good to go.

Tim

---
Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced a use-after-free bug.
The sent skbs from unix_stream_sendmsg could be consumed and destructed
by the receive side, removing all references to the credentials,
before the send side has finished sending out all
packets. However, send side could continue to consturct new packets in the
stream, using credentials that have lost its last reference and been
freed.

In this fix, we don't steal the reference to credentials we have obtained
in scm_send at beginning of unix_stream_sendmsg, till we've reached
the last packet.  This fixes the problem in commit 0856a30409.

Signed-off-by: Tim Chen <redacted>
Reported-by: Jiri Slaby <redacted>
Tested-by: Sedat Dilek <redacted>
Tested-by: Valdis Kletnieks <redacted>
---
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 136298c..47780dc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,10 +1383,11 @@ 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 {
@@ -1458,7 +1459,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;
@@ -1581,6 +1582,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)
@@ -1642,11 +1644,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;
@@ -1654,7 +1659,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);
@@ -1671,7 +1676,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
              sent += size;
      }

-     if (skb)
+     if (steal_refs)
              scm_release(siocb->scm);
      else
              scm_destroy(siocb->scm);
@@ -1687,9 +1692,10 @@ pipe_err:
              send_sig(SIGPIPE, current, 0);
      err = -EPIPE;
 out_err:
-     if (skb == NULL)
+     if (steal_refs)
+             scm_release(siocb->scm);
+     else
              scm_destroy(siocb->scm);
-out:
      siocb->scm = NULL;
      return sent ? : err;
 }

I dont think this patch is good.

Sedat traces have nothing to do with af_unix.

Once unix_scm_to_skb() was called and successful, and steal_refs is true
our refs are attached to this skb. They will be released by
skb_free(skb). Same for fp : They either were sent in a previous skb or
this one.

This is why the "goto out;" was OK.
Good morning,

/me sees so many patches :-).
Yes, I guess the patch by Eric has nothing to do with seen
call-traces, adding "irqpoll" to Kernel command line seems to fix them
(my uptime: approx. 10:30, Eric's proposal patch in my last
patch-series is attached).

- Sedat -

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 07:12:06

Le jeudi 08 septembre 2011 à 14:22 +0800, Yan, Zheng a écrit :
I don't think so. unix_scm_to_skb() calls unix_attach_fds(), it
always duplicates scm->fp. 
What a mess. This code is a nightmare.

Part of the mess comes from scm_destroy() and scm_release() duplication.

We should have scm_destroy() only, as before, and NULLify scm->pid/cred
in unix_scm_to_skb() when we steal references.

It makes more sense and keeps things clearer.


 include/net/scm.h  |    9 ---------
 net/unix/af_unix.c |   27 +++++++++++++++------------
 2 files changed, 15 insertions(+), 21 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..1fa270a 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,8 +1642,11 @@ 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;
@@ -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,8 +1687,7 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
-		scm_destroy(siocb->scm);
+	scm_destroy(siocb->scm);
 out:
 	siocb->scm = NULL;
 	return sent ? : err;

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Yan, Zheng <hidden>
Date: 2011-09-08 07:23:32

On 09/08/2011 03:11 PM, Eric Dumazet wrote:
quoted hunk
Le jeudi 08 septembre 2011 à 14:22 +0800, Yan, Zheng a écrit :
quoted
I don't think so. unix_scm_to_skb() calls unix_attach_fds(), it
always duplicates scm->fp. 
What a mess. This code is a nightmare.

Part of the mess comes from scm_destroy() and scm_release() duplication.

We should have scm_destroy() only, as before, and NULLify scm->pid/cred
in unix_scm_to_skb() when we steal references.

It makes more sense and keeps things clearer.


 include/net/scm.h  |    9 ---------
 net/unix/af_unix.c |   27 +++++++++++++++------------
 2 files changed, 15 insertions(+), 21 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..1fa270a 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,8 +1642,11 @@ 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;
@@ -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,8 +1687,7 @@ pipe_err:
 		send_sig(SIGPIPE, current, 0);
 	err = -EPIPE;
 out_err:
-	if (skb == NULL)
-		scm_destroy(siocb->scm);
+	scm_destroy(siocb->scm);
 out:
 	siocb->scm = NULL;
 	return sent ? : err;
This code looks great, except "goto out;" is still there. I think we should replace it with "goto out_err;" :)

Regards

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 07:33:11

Le jeudi 08 septembre 2011 à 15:23 +0800, Yan, Zheng a écrit :
This code looks great, except "goto out;" is still there. I think we should replace it with "goto out_err;" :)
Indeed, you're right, thanks

 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;
 }

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Jiri Slaby <hidden>
Date: 2011-09-08 07:56:07

On 09/08/2011 09:11 AM, Eric Dumazet wrote:
Le jeudi 08 septembre 2011 à 14:22 +0800, Yan, Zheng a écrit :
quoted
I don't think so. unix_scm_to_skb() calls unix_attach_fds(), it
always duplicates scm->fp. 
What a mess. This code is a nightmare.

Part of the mess comes from scm_destroy() and scm_release() duplication.

We should have scm_destroy() only, as before, and NULLify scm->pid/cred
in unix_scm_to_skb() when we steal references.
This patch works for me. I haven't tried the out_err fixup from the
followup, but I assume I won't spot a difference as those are fail paths
anyway...

thanks,
-- 
js

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Sedat Dilek <hidden>
Date: 2011-09-08 08:43:54

On Thu, Sep 8, 2011 at 9:56 AM, Jiri Slaby [off-list ref] wrote:
On 09/08/2011 09:11 AM, Eric Dumazet wrote:
quoted
Le jeudi 08 septembre 2011 à 14:22 +0800, Yan, Zheng a écrit :
quoted
I don't think so. unix_scm_to_skb() calls unix_attach_fds(), it
always duplicates scm->fp.
What a mess. This code is a nightmare.

Part of the mess comes from scm_destroy() and scm_release() duplication.

We should have scm_destroy() only, as before, and NULLify scm->pid/cred
in unix_scm_to_skb() when we steal references.
This patch works for me. I haven't tried the out_err fixup from the
followup, but I assume I won't spot a difference as those are fail paths
anyway...

thanks,
--
js
I have tested the same patch here (before shopping, but can test the
"final" patch, too.).

- Sedat -

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Sedat Dilek <hidden>
Date: 2011-09-08 10:05:42

On Thu, Sep 8, 2011 at 9:33 AM, Eric Dumazet [off-list ref] wrote:
quoted hunk
Le jeudi 08 septembre 2011 à 15:23 +0800, Yan, Zheng a écrit :
quoted
This code looks great, except "goto out;" is still there. I think we should replace it with "goto out_err;" :)
Indeed, you're right, thanks

 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;
 }
I have tested this fixup patch on i386.
Can we have a separate patch with corrected descriptive text?

Thanks to all involved people.

- Sedat -

[PATCH net-next v3] af_unix: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 13:21:39

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;
 }

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-08 13:28:15

Le mercredi 07 septembre 2011 à 23:26 +0200, Eric Dumazet a écrit :
Le mercredi 07 septembre 2011 à 05:01 -0700, Tim Chen a écrit :
quoted
Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?
Yes, I'll try to cook a patch.
Thinking a bit more on this issue, I really believe we should not stick
pid/cred in skbs sent from a write() system call.

That would break following use case :

An application uses a write(fd) and expects a receiver using recvmsg()
to get process credentials (SCM_CREDENTIALS)

This is currently working, but not documented (man unix says ancillary
data are sent with sendmsg())

If everybody agrees, I can send a patch for this : This would speedup
write()/read() af_unix by an order of magnitude.

Re: [PATCH net-next v3] af_unix: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-08 15:37:47

On Thu, 2011-09-08 at 15:21 +0200, Eric Dumazet wrote:
Le jeudi 08 septembre 2011 à 11:59 +0200, Sedat Dilek a écrit :
quoted
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>
---
Thanks.

Acked-by: Tim Chen <redacted>

Re: [PATCH -next v2] unix stream: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-08 23:24:18

On Thu, 2011-09-08 at 15:28 +0200, Eric Dumazet wrote:
Le mercredi 07 septembre 2011 à 23:26 +0200, Eric Dumazet a écrit :
quoted
Le mercredi 07 septembre 2011 à 05:01 -0700, Tim Chen a écrit :
quoted
quoted
Eric, are you planning to do a fast path patch that doesn't do pid ref
for the case where CONFIG_PID_NS is not set?
Yes, I'll try to cook a patch.
Thinking a bit more on this issue, I really believe we should not stick
pid/cred in skbs sent from a write() system call.
I prefer this approach too.
That would break following use case :

An application uses a write(fd) and expects a receiver using recvmsg()
to get process credentials (SCM_CREDENTIALS)

This is currently working, but not documented (man unix says ancillary
data are sent with sendmsg())

If everybody agrees, I can send a patch for this : This would speedup
write()/read() af_unix by an order of magnitude.
Looking forward to the patch.  This should improve the scalability of
af_unix.

Tim

[PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Eric Dumazet <hidden>
Date: 2011-09-09 05:06:26

Le jeudi 08 septembre 2011 à 02:24 -0700, Tim Chen a écrit :
Looking forward to the patch.  This should improve the scalability of
af_unix.
Here it is, based on top on previous one [af_unix: Fix use-after-free
crashes]

Thanks

[PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.

# Events: 154K cycles
#
# Overhead  Command       Shared Object                               Symbol
# ........  .......  ..................  ...................................
#
    10.40%  hackbench  [kernel.kallsyms]   [k] put_pid
     8.60%  hackbench  [kernel.kallsyms]   [k] unix_stream_recvmsg
     7.87%  hackbench  [kernel.kallsyms]   [k] unix_stream_sendmsg
     6.11%  hackbench  [kernel.kallsyms]   [k] do_raw_spin_lock
     4.95%  hackbench  [kernel.kallsyms]   [k] unix_scm_to_skb
     4.87%  hackbench  [kernel.kallsyms]   [k] pid_nr_ns
     4.34%  hackbench  [kernel.kallsyms]   [k] cred_to_ucred
     2.39%  hackbench  [kernel.kallsyms]   [k] unix_destruct_scm
     2.24%  hackbench  [kernel.kallsyms]   [k] sub_preempt_count
     1.75%  hackbench  [kernel.kallsyms]   [k] fget_light
     1.51%  hackbench  [kernel.kallsyms]   [k] __mutex_lock_interruptible_slowpath
     1.42%  hackbench  [kernel.kallsyms]   [k] sock_alloc_send_pskb


This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call.

Performance boost in hackbench : more than 50% gain on a 16 thread
machine (2 quad-core cpus, 2 threads per core)

hackbench 20 thread 2000

4.224 sec instead of 9.102 sec


Signed-off-by: Eric Dumazet <redacted>
---
 include/net/scm.h        |   11 +----------
 net/core/scm.c           |   10 ++++++----
 net/netlink/af_netlink.c |    5 ++---
 net/unix/af_unix.c       |    9 +++++----
 4 files changed, 14 insertions(+), 21 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 2a5b42f..74c8fdc 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -45,14 +45,6 @@ static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_co
 { }
 #endif /* CONFIG_SECURITY_NETWORK */
 
-static __inline__ void scm_set_cred(struct scm_cookie *scm,
-				    struct pid *pid, const struct cred *cred)
-{
-	scm->pid  = get_pid(pid);
-	scm->cred = get_cred(cred);
-	cred_to_ucred(pid, cred, &scm->creds);
-}
-
 static __inline__ void scm_set_cred_noref(struct scm_cookie *scm,
 				    struct pid *pid, const struct cred *cred)
 {
@@ -81,8 +73,7 @@ static __inline__ void scm_destroy(struct scm_cookie *scm)
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
 			       struct scm_cookie *scm)
 {
-	scm_set_cred(scm, task_tgid(current), current_cred());
-	scm->fp = NULL;
+	memset(scm, 0, sizeof(*scm));
 	unix_get_peersec_dgram(sock, scm);
 	if (msg->msg_controllen <= 0)
 		return 0;
diff --git a/net/core/scm.c b/net/core/scm.c
index 811b53f..ff52ad0 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -173,7 +173,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 			if (err)
 				goto error;
 
-			if (pid_vnr(p->pid) != p->creds.pid) {
+			if (!p->pid || pid_vnr(p->pid) != p->creds.pid) {
 				struct pid *pid;
 				err = -ESRCH;
 				pid = find_get_pid(p->creds.pid);
@@ -183,8 +183,9 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 				p->pid = pid;
 			}
 
-			if ((p->cred->euid != p->creds.uid) ||
-				(p->cred->egid != p->creds.gid)) {
+			if (!p->cred ||
+			    (p->cred->euid != p->creds.uid) ||
+			    (p->cred->egid != p->creds.gid)) {
 				struct cred *cred;
 				err = -ENOMEM;
 				cred = prepare_creds();
@@ -193,7 +194,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 
 				cred->uid = cred->euid = p->creds.uid;
 				cred->gid = cred->egid = p->creds.gid;
-				put_cred(p->cred);
+				if (p->cred)
+					put_cred(p->cred);
 				p->cred = cred;
 			}
 			break;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 4330db9..1201b6d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1324,10 +1324,9 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (NULL == siocb->scm) {
+	if (NULL == siocb->scm)
 		siocb->scm = &scm;
-		memset(&scm, 0, sizeof(scm));
-	}
+
 	err = scm_send(sock, msg, siocb->scm);
 	if (err < 0)
 		return err;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index c8a08ba..4c77385 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1383,12 +1383,13 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
 {
 	int err = 0;
 
+	UNIXCB(skb).pid  = scm->pid;
+	UNIXCB(skb).cred = scm->cred;
 	if (!steal_refs) {
-		UNIXCB(skb).pid  = get_pid(scm->pid);
-		UNIXCB(skb).cred = get_cred(scm->cred);
+		get_pid(scm->pid);
+		if (scm->cred)
+			get_cred(scm->cred);
 	} else {
-		UNIXCB(skb).pid  = scm->pid;
-		UNIXCB(skb).cred = scm->cred;
 		scm->pid = NULL;
 		scm->cred = NULL;
 	}

Re: [PATCH net-next v3] af_unix: Fix use-after-free crashes

From: Eric Dumazet <hidden>
Date: 2011-09-09 06:51:48

Le jeudi 08 septembre 2011 à 01:37 -0700, Tim Chen a écrit :
On Thu, 2011-09-08 at 15:21 +0200, Eric Dumazet wrote:
quoted
Le jeudi 08 septembre 2011 à 11:59 +0200, Sedat Dilek a écrit :
quoted
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>
---
Thanks.

Acked-by: Tim Chen <redacted>
Now we have to fix a bug in unix_stream_recvmsg() as well.

consume_skb() call actually releases pid/cred references, and we can use
them after their eventual freeing.

Keep also in mind that receiver can provides a too short user buffer,
and skb can be put back to head of sk_receive_queue

Tim, your 0856a304091b33 commit introduced a lot of bugs, I was right
asking a revert.

If we revert your patch, my litle patch (af_unix: dont send
SCM_CREDENTIALS by default) is enough to solve performance problems.

[PATCH net-next] af_unix: fix use after free in unix_stream_recvmsg()

From: Eric Dumazet <hidden>
Date: 2011-09-09 08:15:39

Le vendredi 09 septembre 2011 à 08:51 +0200, Eric Dumazet a écrit :
Now we have to fix a bug in unix_stream_recvmsg() as well.

consume_skb() call actually releases pid/cred references, and we can use
them after their eventual freeing.

Keep also in mind that receiver can provides a too short user buffer,
and skb can be put back to head of sk_receive_queue
Here is the patch to address this point.

Apply it after (af_unix: Fix use-after-free crashes)

I can make a combo patch once everybody agrees.

[PATCH net-next] af_unix: fix use after free in unix_stream_recvmsg()

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced an use-after-free
bug in unix_stream_recvmsg().

We should call consume_skb(skb) only after our possible use of pid/cred.

Signed-off-by: Eric Dumazet <redacted>
---
 net/unix/af_unix.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index c8a08ba..1bd4ecf 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1873,6 +1873,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 	int target;
 	int err = 0;
 	long timeo;
+	struct sk_buff *skb;
 
 	err = -EINVAL;
 	if (sk->sk_state != TCP_ESTABLISHED)
@@ -1904,7 +1905,6 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 
 	do {
 		int chunk;
-		struct sk_buff *skb;
 
 		unix_state_lock(sk);
 		skb = skb_dequeue(&sk->sk_receive_queue);
@@ -1949,6 +1949,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			if ((UNIXCB(skb).pid  != siocb->scm->pid) ||
 			    (UNIXCB(skb).cred != siocb->scm->cred)) {
 				skb_queue_head(&sk->sk_receive_queue, skb);
+				skb = NULL;
 				break;
 			}
 		} else {
@@ -1967,6 +1968,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 		chunk = min_t(unsigned int, skb->len, size);
 		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
 			skb_queue_head(&sk->sk_receive_queue, skb);
+			skb = NULL;
 			if (copied == 0)
 				copied = -EFAULT;
 			break;
@@ -1984,13 +1986,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			/* put the skb back if we didn't use it up.. */
 			if (skb->len) {
 				skb_queue_head(&sk->sk_receive_queue, skb);
+				skb = NULL;
 				break;
 			}
 
-			consume_skb(skb);
-
-			if (siocb->scm->fp)
+			if (UNIXCB(skb).pid || siocb->scm->fp)
 				break;
+			consume_skb(skb);
+			skb = NULL;
 		} else {
 			/* It is questionable, see note in unix_dgram_recvmsg.
 			 */
@@ -1999,12 +2002,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 
 			/* put message back and return */
 			skb_queue_head(&sk->sk_receive_queue, skb);
+			skb = NULL;
 			break;
 		}
 	} while (size);
 
 	mutex_unlock(&u->readlock);
 	scm_recv(sock, msg, siocb->scm, flags);
+	consume_skb(skb);
 out:
 	return copied ? : err;
 }

Re: [PATCH net-next] af_unix: fix use after free in unix_stream_recvmsg()

From: Tim Chen <hidden>
Date: 2011-09-09 17:40:00

On Fri, 2011-09-09 at 09:58 +0200, Eric Dumazet wrote:
quoted hunk
Le vendredi 09 septembre 2011 à 08:51 +0200, Eric Dumazet a écrit :
quoted
Now we have to fix a bug in unix_stream_recvmsg() as well.

consume_skb() call actually releases pid/cred references, and we can use
them after their eventual freeing.

Keep also in mind that receiver can provides a too short user buffer,
and skb can be put back to head of sk_receive_queue
Here is the patch to address this point.

Apply it after (af_unix: Fix use-after-free crashes)

I can make a combo patch once everybody agrees.

[PATCH net-next] af_unix: fix use after free in unix_stream_recvmsg()

Commit 0856a30409 (Scm: Remove unnecessary pid & credential references
in Unix socket's send and receive path) introduced an use-after-free
bug in unix_stream_recvmsg().

We should call consume_skb(skb) only after our possible use of pid/cred.

Signed-off-by: Eric Dumazet <redacted>
---
 net/unix/af_unix.c |   13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index c8a08ba..1bd4ecf 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1873,6 +1873,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 	int target;
 	int err = 0;
 	long timeo;
+	struct sk_buff *skb;
 
 	err = -EINVAL;
 	if (sk->sk_state != TCP_ESTABLISHED)
@@ -1904,7 +1905,6 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 
 	do {
 		int chunk;
-		struct sk_buff *skb;
 
 		unix_state_lock(sk);
 		skb = skb_dequeue(&sk->sk_receive_queue);
@@ -1949,6 +1949,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			if ((UNIXCB(skb).pid  != siocb->scm->pid) ||
 			    (UNIXCB(skb).cred != siocb->scm->cred)) {
 				skb_queue_head(&sk->sk_receive_queue, skb);
+				skb = NULL;
 				break;
 			}
 		} else {
@@ -1967,6 +1968,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 		chunk = min_t(unsigned int, skb->len, size);
 		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
 			skb_queue_head(&sk->sk_receive_queue, skb);
+			skb = NULL;
 			if (copied == 0)
 				copied = -EFAULT;
 			break;
@@ -1984,13 +1986,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 			/* put the skb back if we didn't use it up.. */
 			if (skb->len) {
 				skb_queue_head(&sk->sk_receive_queue, skb);
+				skb = NULL;
 				break;
 			}
 
-			consume_skb(skb);
-
-			if (siocb->scm->fp)
+			if (UNIXCB(skb).pid || siocb->scm->fp)
 				break;
+			consume_skb(skb);
+			skb = NULL;
 		} else {
 			/* It is questionable, see note in unix_dgram_recvmsg.
 			 */
@@ -1999,12 +2002,14 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
 
 			/* put message back and return */
 			skb_queue_head(&sk->sk_receive_queue, skb);
+			skb = NULL;
 			break;
 		}
 	} while (size);
 
 	mutex_unlock(&u->readlock);
 	scm_recv(sock, msg, siocb->scm, flags);
+	consume_skb(skb);
 out:
 	return copied ? : err;
 }
Acked-by: Tim Chen <redacted>

Re: [PATCH net-next v3] af_unix: Fix use-after-free crashes

From: Tim Chen <hidden>
Date: 2011-09-09 17:41:31

On Fri, 2011-09-09 at 08:51 +0200, Eric Dumazet wrote:
Le jeudi 08 septembre 2011 à 01:37 -0700, Tim Chen a écrit :
quoted
On Thu, 2011-09-08 at 15:21 +0200, Eric Dumazet wrote:
quoted
Le jeudi 08 septembre 2011 à 11:59 +0200, Sedat Dilek a écrit :
quoted
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>
---
Thanks.

Acked-by: Tim Chen <redacted>
Now we have to fix a bug in unix_stream_recvmsg() as well.
Thanks for your careful review to catch the bugs.  Wish I had thought
about the stream msg case more in my original patch.
consume_skb() call actually releases pid/cred references, and we can use
them after their eventual freeing.

Keep also in mind that receiver can provides a too short user buffer,
and skb can be put back to head of sk_receive_queue

Tim, your 0856a304091b33 commit introduced a lot of bugs, I was right
asking a revert.

If we revert your patch, my litle patch (af_unix: dont send
SCM_CREDENTIALS by default) is enough to solve performance problems.
But the regression where we do send SCM_CREDENTIALS is not addressed,
right?  I don't mind reverting my patch if there's a better solution.

Tim

Re: [PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Tim Chen <hidden>
Date: 2011-09-12 19:12:31

On Fri, 2011-09-09 at 07:06 +0200, Eric Dumazet wrote:
Le jeudi 08 septembre 2011 à 02:24 -0700, Tim Chen a écrit :
quoted
Looking forward to the patch.  This should improve the scalability of
af_unix.
Here it is, based on top on previous one [af_unix: Fix use-after-free
crashes]

Thanks

[PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.

# Events: 154K cycles
#
# Overhead  Command       Shared Object                               Symbol
# ........  .......  ..................  ...................................
#
    10.40%  hackbench  [kernel.kallsyms]   [k] put_pid
     8.60%  hackbench  [kernel.kallsyms]   [k] unix_stream_recvmsg
     7.87%  hackbench  [kernel.kallsyms]   [k] unix_stream_sendmsg
     6.11%  hackbench  [kernel.kallsyms]   [k] do_raw_spin_lock
     4.95%  hackbench  [kernel.kallsyms]   [k] unix_scm_to_skb
     4.87%  hackbench  [kernel.kallsyms]   [k] pid_nr_ns
     4.34%  hackbench  [kernel.kallsyms]   [k] cred_to_ucred
     2.39%  hackbench  [kernel.kallsyms]   [k] unix_destruct_scm
     2.24%  hackbench  [kernel.kallsyms]   [k] sub_preempt_count
     1.75%  hackbench  [kernel.kallsyms]   [k] fget_light
     1.51%  hackbench  [kernel.kallsyms]   [k] __mutex_lock_interruptible_slowpath
     1.42%  hackbench  [kernel.kallsyms]   [k] sock_alloc_send_pskb


This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call.

Performance boost in hackbench : more than 50% gain on a 16 thread
machine (2 quad-core cpus, 2 threads per core)

hackbench 20 thread 2000

4.224 sec instead of 9.102 sec


Signed-off-by: Eric Dumazet <redacted>
This speeds up the case where we're not passing SCM_CREDENTIALS nicely.

Acked-by: Tim Chen <redacted>

Re: [PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

From: David Miller <davem@davemloft.net>
Date: 2011-09-19 01:10:27

Eric, please respin your patches against current net-next, thanks!

Re: [PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Eric Dumazet <hidden>
Date: 2011-09-19 04:29:04

Le dimanche 18 septembre 2011 à 21:07 -0400, David Miller a écrit :
Eric, please respin your patches against current net-next, thanks!
Sure, here it is :

Its a bit different, so I didnt add Tim 'Acked-by'

[PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.

# Events: 154K cycles
#
# Overhead  Command       Shared Object        Symbol
# ........  .......  ..................  .........................
#
    10.40%  hackbench  [kernel.kallsyms]   [k] put_pid
     8.60%  hackbench  [kernel.kallsyms]   [k] unix_stream_recvmsg
     7.87%  hackbench  [kernel.kallsyms]   [k] unix_stream_sendmsg
     6.11%  hackbench  [kernel.kallsyms]   [k] do_raw_spin_lock
     4.95%  hackbench  [kernel.kallsyms]   [k] unix_scm_to_skb
     4.87%  hackbench  [kernel.kallsyms]   [k] pid_nr_ns
     4.34%  hackbench  [kernel.kallsyms]   [k] cred_to_ucred
     2.39%  hackbench  [kernel.kallsyms]   [k] unix_destruct_scm
     2.24%  hackbench  [kernel.kallsyms]   [k] sub_preempt_count
     1.75%  hackbench  [kernel.kallsyms]   [k] fget_light
     1.51%  hackbench  [kernel.kallsyms]   [k]
__mutex_lock_interruptible_slowpath
     1.42%  hackbench  [kernel.kallsyms]   [k] sock_alloc_send_pskb


This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call.

Performance boost in hackbench : more than 50% gain on a 16 thread
machine (2 quad-core cpus, 2 threads per core)

hackbench 20 thread 2000

4.228 sec instead of 9.102 sec

Signed-off-by: Eric Dumazet <redacted>
---
 include/net/scm.h        |    5 ++---
 net/core/scm.c           |   10 ++++++----
 net/netlink/af_netlink.c |    5 ++---
 net/unix/af_unix.c       |    4 +++-
 4 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 745460f..d456f4c 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -49,7 +49,7 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm,
 				    struct pid *pid, const struct cred *cred)
 {
 	scm->pid  = get_pid(pid);
-	scm->cred = get_cred(cred);
+	scm->cred = cred ? get_cred(cred) : NULL;
 	cred_to_ucred(pid, cred, &scm->creds);
 }
 
@@ -73,8 +73,7 @@ static __inline__ void scm_destroy(struct scm_cookie *scm)
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
 			       struct scm_cookie *scm)
 {
-	scm_set_cred(scm, task_tgid(current), current_cred());
-	scm->fp = NULL;
+	memset(scm, 0, sizeof(*scm));
 	unix_get_peersec_dgram(sock, scm);
 	if (msg->msg_controllen <= 0)
 		return 0;
diff --git a/net/core/scm.c b/net/core/scm.c
index 811b53f..ff52ad0 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -173,7 +173,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 			if (err)
 				goto error;
 
-			if (pid_vnr(p->pid) != p->creds.pid) {
+			if (!p->pid || pid_vnr(p->pid) != p->creds.pid) {
 				struct pid *pid;
 				err = -ESRCH;
 				pid = find_get_pid(p->creds.pid);
@@ -183,8 +183,9 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 				p->pid = pid;
 			}
 
-			if ((p->cred->euid != p->creds.uid) ||
-				(p->cred->egid != p->creds.gid)) {
+			if (!p->cred ||
+			    (p->cred->euid != p->creds.uid) ||
+			    (p->cred->egid != p->creds.gid)) {
 				struct cred *cred;
 				err = -ENOMEM;
 				cred = prepare_creds();
@@ -193,7 +194,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 
 				cred->uid = cred->euid = p->creds.uid;
 				cred->gid = cred->egid = p->creds.gid;
-				put_cred(p->cred);
+				if (p->cred)
+					put_cred(p->cred);
 				p->cred = cred;
 			}
 			break;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 4330db9..1201b6d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1324,10 +1324,9 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (NULL == siocb->scm) {
+	if (NULL == siocb->scm)
 		siocb->scm = &scm;
-		memset(&scm, 0, sizeof(scm));
-	}
+
 	err = scm_send(sock, msg, siocb->scm);
 	if (err < 0)
 		return err;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index ec68e1c..90c55c6 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1381,8 +1381,10 @@ 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)
 {
 	int err = 0;
+
 	UNIXCB(skb).pid  = get_pid(scm->pid);
-	UNIXCB(skb).cred = get_cred(scm->cred);
+	if (scm->cred)
+		UNIXCB(skb).cred = get_cred(scm->cred);
 	UNIXCB(skb).fp = NULL;
 	if (scm->fp && send_fds)
 		err = unix_attach_fds(scm, skb);

Re: [PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Eric Dumazet <hidden>
Date: 2011-09-19 15:01:59

Le lundi 19 septembre 2011 à 06:28 +0200, Eric Dumazet a écrit :
Le dimanche 18 septembre 2011 à 21:07 -0400, David Miller a écrit :
quoted
Eric, please respin your patches against current net-next, thanks!
Sure, here it is :

Its a bit different, so I didnt add Tim 'Acked-by'

[PATCH net-next] af_unix: dont send SCM_CREDENTIALS by default

Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.
This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call.

I appears udevd is such an application.

I am trying testing SOCK_PASSCRED flag and include SCM_CREDENTIALS if
set.

[PATCH v2 net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Eric Dumazet <hidden>
Date: 2011-09-19 15:52:19

Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.

# Events: 154K cycles
#
# Overhead  Command       Shared Object        Symbol
# ........  .......  ..................  .........................
#
    10.40%  hackbench  [kernel.kallsyms]   [k] put_pid
     8.60%  hackbench  [kernel.kallsyms]   [k] unix_stream_recvmsg
     7.87%  hackbench  [kernel.kallsyms]   [k] unix_stream_sendmsg
     6.11%  hackbench  [kernel.kallsyms]   [k] do_raw_spin_lock
     4.95%  hackbench  [kernel.kallsyms]   [k] unix_scm_to_skb
     4.87%  hackbench  [kernel.kallsyms]   [k] pid_nr_ns
     4.34%  hackbench  [kernel.kallsyms]   [k] cred_to_ucred
     2.39%  hackbench  [kernel.kallsyms]   [k] unix_destruct_scm
     2.24%  hackbench  [kernel.kallsyms]   [k] sub_preempt_count
     1.75%  hackbench  [kernel.kallsyms]   [k] fget_light
     1.51%  hackbench  [kernel.kallsyms]   [k]
__mutex_lock_interruptible_slowpath
     1.42%  hackbench  [kernel.kallsyms]   [k] sock_alloc_send_pskb


This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call, and receiver not using SO_PASSCRED
socket option.

If SOCK_PASSCRED is set on source or destination socket, we still
include credentials for mere write() syscalls.

Performance boost in hackbench : more than 50% gain on a 16 thread
machine (2 quad-core cpus, 2 threads per core)

hackbench 20 thread 2000

4.228 sec instead of 9.102 sec

Signed-off-by: Eric Dumazet <redacted>
---
 net/netlink/af_netlink.c net/unix/af_unix.c|diffstat -p1 -w70
 include/net/scm.h        |    5 ++---
 net/core/scm.c           |   10 ++++++----
 net/netlink/af_netlink.c |    5 ++---
 net/unix/af_unix.c       |   24 +++++++++++++++++++++++--
 4 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/include/net/scm.h b/include/net/scm.h
index 745460f..d456f4c 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -49,7 +49,7 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm,
 				    struct pid *pid, const struct cred *cred)
 {
 	scm->pid  = get_pid(pid);
-	scm->cred = get_cred(cred);
+	scm->cred = cred ? get_cred(cred) : NULL;
 	cred_to_ucred(pid, cred, &scm->creds);
 }
 
@@ -73,8 +73,7 @@ static __inline__ void scm_destroy(struct scm_cookie *scm)
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
 			       struct scm_cookie *scm)
 {
-	scm_set_cred(scm, task_tgid(current), current_cred());
-	scm->fp = NULL;
+	memset(scm, 0, sizeof(*scm));
 	unix_get_peersec_dgram(sock, scm);
 	if (msg->msg_controllen <= 0)
 		return 0;
diff --git a/net/core/scm.c b/net/core/scm.c
index 811b53f..ff52ad0 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -173,7 +173,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 			if (err)
 				goto error;
 
-			if (pid_vnr(p->pid) != p->creds.pid) {
+			if (!p->pid || pid_vnr(p->pid) != p->creds.pid) {
 				struct pid *pid;
 				err = -ESRCH;
 				pid = find_get_pid(p->creds.pid);
@@ -183,8 +183,9 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 				p->pid = pid;
 			}
 
-			if ((p->cred->euid != p->creds.uid) ||
-				(p->cred->egid != p->creds.gid)) {
+			if (!p->cred ||
+			    (p->cred->euid != p->creds.uid) ||
+			    (p->cred->egid != p->creds.gid)) {
 				struct cred *cred;
 				err = -ENOMEM;
 				cred = prepare_creds();
@@ -193,7 +194,8 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
 
 				cred->uid = cred->euid = p->creds.uid;
 				cred->gid = cred->egid = p->creds.gid;
-				put_cred(p->cred);
+				if (p->cred)
+					put_cred(p->cred);
 				p->cred = cred;
 			}
 			break;
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 4330db9..1201b6d 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1324,10 +1324,9 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
 
-	if (NULL == siocb->scm) {
+	if (NULL == siocb->scm)
 		siocb->scm = &scm;
-		memset(&scm, 0, sizeof(scm));
-	}
+
 	err = scm_send(sock, msg, siocb->scm);
 	if (err < 0)
 		return err;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index ec68e1c..466fbcc 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1381,8 +1381,10 @@ 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)
 {
 	int err = 0;
+
 	UNIXCB(skb).pid  = get_pid(scm->pid);
-	UNIXCB(skb).cred = get_cred(scm->cred);
+	if (scm->cred)
+		UNIXCB(skb).cred = get_cred(scm->cred);
 	UNIXCB(skb).fp = NULL;
 	if (scm->fp && send_fds)
 		err = unix_attach_fds(scm, skb);
@@ -1392,6 +1394,24 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
 }
 
 /*
+ * Some apps rely on write() giving SCM_CREDENTIALS
+ * We include credentials if source or destination socket
+ * asserted SOCK_PASSCRED.
+ */
+static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
+			    const struct sock *other)
+{
+	if (UNIXCB(skb).cred)
+		return;
+	if (test_bit(SOCK_PASSCRED, &sock->flags) ||
+	    !other->sk_socket ||
+	    test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
+		UNIXCB(skb).pid  = get_pid(task_tgid(current));
+		UNIXCB(skb).cred = get_current_cred();
+	}
+}
+
+/*
  *	Send AF_UNIX data.
  */
 
@@ -1538,6 +1558,7 @@ restart:
 
 	if (sock_flag(other, SOCK_RCVTSTAMP))
 		__net_timestamp(skb);
+	maybe_add_creds(skb, sock, other);
 	skb_queue_tail(&other->sk_receive_queue, skb);
 	if (max_level > unix_sk(other)->recursion_level)
 		unix_sk(other)->recursion_level = max_level;
@@ -1652,6 +1673,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		    (other->sk_shutdown & RCV_SHUTDOWN))
 			goto pipe_err_free;
 
+		maybe_add_creds(skb, sock, other);
 		skb_queue_tail(&other->sk_receive_queue, skb);
 		if (max_level > unix_sk(other)->recursion_level)
 			unix_sk(other)->recursion_level = max_level;

Re: [PATCH v2 net-next] af_unix: dont send SCM_CREDENTIALS by default

From: Tim Chen <hidden>
Date: 2011-09-19 21:40:03

On Mon, 2011-09-19 at 17:52 +0200, Eric Dumazet wrote:
Since commit 7361c36c5224 (af_unix: Allow credentials to work across
user and pid namespaces) af_unix performance dropped a lot.

This is because we now take a reference on pid and cred in each write(),
and release them in read(), usually done from another process,
eventually from another cpu. This triggers false sharing.

# Events: 154K cycles
#
# Overhead  Command       Shared Object        Symbol
# ........  .......  ..................  .........................
#
    10.40%  hackbench  [kernel.kallsyms]   [k] put_pid
     8.60%  hackbench  [kernel.kallsyms]   [k] unix_stream_recvmsg
     7.87%  hackbench  [kernel.kallsyms]   [k] unix_stream_sendmsg
     6.11%  hackbench  [kernel.kallsyms]   [k] do_raw_spin_lock
     4.95%  hackbench  [kernel.kallsyms]   [k] unix_scm_to_skb
     4.87%  hackbench  [kernel.kallsyms]   [k] pid_nr_ns
     4.34%  hackbench  [kernel.kallsyms]   [k] cred_to_ucred
     2.39%  hackbench  [kernel.kallsyms]   [k] unix_destruct_scm
     2.24%  hackbench  [kernel.kallsyms]   [k] sub_preempt_count
     1.75%  hackbench  [kernel.kallsyms]   [k] fget_light
     1.51%  hackbench  [kernel.kallsyms]   [k]
__mutex_lock_interruptible_slowpath
     1.42%  hackbench  [kernel.kallsyms]   [k] sock_alloc_send_pskb


This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call, and receiver not using SO_PASSCRED
socket option.

If SOCK_PASSCRED is set on source or destination socket, we still
include credentials for mere write() syscalls.

Performance boost in hackbench : more than 50% gain on a 16 thread
machine (2 quad-core cpus, 2 threads per core)

hackbench 20 thread 2000

4.228 sec instead of 9.102 sec

Signed-off-by: Eric Dumazet <redacted>
---
Do we have to worry about the case where peer socket changes its flag
to SOCK_PASSCRED while packets are in flight?  If there isn't such
pathological use case, the patch looks fine to me.

Acked-by: Tim Chen <redacted>

Re: [PATCH v2 net-next] af_unix: dont send SCM_CREDENTIALS by default

From: David Miller <hidden>
Date: 2011-09-28 17:30:52

From: Eric Dumazet <redacted>
Date: Mon, 19 Sep 2011 17:52:27 +0200
This patch includes SCM_CREDENTIALS information in a af_unix message/skb
only if requested by the sender, [man 7 unix for details how to include
ancillary data using sendmsg() system call]

Note: This might break buggy applications that expected SCM_CREDENTIAL
from an unaware write() system call, and receiver not using SO_PASSCRED
socket option.

If SOCK_PASSCRED is set on source or destination socket, we still
include credentials for mere write() syscalls.
I thought a lot about this and I think we should be able to get away
with this trick, so I've added this patch to net-next, thanks!
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help