* [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