summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-09-17 17:17:09 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-10-04 20:03:40 +0200
commit9e6b7cd7e77d4ca43b57c726d9bfa86d06e0567f (patch)
tree04e1bc509bbb1dd5323c29df0c642a5cdbec161e
parenttty: fix data race in flush_to_ldisc (diff)
downloadlinux-9e6b7cd7e77d4ca43b57c726d9bfa86d06e0567f.tar.xz
linux-9e6b7cd7e77d4ca43b57c726d9bfa86d06e0567f.zip
tty: fix data race in tty_buffer_flush
tty_buffer_flush frees not acquired buffers. As the result, for example, read of b->size in tty_buffer_free can return garbage value which will lead to a huge buffer hanging in the freelist. This is just the benignest manifestation of freeing of a not acquired object. If the object is passed to kfree, heap can be corrupted. Acquire visibility over the buffer before freeing it. The data race was found with KernelThreadSanitizer (KTSAN). Signed-off-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/tty_buffer.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index 23de97de09a4..b3a5e33948c1 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -242,7 +242,10 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
atomic_inc(&buf->priority);
mutex_lock(&buf->lock);
- while ((next = buf->head->next) != NULL) {
+ /* paired w/ release in __tty_buffer_request_room; ensures there are
+ * no pending memory accesses to the freed buffer
+ */
+ while ((next = smp_load_acquire(&buf->head->next)) != NULL) {
tty_buffer_free(port, buf->head);
buf->head = next;
}