CVE-2022-1247: Race Condition
An issue found in linux-kernel that leads to a race condition in roseconnect(). The rose driver uses roseneigh->use to represent how many objects are using the roseneigh. When a user wants to delete a roseroute via roseioctl(), the rose driver calls rosedelnode() and removes neighbours only if their “count” and “use” are zero.
Other sources
We have found a race condition bug in roseconnect(). The rose driver uses roseneigh->use to represent how many objects are using the roseneigh. When a user wants to delete a roseroute via roseioctl(), the rose driver calls rosedelnode() and removes neighbours only if their "count" and "use" are zero:
''' static int rosedelnode(struct roseroutestruct roseroute, struct netdevice dev) { ... spinlockbh(&rosenodelistlock); spinlockbh(&roseneighlistlock); ... if (roseneigh->count == 0 && roseneigh->use == 0) [1] roseremoveneigh(roseneigh); [2] ... out: spinunlockbh(&roseneighlistlock); spinunlockbh(&rosenodelistlock); ... } '''
As the code shows above, modifications on a roseneigh should be protected by roseneighlistlock. However, roseneigh->use is changed without any locks in roseconnect():
''' static int roseconnect(struct socket sock, struct sockaddr uaddr, int addrlen, int flags) { ... rose->neighbour = rosegetneigh(&addr->sroseaddr, &cause, &diagnostic, 0); [3] ... rose->neighbour->use++; [4] ... } '''
So if a user creates two threads, t1 and t2, and execute functions in the following order: t1[3] - t2[1] - t2[2] - t3[4] The rose socket will keep the pointer of the neighbour while the neighbour is freed, and thus lead to an uaf.
I wrote a PoC and tested it on Linux-5.17-rc5, the result shows as below:
''' / $ /home/pwn/poc > /dev/null [ 44.546300] ================================================================== [ 44.547030] BUG: KASAN: use-after-free in roseconnect+0x11f5/0x1420 [rose] [ 44.548981] Read of size 2 at addr ffff88800de27b2a by task exp/305 [ 44.549048] [ 44.549048] CPU: 7 PID: 305 Comm: exp Tainted: G E 5.17.0-rc5 #1 [ 44.549048] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 [ 44.549048] Call Trace: [ 44.549048] <TASK> [ 44.549048] dumpstacklvl+0x4c/0x63 [ 44.549048] printaddressdescription.constprop.0+0x24/0x150 [ 44.549048] ? roseconnect+0x11f5/0x1420 [rose] [ 44.549048] kasanreport.cold+0x82/0xdb [ 44.549048] ? roseconnect+0x11f5/0x1420 [rose] [ 44.549048] asanreportload2noabort+0x14/0x20 [ 44.549048] roseconnect+0x11f5/0x1420 [rose] [ 44.549048] ? rosenewlci+0xd0/0xd0 [rose] [ 44.549048] ? aaafperm+0x240/0x240 [ 44.549048] ? kasancheckwrite+0x14/0x20 [ 44.549048] ? readhpet+0x152/0x200 [ 44.549048] ? tomoyosocketconnect+0xe/0x10 [ 44.549048] sysconnectfile+0x141/0x1a0 [ 44.549048] ? moveaddrtokernel.part.0+0x36/0xe0 [ 44.549048] sysconnect+0x10c/0x140 [ 44.549048] ? sysconnectfile+0x1a0/0x1a0 [ 44.549048] ? hrtimerinterrupt+0x330/0x740 [ 44.549048] ? debugsmpprocessorid+0x17/0x20 [ 44.549048] ? fpregsassertstateconsistent+0x4e/0xb0 [ 44.549048] x64sysconnect+0x72/0xb0 [ 44.549048] ? irqentryexit+0x33/0x40 [ 44.549048] dosyscall64+0x3b/0xc0 [ 44.549048] entrySYSCALL64afterhwframe+0x44/0xae [ 44.549048] RIP: 0033:0x405b27 [ 44.549048] Code: 44 00 00 41 54 55 41 89 d4 53 48 89 f5 89 fb 48 83 ec 10 e8 0b fb ff ff 44 89 e2 41 89 c0 48 89 ee 89 df b8 2a 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 33 44 89 c7 89 44 24 0c e8 45 fb ff ff 8b 44 [ 44.549048] RSP: 002b:00007f8454b67d20 EFLAGS: 00000293 ORIGRAX: 000000000000002a [ 44.549048] RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000000000405b27 [ 44.549048] RDX: 000000000000001c RSI: 00007ffeedd3c684 RDI: 0000000000000003 [ 44.549048] RBP: 00007ffeedd3c684 R08: 0000000000000000 R09: 00007f8454b68700 [ 44.549048] R10: 00007f8454b689d0 R11: 0000000000000293 R12: 000000000000001c [ 44.549048] R13: 0000000000000000 R14: 00007ffeedd3c680 R15: 00007ffeedd3c5c0 [ 44.549048] </TASK> [ 44.549048] [ 44.549048] Allocated by task 189: [ 44.549048] kasansavestack+0x26/0x50 [ 44.549048] kasankmalloc+0x88/0xa0 [ 44.549048] kmemcachealloctrace+0xc0/0x470 [ 44.549048] rosertioctl+0x7a9/0x1a60 [rose] [ 44.549048] roseioctl+0x5e7/0x6b0 [rose] [ 44.549048] sockdoioctl+0xda/0x1d0 [ 44.549048] sockioctl+0x1b5/0x550 [ 44.549048] x64sysioctl+0x131/0x1a0 [ 44.549048] dosyscall64+0x3b/0xc0 [ 44.549048] entrySYSCALL64afterhwframe+0x44/0xae [ 44.549048] [ 44.549048] Freed by task 304: [ 44.549048] kasansavestack+0x26/0x50 [ 44.549048] kasansettrack+0x25/0x30 [ 44.549048] kasansetfreeinfo+0x24/0x40 [ 44.549048] kasanslabfree+0x100/0x140 [ 44.549048] kfree+0x9a/0x2c0 [ 44.549048] roseremoveneigh+0x1d8/0x2e0 [rose] [ 44.549048] rosertioctl+0xf71/0x1a60 [rose] [ 44.549048] roseioctl+0x5e7/0x6b0 [rose] [ 44.549048] sockdoioctl+0xda/0x1d0 [ 44.549048] sockioctl+0x1b5/0x550 [ 44.549048] x64sysioctl+0x131/0x1a0 [ 44.549048] dosyscall64+0x3b/0xc0 [ 44.549048] entrySYSCALL64afterhwframe+0x44/0xae [ 44.549048] [ 44.549048] The buggy address belongs to the object at ffff88800de27b00 [ 44.549048] which belongs to the cache kmalloc-192 of size 192 [ 44.549048] The buggy address is located 42 bytes inside of [ 44.549048] 192-byte region [ffff88800de27b00, ffff88800de27bc0) [ 44.549048] The buggy address belongs to the page: [ 44.549048] page:(ptrval) refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0xde26 [ 44.549048] head:(ptrval) order:1 compoundmapcount:0 [ 44.549048] flags: 0xfffffc0010200(slab|head|node=0|zone=1|lastcpupid=0x1fffff) [ 44.549048] raw: 000fffffc0010200 0000000000000000 dead000000000122 ffff888005842a00 [ 44.549048] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000 [ 44.549048] page dumped because: kasan: bad access detected [ 44.549048] [ 44.549048] Memory state around the buggy address: [ 44.549048] ffff88800de27a00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 44.549048] ffff88800de27a80: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc [ 44.549048] >ffff88800de27b00: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 44.549048] ^ [ 44.549048] ffff88800de27b80: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc [ 44.549048] ffff88800de27c00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 [ 44.549048] ================================================================== [ 44.549048] Disabling lock debugging due to kernel taint [ 46.800632] random: fast init done / $ '''
By the way, I don't think the "count" and "use" fields are well managed in rose driver, because there are many other places they are used without lock protection, such as roserxcallrequest(), rosestate1machine(), rosestate2machine() and so on. These places should also be checked.
— Red Hat