* [PATCH 2/2] tty: Fix lockless tty buffer race [not found] <1399042572-6533-1-git-send-email-peter@hurleysoftware.com> @ 2014-05-02 14:56 ` Peter Hurley 2014-05-02 15:05 ` Peter Hurley 0 siblings, 1 reply; 3+ messages in thread From: Peter Hurley @ 2014-05-02 14:56 UTC (permalink / raw) To: Greg Kroah-Hartman Cc: Jiri Slaby, One Thousand Gnomes, Manfred Schlaegl, linux-kernel, Peter Hurley, stable Commit 6a20dbd6caa2358716136144bf524331d70b1e03, "tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc" correctly identifies an unsafe race condition between __tty_buffer_request_room() and flush_to_ldisc(), where the consumer flush_to_ldisc() prematurely advances the head before consuming the last of the data committed. For example: CPU 0 | CPU 1 __tty_buffer_request_room | flush_to_ldisc ... | ... | count = head->commit - head->read n = tty_buffer_alloc() | b->commit = b->used | b->next = n | | if (!count) /* T */ | if (head->next == NULL) /* F */ | buf->head = head->next In this case, buf->head has been advanced but head->commit may have been updated with a new value. Instead of reintroducing an unnecessary lock, fix the race locklessly. Read the commit-next pair in the reverse order of writing, which guarantees the commit value read is the latest value written if the head is advancing. Reported-by: Manfred Schlaegl <manfred.schlaegl@gmx.at> Cc: <stable@vger.kernel.org> # 3.12.x+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> --- drivers/tty/tty_buffer.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 8ebd9f8..cf78d19 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -258,7 +258,11 @@ static int __tty_buffer_request_room(struct tty_port *port, size_t size, n->flags = flags; buf->tail = n; b->commit = b->used; - smp_mb(); + /* paired w/ barrier in flush_to_ldisc(); ensures the + * latest commit value can be read before the head is + * advanced to the next buffer + */ + smp_wmb(); b->next = n; } else if (change) size = 0; @@ -444,17 +448,24 @@ static void flush_to_ldisc(struct work_struct *work) while (1) { struct tty_buffer *head = buf->head; + struct tty_buffer *next; int count; /* Ldisc or user is trying to gain exclusive access */ if (atomic_read(&buf->priority)) break; + next = head->next; + /* paired w/ barrier in __tty_buffer_request_room(); + * ensures commit value read is not stale if the head + * is advancing to the next buffer + */ + smp_rmb(); count = head->commit - head->read; if (!count) { - if (head->next == NULL) + if (next == NULL) break; - buf->head = head->next; + buf->head = next; tty_buffer_free(port, head); continue; } -- 1.8.1.2 ^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 2/2] tty: Fix lockless tty buffer race 2014-05-02 14:56 ` [PATCH 2/2] tty: Fix lockless tty buffer race Peter Hurley @ 2014-05-02 15:05 ` Peter Hurley 2014-05-06 8:00 ` Manfred Schlaegl 0 siblings, 1 reply; 3+ messages in thread From: Peter Hurley @ 2014-05-02 15:05 UTC (permalink / raw) To: Greg Kroah-Hartman Cc: Jiri Slaby, One Thousand Gnomes, Manfred Schlaegl, linux-kernel, stable On 05/02/2014 10:56 AM, Peter Hurley wrote: > Commit 6a20dbd6caa2358716136144bf524331d70b1e03, > "tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc" > correctly identifies an unsafe race condition between > __tty_buffer_request_room() and flush_to_ldisc(), where the consumer > flush_to_ldisc() prematurely advances the head before consuming the > last of the data committed. For example: > > CPU 0 | CPU 1 > __tty_buffer_request_room | flush_to_ldisc > ... | ... > | count = head->commit - head->read > n = tty_buffer_alloc() | > b->commit = b->used | > b->next = n | > | if (!count) /* T */ > | if (head->next == NULL) /* F */ > | buf->head = head->next > > In this case, buf->head has been advanced but head->commit may have > been updated with a new value. > > Instead of reintroducing an unnecessary lock, fix the race locklessly. > Read the commit-next pair in the reverse order of writing, which guarantees > the commit value read is the latest value written if the head is > advancing. > > Reported-by: Manfred Schlaegl <manfred.schlaegl@gmx.at> > Cc: <stable@vger.kernel.org> # 3.12.x+ The patch submitted by Manfred notes the commits which introduced the race [1], but attributes those commits to the 3.11 cycle. Those commits were merged in the 3.12 cycle. Regards, Peter Hurley [1] commits e8437d7ecbc50198705331449367d401ebb3181f, "tty: Make driver-side flip buffers lockless", and e9975fdec0138f1b2a85b9624e41660abd9865d4, "tty: Ensure single-threaded flip buffer consumer with mutex" ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 2/2] tty: Fix lockless tty buffer race 2014-05-02 15:05 ` Peter Hurley @ 2014-05-06 8:00 ` Manfred Schlaegl 0 siblings, 0 replies; 3+ messages in thread From: Manfred Schlaegl @ 2014-05-06 8:00 UTC (permalink / raw) To: Peter Hurley, Greg Kroah-Hartman Cc: Jiri Slaby, One Thousand Gnomes, linux-kernel, stable On 2014-05-02 17:05, Peter Hurley wrote: > On 05/02/2014 10:56 AM, Peter Hurley wrote: >> Commit 6a20dbd6caa2358716136144bf524331d70b1e03, >> "tty: Fix race condition between __tty_buffer_request_room and flush_to_ldisc" >> correctly identifies an unsafe race condition between >> __tty_buffer_request_room() and flush_to_ldisc(), where the consumer >> flush_to_ldisc() prematurely advances the head before consuming the >> last of the data committed. For example: >> >> CPU 0 | CPU 1 >> __tty_buffer_request_room | flush_to_ldisc >> ... | ... >> | count = head->commit - head->read >> n = tty_buffer_alloc() | >> b->commit = b->used | >> b->next = n | >> | if (!count) /* T */ >> | if (head->next == NULL) /* F */ >> | buf->head = head->next >> >> In this case, buf->head has been advanced but head->commit may have >> been updated with a new value. >> >> Instead of reintroducing an unnecessary lock, fix the race locklessly. >> Read the commit-next pair in the reverse order of writing, which guarantees >> the commit value read is the latest value written if the head is >> advancing. This is a fine solution! I'll verify this against my previous experimental setup (3.12.x and 3.12.x-rt25), but I dont't expect any problems. >> >> Reported-by: Manfred Schlaegl <manfred.schlaegl@gmx.at> >> Cc: <stable@vger.kernel.org> # 3.12.x+ > > The patch submitted by Manfred notes the commits which introduced the > race [1], but attributes those commits to the 3.11 cycle. Those commits > were merged in the 3.12 cycle. You are right. I'm sorry for this. Regars, Manfred ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2014-05-06 8:00 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1399042572-6533-1-git-send-email-peter@hurleysoftware.com>
2014-05-02 14:56 ` [PATCH 2/2] tty: Fix lockless tty buffer race Peter Hurley
2014-05-02 15:05 ` Peter Hurley
2014-05-06 8:00 ` Manfred Schlaegl
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox