Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1238548 > unrolled thread

[PATCH] unix: fix use-after-free with unix_dgram_poll()

Started byJason Baron <jbaron@akamai.com>
First post2015-10-02 21:20 +0200
Last post2015-10-02 22:00 +0200
Articles 5 — 2 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH] unix: fix use-after-free with unix_dgram_poll() Jason Baron <jbaron@akamai.com> - 2015-10-02 21:20 +0200
    Re: [PATCH] unix: fix use-after-free with unix_dgram_poll() Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2015-10-02 21:40 +0200
      Re: [PATCH] unix: fix use-after-free with unix_dgram_poll() Jason Baron <jbaron@akamai.com> - 2015-10-02 22:00 +0200
        Re: [PATCH] unix: fix use-after-free with unix_dgram_poll() Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2015-10-02 22:20 +0200
      Re: [PATCH] unix: fix use-after-free with unix_dgram_poll() Rainer Weikusat <rweikusat@mobileactivedefense.com> - 2015-10-02 22:00 +0200

#1238548 — [PATCH] unix: fix use-after-free with unix_dgram_poll()

FromJason Baron <jbaron@akamai.com>
Date2015-10-02 21:20 +0200
Subject[PATCH] unix: fix use-after-free with unix_dgram_poll()
Message-ID<qfe26-46r-21@gated-at.bofh.it>
From: Jason Baron <jbaron@akamai.com>

The unix_dgram_poll() routine calls sock_poll_wait() not only for the wait
queue associated with the socket s that we've called poll() on, but it also
calls sock_poll_wait() for a remote peer socket's wait queue, if it's connected.
Thus, if we call poll()/select()/epoll() for the socket s, there are then
a couple of code paths in which the remote peer socket s2 and its associated
peer_wait queue can be freed before poll()/select()/epoll() have a chance
to remove themselves from this remote peer socket s2's wait queue.

The remote peer's socket and associated wait queues can be freed via:

1. If s calls connect() to connect to a new socket other than s2, it will
drop its reference on s2, and thus a close() on s2 will free it.

2. If we call close() on s2, then a subsequent sendmsg() from s, will drop
the final reference to s2, allowing it to be freed.

Address this issue, by reverting unix_dgram_poll() to only register with
the wait queue associated with s and simply drop the second sock_poll_wait()
registration for the remote peer socket wait queue. This then presents the
expected semantics to poll()/select()/epoll().

This works because we will continue to get POLLOUT wakeups from
unix_write_space(), which is called via sock_wfree(). In fact, we avoid having
two wakeup calls here for every buffer we read, since unix_dgram_recvmsg()
unconditionally calls wake_up_interruptible_sync_poll() on its 'peer_wait' queue
and we will no longer be in poll against that queue. So I think this should be
more performant than the current code. And we avoid the second poll() call here
as well during registration.

unix_write_space() should probably be enhanced such that it checks for the
unix_recvq_full() condition as well. In fact, it should probably look for
some fraction of that buffer being free, as is done in unix_writable(). But I'm
considering that a separate enhancement from fixing this issue.

I've tested this by specifically reproducing cases #1 and #2 above as well as
by running the test code here: https://lkml.org/lkml/2015/9/13/195

Signed-off-by: Jason Baron <jbaron@akamai.com>
---
 net/unix/af_unix.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 03ee4d3..c1ae595 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2441,7 +2441,6 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock,
 	other = unix_peer_get(sk);
 	if (other) {
 		if (unix_peer(other) != sk) {
-			sock_poll_wait(file, &unix_sk(other)->peer_wait, wait);
 			if (unix_recvq_full(other))
 				writable = 0;
 		}
-- 
1.8.2.rc2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [next] | [standalone]


#1238559

FromRainer Weikusat <rweikusat@mobileactivedefense.com>
Date2015-10-02 21:40 +0200
Message-ID<qfels-4sN-19@gated-at.bofh.it>
In reply to#1238548
Jason Baron <jbaron@akamai.com> writes:
> From: Jason Baron <jbaron@akamai.com>
>
> The unix_dgram_poll() routine calls sock_poll_wait() not only for the wait
> queue associated with the socket s that we've called poll() on, but it also
> calls sock_poll_wait() for a remote peer socket's wait queue, if it's connected.
> Thus, if we call poll()/select()/epoll() for the socket s, there are then
> a couple of code paths in which the remote peer socket s2 and its associated
> peer_wait queue can be freed before poll()/select()/epoll() have a chance
> to remove themselves from this remote peer socket s2's wait queue.

[...]

> This works because we will continue to get POLLOUT wakeups from
> unix_write_space(), which is called via sock_wfree().

As pointed out in my original comment, this doesn't work (as far as I
can/ could tell) because it will only wake up sockets which had a chance
to enqueue datagrams to the queue of the receiving socket as only
skbuffs enqueued there will be consumed. A socket which is really
waiting for space in the receiving queue won't ever be woken up in this
way.

