Message ID | 2bf30957-ad04-473a-a72e-8baab648fb56@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | pstore: Fix uaf when backend is unregistered | expand |
On Wed, Oct 09, 2024 at 01:10:14AM +0800, Zach Wade wrote: > when unload pstore_blk, we will unlink the pstore file and > set pos->dentry to NULL, but simple_unlink(d_inode(root), pos->dentry) > may free inode of pos->dentry and free pos by free_pstore_private, > this may trigger uaf. kasan report: Thanks for this! I need to double check what happening here a bit more closely, as maybe some ordering changed after a43e0fc5e913 ("pstore: inode: Only d_invalidate() is needed") > @@ -316,9 +316,10 @@ int pstore_put_backend_records(struct pstore_info *psi) > list_for_each_entry_safe(pos, tmp, &records_list, list) { > if (pos->record->psi == psi) { > list_del_init(&pos->list); > - d_invalidate(pos->dentry); > - simple_unlink(d_inode(root), pos->dentry); > + unlink_dentry = pos->dentry; > pos->dentry = NULL; > + d_invalidate(unlink_dentry); > + simple_unlink(d_inode(root), unlink_dentry); But on the face of it, this does solve the UAF. I'll double-check that this isn't a result of the mentioned commit. -Kees
On 2024/10/9 1:23, Kees Cook wrote: > On Wed, Oct 09, 2024 at 01:10:14AM +0800, Zach Wade wrote: >> when unload pstore_blk, we will unlink the pstore file and >> set pos->dentry to NULL, but simple_unlink(d_inode(root), pos->dentry) >> may free inode of pos->dentry and free pos by free_pstore_private, >> this may trigger uaf. kasan report: > > Thanks for this! I need to double check what happening here a bit more > closely, as maybe some ordering changed after a43e0fc5e913 ("pstore: > inode: Only d_invalidate() is needed") > >> @@ -316,9 +316,10 @@ int pstore_put_backend_records(struct pstore_info *psi) >> list_for_each_entry_safe(pos, tmp, &records_list, list) { >> if (pos->record->psi == psi) { >> list_del_init(&pos->list); >> - d_invalidate(pos->dentry); >> - simple_unlink(d_inode(root), pos->dentry); >> + unlink_dentry = pos->dentry; >> pos->dentry = NULL; >> + d_invalidate(unlink_dentry); >> + simple_unlink(d_inode(root), unlink_dentry); > > But on the face of it, this does solve the UAF. I'll double-check that > this isn't a result of the mentioned commit. Hi Cook, However, I think a more appropriate solution to the problem here is to check the reference count when uninstalling the mount point. At the same time, the test script is attached: #!/bin/bash modprobe pstore_blk blkdev=/dev/sda2 best_effort=on mount -t pstore pstore /sys/fs/pstore sleep 2 umount /sys/fs/pstore modprobe -r pstore_blk Thanks, Zach > > -Kees >
On 2024/10/9 21:36, Zach Wade wrote: > > > On 2024/10/9 1:23, Kees Cook wrote: >> On Wed, Oct 09, 2024 at 01:10:14AM +0800, Zach Wade wrote: >>> when unload pstore_blk, we will unlink the pstore file and >>> set pos->dentry to NULL, but simple_unlink(d_inode(root), pos->dentry) >>> may free inode of pos->dentry and free pos by free_pstore_private, >>> this may trigger uaf. kasan report: >> >> Thanks for this! I need to double check what happening here a bit more >> closely, as maybe some ordering changed after a43e0fc5e913 ("pstore: >> inode: Only d_invalidate() is needed") >> >>> @@ -316,9 +316,10 @@ int pstore_put_backend_records(struct >>> pstore_info *psi) >>> list_for_each_entry_safe(pos, tmp, &records_list, list) { >>> if (pos->record->psi == psi) { >>> list_del_init(&pos->list); >>> - d_invalidate(pos->dentry); >>> - simple_unlink(d_inode(root), pos->dentry); >>> + unlink_dentry = pos->dentry; >>> pos->dentry = NULL; >>> + d_invalidate(unlink_dentry); >>> + simple_unlink(d_inode(root), unlink_dentry); >> >> But on the face of it, this does solve the UAF. I'll double-check that >> this isn't a result of the mentioned commit. > > Hi Cook, > > However, I think a more appropriate solution to the problem here is to > check the reference count when uninstalling the mount point. > > At the same time, the test script is attached: > #!/bin/bash > modprobe pstore_blk > blkdev=/dev/sda2 > best_effort=on > mount -t pstore pstore /sys/fs/pstore > sleep 2 > umount /sys/fs/pstore > modprobe -r pstore_blk > > Thanks, > Zach > >> > >> -Kees >> > friendly ping. Hi Cook, How are you doing? I really hope we can solve this problem. Thanks, Zach
diff --git a/fs/pstore/inode.c b/fs/pstore/inode.c index 56815799ce79..7561693e0f32 100644 --- a/fs/pstore/inode.c +++ b/fs/pstore/inode.c @@ -306,7 +306,7 @@ static struct dentry *psinfo_lock_root(void) int pstore_put_backend_records(struct pstore_info *psi) { struct pstore_private *pos, *tmp; - struct dentry *root; + struct dentry *root, *unlink_dentry; root = psinfo_lock_root(); if (!root) @@ -316,9 +316,10 @@ int pstore_put_backend_records(struct pstore_info *psi) list_for_each_entry_safe(pos, tmp, &records_list, list) { if (pos->record->psi == psi) { list_del_init(&pos->list); - d_invalidate(pos->dentry); - simple_unlink(d_inode(root), pos->dentry); + unlink_dentry = pos->dentry; pos->dentry = NULL; + d_invalidate(unlink_dentry); + simple_unlink(d_inode(root), unlink_dentry); } } }