| From 60eb9918333c0401f6821260a7eeac8b96d748d6 Mon Sep 17 00:00:00 2001 |
| From: Eric Biggers <ebiggers@google.com> |
| Date: Sat, 7 Mar 2020 18:38:49 -0800 |
| Subject: [PATCH] libfs: fix infoleak in simple_attr_read() |
| |
| commit a65cab7d7f05c2061a3e2490257d3086ff3202c6 upstream. |
| |
| Reading from a debugfs file at a nonzero position, without first reading |
| at position 0, leaks uninitialized memory to userspace. |
| |
| It's a bit tricky to do this, since lseek() and pread() aren't allowed |
| on these files, and write() doesn't update the position on them. But |
| writing to them with splice() *does* update the position: |
| |
| #define _GNU_SOURCE 1 |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| int main() |
| { |
| int pipes[2], fd, n, i; |
| char buf[32]; |
| |
| pipe(pipes); |
| write(pipes[1], "0", 1); |
| fd = open("/sys/kernel/debug/fault_around_bytes", O_RDWR); |
| splice(pipes[0], NULL, fd, NULL, 1, 0); |
| n = read(fd, buf, sizeof(buf)); |
| for (i = 0; i < n; i++) |
| printf("%02x", buf[i]); |
| printf("\n"); |
| } |
| |
| Output: |
| 5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a30 |
| |
| Fix the infoleak by making simple_attr_read() always fill |
| simple_attr::get_buf if it hasn't been filled yet. |
| |
| Reported-by: syzbot+fcab69d1ada3e8d6f06b@syzkaller.appspotmail.com |
| Reported-by: Alexander Potapenko <glider@google.com> |
| Fixes: acaefc25d21f ("[PATCH] libfs: add simple attribute files") |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Eric Biggers <ebiggers@google.com> |
| Acked-by: Kees Cook <keescook@chromium.org> |
| Link: https://lore.kernel.org/r/20200308023849.988264-1-ebiggers@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/libfs.c b/fs/libfs.c |
| index 7ac2d4bb4735..0d95cc9d3f2f 100644 |
| --- a/fs/libfs.c |
| +++ b/fs/libfs.c |
| @@ -805,7 +805,7 @@ int simple_attr_open(struct inode *inode, struct file *file, |
| { |
| struct simple_attr *attr; |
| |
| - attr = kmalloc(sizeof(*attr), GFP_KERNEL); |
| + attr = kzalloc(sizeof(*attr), GFP_KERNEL); |
| if (!attr) |
| return -ENOMEM; |
| |
| @@ -845,9 +845,11 @@ ssize_t simple_attr_read(struct file *file, char __user *buf, |
| if (ret) |
| return ret; |
| |
| - if (*ppos) { /* continued read */ |
| + if (*ppos && attr->get_buf[0]) { |
| + /* continued read */ |
| size = strlen(attr->get_buf); |
| - } else { /* first read */ |
| + } else { |
| + /* first read */ |
| u64 val; |
| ret = attr->get(attr->data, &val); |
| if (ret) |
| -- |
| 2.7.4 |
| |