]> git.pond.sub.org Git - empserver/commitdiff
Fix race in io_output() that can lead to double-free
authorMarkus Armbruster <armbru@pond.sub.org>
Sun, 4 Jul 2010 15:30:33 +0000 (17:30 +0200)
committerMarkus Armbruster <armbru@pond.sub.org>
Sun, 25 Jul 2010 16:24:00 +0000 (18:24 +0200)
Move call of ioq_makeiov() to its use, because calling it before
empth_select() is racy, as follows.

Player thread flushes output by calling io_output(player->iop, 1).
io_output() sets up iov[] to point to queued output.  empth_select()
blocks on output.

Another thread sends a C_FLASH or C_INFORM message to this player.
This calls io_output(p->iop, 0).  The output file descriptor has
become writable since the player thread blocked on it, so some output
gets written and dequeued.

The player thread resumes, writes out iov[] and dequeues.  Any output
already written by the other thread gets duplicated.  If the other
thread's dequeue operation freed struct io buffers, there's use after
free followed by double-free.

src/lib/empthread/io.c

index 67c605206cf9592cab798a638fc88e5f59551e20..1d78504deef7913b6b3066283548bcd311f25da3 100644 (file)
@@ -209,8 +209,6 @@ io_output(struct iop *iop, int wait)
     if (iop->flags & IO_ERROR)
        return -1;
 
-    n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
-
     if (wait) {
        res = empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
        if (res == 0)
@@ -221,6 +219,7 @@ io_output(struct iop *iop, int wait)
        }
     }
 
+    n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
     cc = writev(iop->fd, iov, n);
     if (cc < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK)