From: Alexander Potapenko <glider@google.com>
To: dvyukov@google.com, kcc@google.com, edumazet@google.com,
viro@zeniv.linux.org.uk
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH] [iov_iter] use memmove() when copying to/from user page
Date: Tue, 16 May 2017 14:27:34 +0200 [thread overview]
Message-ID: <20170516122734.56760-1-glider@google.com> (raw)
It's possible that calling sendfile() to copy the data from a memfd to
itself may result in doing a memcpy() with overlapping arguments.
To avoid undefined behavior here, replace memcpy() with memmove() and
rename memcpy_to_page()/memcpy_from_page() accordingly.
This problem has been detected with KMSAN and syzkaller.
Signed-off-by: Alexander Potapenko <glider@google.com>
---
For the record, the following program:
===================================
// autogenerated by syzkaller (http://github.com/google/syzkaller)
#ifndef __NR_memfd_create
#define __NR_memfd_create 319
#endif
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static uint64_t current_time_ms()
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts)) _exit(1);
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
}
int fd;
void *page;
void* thr(void* arg)
{
sendfile(fd, fd, page, 0xfec);
return 0;
}
void test()
{
int i;
pthread_t th[2];
page = mmap(0x20000000, 0x1000, 0x3, 0x32, -1, 0);
char buf[1] = "\0";
fd = syscall(__NR_memfd_create, buf, 0);
write(fd, buf, 1);
for (i = 0; i < 2; i++) {
pthread_create(&th[i], 0, thr, NULL);
usleep(10000);
}
usleep(100000);
}
int main()
{
int iter;
for (iter = 0; iter < 10; iter++) {
int pid = fork();
if (pid < 0)
_exit(1);
if (pid == 0) {
test();
_exit(0);
}
int status = 0;
uint64_t start = current_time_ms();
for (;;) {
int res;
if (current_time_ms() - start > 5 * 1000) {
kill(-pid, SIGKILL);
kill(pid, SIGKILL);
while (waitpid(-1, &status, __WALL) != pid) {
}
return 0;
}
usleep(1000);
}
}
return 0;
}
===================================
triggers calls to memcpy() with overlapping arguments:
==================================================================
BUG: memcpy-param-overlap in generic_perform_write+0x551/0xa20
__msan_memcpy(ffff88013c6e3001, ffff88013c6e3000, 105)
CPU: 0 PID: 1040 Comm: probe Not tainted 4.11.0-rc5+ #2562
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:16
dump_stack+0x143/0x1b0 lib/dump_stack.c:52
__msan_memcpy+0x91/0x1e0 mm/kmsan/kmsan_instr.c:359
memcpy_from_page lib/iov_iter.c:433
iov_iter_copy_from_user_atomic+0x98e/0x1320 lib/iov_iter.c:721
generic_perform_write+0x551/0xa20 mm/filemap.c:2843
__generic_file_write_iter+0x3fa/0x8f0 mm/filemap.c:2960
generic_file_write_iter+0x705/0xa80 mm/filemap.c:2988
call_write_iter ./include/linux/fs.h:1733
vfs_iter_write+0x481/0x580 fs/read_write.c:391
iter_file_splice_write+0xa87/0x12f0 fs/splice.c:771
do_splice_from fs/splice.c:873
direct_splice_actor+0x1ae/0x210 fs/splice.c:1040
splice_direct_to_actor+0x683/0xd40 fs/splice.c:995
do_splice_direct+0x327/0x430 fs/splice.c:1083
do_sendfile+0x8a3/0x12e0 fs/read_write.c:1379
SYSC_sendfile64+0x1ab/0x2b0 fs/read_write.c:1434
SyS_sendfile64+0x9a/0xc0 fs/read_write.c:1426
do_syscall_64+0x72/0xa0 arch/x86/entry/common.c:285
entry_SYSCALL64_slow_path+0x25/0x25 arch/x86/entry/entry_64.S:246
RIP: 0033:0x43f4da
RSP: 002b:00007f1f1bba4db8 EFLAGS: 00000206 ORIG_RAX: 0000000000000028
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 000000000043f4da
RDX: 0000000020000000 RSI: 0000000000000003 RDI: 0000000000000003
RBP: 00007f1f1bba4dd0 R08: 00007f1f1bba5700 R09: 00007f1f1bba5700
R10: 0000000000000fec R11: 0000000000000206 R12: 0000000000000000
R13: 0000000000000000 R14: 00007f1f1bba59c0 R15: 00007f1f1bba5700
==================================================================
---
lib/iov_iter.c | 36 +++++++++++++++++++-----------------
1 file changed, 19 insertions(+), 17 deletions(-)
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index f835964c9485..097a8820e930 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -427,17 +427,19 @@ void iov_iter_init(struct iov_iter *i, int direction,
}
EXPORT_SYMBOL(iov_iter_init);
-static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+static void memmove_from_page(char *to, struct page *page, size_t offset,
+ size_t len)
{
char *from = kmap_atomic(page);
- memcpy(to, from + offset, len);
+ memmove(to, from + offset, len);
kunmap_atomic(from);
}
-static void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
+static void memmove_to_page(struct page *page, size_t offset, const char *from,
+ size_t len)
{
char *to = kmap_atomic(page);
- memcpy(to + offset, from, len);
+ memmove(to + offset, from, len);
kunmap_atomic(to);
}
@@ -525,7 +527,7 @@ static size_t copy_pipe_to_iter(const void *addr, size_t bytes,
return 0;
for ( ; n; idx = next_idx(idx, pipe), off = 0) {
size_t chunk = min_t(size_t, n, PAGE_SIZE - off);
- memcpy_to_page(pipe->bufs[idx].page, off, addr, chunk);
+ memmove_to_page(pipe->bufs[idx].page, off, addr, chunk);
i->idx = idx;
i->iov_offset = off + chunk;
n -= chunk;
@@ -543,8 +545,8 @@ size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
iterate_and_advance(i, bytes, v,
__copy_to_user(v.iov_base, (from += v.iov_len) - v.iov_len,
v.iov_len),
- memcpy_to_page(v.bv_page, v.bv_offset,
- (from += v.bv_len) - v.bv_len, v.bv_len),
+ memmove_to_page(v.bv_page, v.bv_offset,
+ (from += v.bv_len) - v.bv_len, v.bv_len),
memcpy(v.iov_base, (from += v.iov_len) - v.iov_len, v.iov_len)
)
@@ -562,8 +564,8 @@ size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
iterate_and_advance(i, bytes, v,
__copy_from_user((to += v.iov_len) - v.iov_len, v.iov_base,
v.iov_len),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
+ memmove_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
)
@@ -586,8 +588,8 @@ bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
v.iov_base, v.iov_len))
return false;
0;}),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
+ memmove_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
)
@@ -606,8 +608,8 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
iterate_and_advance(i, bytes, v,
__copy_from_user_inatomic_nocache((to += v.iov_len) - v.iov_len,
v.iov_base, v.iov_len),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
+ memmove_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
)
@@ -629,8 +631,8 @@ bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i)
v.iov_base, v.iov_len))
return false;
0;}),
- memcpy_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
+ memmove_from_page((to += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
memcpy((to += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
)
@@ -721,8 +723,8 @@ size_t iov_iter_copy_from_user_atomic(struct page *page,
iterate_all_kinds(i, bytes, v,
__copy_from_user_inatomic((p += v.iov_len) - v.iov_len,
v.iov_base, v.iov_len),
- memcpy_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
- v.bv_offset, v.bv_len),
+ memmove_from_page((p += v.bv_len) - v.bv_len, v.bv_page,
+ v.bv_offset, v.bv_len),
memcpy((p += v.iov_len) - v.iov_len, v.iov_base, v.iov_len)
)
kunmap_atomic(kaddr);
--
2.13.0.303.g4ebf302169-goog
next reply other threads:[~2017-05-16 12:27 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-05-16 12:27 Alexander Potapenko [this message]
2017-05-16 18:48 ` [PATCH] [iov_iter] use memmove() when copying to/from user page Al Viro
2017-05-16 18:53 ` Dmitry Vyukov
2017-05-16 19:37 ` Al Viro
2017-05-16 20:10 ` Dmitry Vyukov
2017-05-16 20:52 ` Al Viro
2017-05-16 21:01 ` Dmitry Vyukov
2017-05-16 21:33 ` Al Viro
2017-05-16 22:15 ` Dmitry Vyukov
2017-05-16 22:48 ` Al Viro
2017-05-25 17:04 ` Pavel Machek
2017-05-25 17:15 ` Eric Dumazet
2017-05-25 21:27 ` Pavel Machek
2017-05-25 18:22 ` Al Viro
2017-05-25 19:15 ` Al Viro
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170516122734.56760-1-glider@google.com \
--to=glider@google.com \
--cc=dvyukov@google.com \
--cc=edumazet@google.com \
--cc=kcc@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.