Further, considering that you're demonstrably not interested in
debugging and fixing this issue (as you haven't even bothered to post
one of the test programs you claim to have), I'm beginning to wonder why
this tripe is being sent to me at all --- it's not "git on autopilot"
this time as someone took the time to dig up my current e-mail address
as the one in the original commit is not valid anymore. Could you please
refrain from such exercises in future unless a discussion is actually
intended?


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1238564

FromJason Baron <jbaron@akamai.com>
Date2015-10-02 22:00 +0200
Message-ID<qfeEO-4Pc-13@gated-at.bofh.it>
In reply to#1238559
On 10/02/2015 03:30 PM, Rainer Weikusat wrote:
> Jason Baron <jbaron@akamai.com> writes:
>> From: Jason Baron <jbaron@akamai.com>
>>
>> The unix_dgram_poll() routine calls sock_poll_wait() not only for the wait
>> queue associated with the socket s that we've called poll() on, but it also
>> calls sock_poll_wait() for a remote peer socket's wait queue, if it's connected.
>> Thus, if we call poll()/select()/epoll() for the socket s, there are then
>> a couple of code paths in which the remote peer socket s2 and its associated
>> peer_wait queue can be freed before poll()/select()/epoll() have a chance
>> to remove themselves from this remote peer socket s2's wait queue.
> 
> [...]
> 
>> This works because we will continue to get POLLOUT wakeups from
>> unix_write_space(), which is called via sock_wfree().
> 
> As pointed out in my original comment, this doesn't work (as far as I
> can/ could tell) because it will only wake up sockets which had a chance
> to enqueue datagrams to the queue of the receiving socket as only
> skbuffs enqueued there will be consumed. A socket which is really
> waiting for space in the receiving queue won't ever be woken up in this
> way.

Ok, good point. I was hoping to avoid a more complex approach here. I think
then that the patch I posted in the previous thread on this would address
this concern. I will post it for review.

> 
> Further, considering that you're demonstrably not interested in
> debugging and fixing this issue (as you haven't even bothered to post
> one of the test programs you claim to have), I'm beginning to wonder why
> this tripe is being sent to me at all --- it's not "git on autopilot"
> this time as someone took the time to dig up my current e-mail address
> as the one in the original commit is not valid anymore. Could you please
> refrain from such exercises in future unless a discussion is actually
> intended?
> 
> 

Just trying to help fix this.

Thanks,

-Jason

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1238578

FromRainer Weikusat <rweikusat@mobileactivedefense.com>
Date2015-10-02 22:20 +0200
Message-ID<qfeYb-5qP-17@gated-at.bofh.it>
In reply to#1238564
Jason Baron <jbaron@akamai.com> writes:
> On 10/02/2015 03:30 PM, Rainer Weikusat wrote:
>> Jason Baron <jbaron@akamai.com> writes:
>>> From: Jason Baron <jbaron@akamai.com>
>>>
>>> The unix_dgram_poll() routine calls sock_poll_wait() not only for the wait
>>> queue associated with the socket s that we've called poll() on, but it also
>>> calls sock_poll_wait() for a remote peer socket's wait queue, if it's connected.
>>> Thus, if we call poll()/select()/epoll() for the socket s, there are then
>>> a couple of code paths in which the remote peer socket s2 and its associated
>>> peer_wait queue can be freed before poll()/select()/epoll() have a chance
>>> to remove themselves from this remote peer socket s2's wait queue.
>> 
>> [...]
>> 
>>> This works because we will continue to get POLLOUT wakeups from
>>> unix_write_space(), which is called via sock_wfree().
>> 
>> As pointed out in my original comment, this doesn't work (as far as I
>> can/ could tell) because it will only wake up sockets which had a chance
>> to enqueue datagrams to the queue of the receiving socket as only
>> skbuffs enqueued there will be consumed. A socket which is really
>> waiting for space in the receiving queue won't ever be woken up in this
>> way.
>
> Ok, good point. I was hoping to avoid a more complex approach here. I think
> then that the patch I posted in the previous thread on this would address
> this concern. I will post it for review.

Some comments on that: From what I remember, this introduced another
wait queue solely for "peer events" in the connecting socket and
enqueued it there on connect. I think this should use the peer_wait
queue because that's what its purpose seems to be and it should also
only be put onto this wait queue if it's actually interested, similar to
the way this is handled in unix_dgram_sendmsg (via
unix_wait_for_peer). But this (likely) implies it would be necessary to
get rid of the second registration in unix_dgram_disconnected (which
gets called if a datagram socket disconnects from another) which may not
be feasible.

Insofar this stays an issue, I'll put more work into this but right now,
my "work" (as in "stuff I'm supposed to do for the people who pay me")
priorities are something rather different.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [next] | [standalone]


#1238565

FromRainer Weikusat <rweikusat@mobileactivedefense.com>
Date2015-10-02 22:00 +0200
Message-ID<qfeEP-4Pc-17@gated-at.bofh.it>
In reply to#1238559
Rainer Weikusat <rw@doppelsaurus.mobileactivedefense.com> writes:
> Jason Baron <jbaron@akamai.com> writes:
>> From: Jason Baron <jbaron@akamai.com>
>>
>> The unix_dgram_poll() routine calls sock_poll_wait() not only for the wait
>> queue associated with the socket s that we've called poll() on, but it also
>> calls sock_poll_wait() for a remote peer socket's wait queue, if it's connected.
>> Thus, if we call poll()/select()/epoll() for the socket s, there are then
>> a couple of code paths in which the remote peer socket s2 and its associated
>> peer_wait queue can be freed before poll()/select()/epoll() have a chance
>> to remove themselves from this remote peer socket s2's wait queue.
>
> [...]
>
>> This works because we will continue to get POLLOUT wakeups from
>> unix_write_space(), which is called via sock_wfree().
>
> As pointed out in my original comment, this doesn't work (as far as I
> can/ could tell) because it will only wake up sockets which had a chance
> to enqueue datagrams to the queue of the receiving socket as only
> skbuffs enqueued there will be consumed. A socket which is really
> waiting for space in the receiving queue won't ever be woken up in this
> way.

Program which shows that (on 3.2.54 + "local modification", with the 2nd
sock_poll_wait commented out):

---------------
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void)
{
    struct sockaddr_un sun;
    struct pollfd pfd;
    int tg, sk0, sk1, rc;
    char buf[16];

    sun.sun_family = AF_UNIX;
    
    tg = socket(AF_UNIX, SOCK_DGRAM, 0);
    strncpy(sun.sun_path, "/tmp/tg", sizeof(sun.sun_path));
    unlink(sun.sun_path);
    bind(tg, (struct sockaddr *)&sun, sizeof(sun));
    
    sk0 = socket(AF_UNIX, SOCK_DGRAM, 0);
    connect(sk0, (struct sockaddr *)&sun, sizeof(sun));
    
    sk1 = socket(AF_UNIX, SOCK_DGRAM, 0);
    connect(sk1, (struct sockaddr *)&sun, sizeof(sun));

    fcntl(sk0, F_SETFL, fcntl(sk0, F_GETFL) | O_NONBLOCK);
    fcntl(sk1, F_SETFL, fcntl(sk1, F_GETFL) | O_NONBLOCK);
    
    while (write(sk0, "bla", 3) != -1);

    if (fork() == 0) {
	pfd.fd = sk1;
	pfd.events = POLLOUT;
	rc = poll(&pfd, 1, -1);

	_exit(0);
    }
    
    sleep(3);
    read(tg, buf, sizeof(buf));
    wait(&rc);

    return 0;
}
------------

For me, this blocks forever while it should terminate as soon as the
datagram was read. Something else may have changed this behaviour in the
meantime, though.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web