Fix race in io_output() that can lead to double-free

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.
This commit is contained in:
Markus Armbruster 2010-07-04 17:30:33 +02:00
parent 25115f4997
commit d28fbbb186

View file

@ -209,8 +209,6 @@ io_output(struct iop *iop, int wait)
if (iop->flags & IO_ERROR) if (iop->flags & IO_ERROR)
return -1; return -1;
n = ioq_makeiov(iop->output, iov, IO_BUFSIZE);
if (wait) { if (wait) {
res = empth_select(iop->fd, EMPTH_FD_WRITE, NULL); res = empth_select(iop->fd, EMPTH_FD_WRITE, NULL);
if (res == 0) 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); cc = writev(iop->fd, iov, n);
if (cc < 0) { if (cc < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) if (errno == EAGAIN || errno == EWOULDBLOCK)