In the Linux kernel, the following vulnerability has been resolved:
ubifs: authentication: Fix use-after-free in ubifstncend_commit
After an insertion in TNC, the tree might split and cause a node to
change its znode->parent
. A further deletion of other nodes in the
tree (which also could free the nodes), the aforementioned node's
znode->cparent
could still point to a freed node. This
znode->cparent
may not be updated when getting nodes to commit in
ubifs_tnc_start_commit()
. This could then trigger a use-after-free
when accessing the znode->cparent
in write_index()
in
ubifs_tnc_end_commit()
.
This can be triggered by running
rm -f /etc/test-file.bin dd if=/dev/urandom of=/etc/test-file.bin bs=1M count=60 conv=fsync
in a loop, and with CONFIG_UBIFS_FS_AUTHENTICATION
. KASAN then
reports:
BUG: KASAN: use-after-free in ubifstncendcommit+0xa5c/0x1950 Write of size 32 at addr ffffff800a3af86c by task ubifsbgt0_20/153
Call trace: dumpbacktrace+0x0/0x340 showstack+0x18/0x24 dumpstacklvl+0x9c/0xbc printaddressdescription.constprop.0+0x74/0x2b0 kasanreport+0x1d8/0x1f0 kasancheckrange+0xf8/0x1a0 memcpy+0x84/0xf4 ubifstncendcommit+0xa5c/0x1950 docommit+0x4e0/0x1340 ubifsbgthread+0x234/0x2e0 kthread+0x36c/0x410 retfrom_fork+0x10/0x20
Allocated by task 401: kasansavestack+0x38/0x70 _kasankmalloc+0x8c/0xd0 _kmalloc+0x34c/0x5bc tncinsert+0x140/0x16a4 ubifstncadd+0x370/0x52c ubifsjnlwritedata+0x5d8/0x870 dowritepage+0x36c/0x510 ubifswritepage+0x190/0x4dc _writepage+0x58/0x154 writecachepages+0x394/0x830 dowritepages+0x1f0/0x5b0 filemapfdatawritewbc+0x170/0x25c filewriteandwaitrange+0x140/0x190 ubifsfsync+0xe8/0x290 vfsfsyncrange+0xc0/0x1e4 dofsync+0x40/0x90 _arm64sysfsync+0x34/0x50 invokesyscall.constprop.0+0xa8/0x260 doel0svc+0xc8/0x1f0 el0svc+0x34/0x70 el0t64synchandler+0x108/0x114 el0t64_sync+0x1a4/0x1a8
Freed by task 403: kasansavestack+0x38/0x70 kasansettrack+0x28/0x40 kasansetfreeinfo+0x28/0x4c _kasanslabfree+0xd4/0x13c kfree+0xc4/0x3a0 tncdelete+0x3f4/0xe40 ubifstncremoverange+0x368/0x73c ubifstncremoveino+0x29c/0x2e0 ubifsjnldeleteinode+0x150/0x260 ubifsevictinode+0x1d4/0x2e4 evict+0x1c8/0x450 iput+0x2a0/0x3c4 dounlinkat+0x2cc/0x490 _arm64sysunlinkat+0x90/0x100 invokesyscall.constprop.0+0xa8/0x260 doel0svc+0xc8/0x1f0 el0svc+0x34/0x70 el0t64synchandler+0x108/0x114 el0t64_sync+0x1a4/0x1a8
The offending memcpy()
in ubifs_copy_hash()
has a use-after-free
when a node becomes root in TNC but still has a cparent
to an already
freed node. More specifically, consider the following TNC:
zroot
/
/
zp1
/
/
zn
Inserting a new node zn_new
with a key smaller then zn
will trigger
a split in tnc_insert()
if zp1
is full:
zroot
/ \
/ \
zp1 zp2
/ \
/ \
zn_new zn
zn->parent
has now been moved to zp2
, but zn->cparent
still
points to zp1
.
Now, consider a removal of all the nodes except zn
. Just when
tnc_delete()
is about to delete zroot
and zp2
:
zroot
\
\
zp2
\
\
zn
zroot
and zp2
get freed and the tree collapses:
zn
zn
now becomes the new zroot
.
get_znodes_to_commit()
will now only find zn
, the new zroot
, and
write_index()
will check its znode->cparent
that wrongly points to
the already freed zp1
. ubifs_copy_hash()
thus gets wrongly called
with znode->cparent->zbranch[znode->iip].hash
that triggers the
use-after-free!
Fix this by explicitly setting znode->cparent
to NULL
in
get_znodes_to_commit()
for the root node. The search for the dirty
nodes
---truncated---