lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <20170516122734.56760-1-glider@google.com>
Date:   Tue, 16 May 2017 14:27:34 +0200
From:   Alexander Potapenko <glider@...gle.com>
To:     dvyukov@...gle.com, kcc@...gle.com, edumazet@...gle.com,
        viro@...iv.linux.org.uk
Cc:     linux-kernel@...r.kernel.org
Subject: [PATCH] [iov_iter] use memmove() when copying to/from user page

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@...gle.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

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