diff options
author | Andreas Kling <kling@serenityos.org> | 2022-01-11 00:51:05 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-11 01:12:16 +0100 |
commit | 08e927f084cec99586b50fafa2ba03d5c70c6992 (patch) | |
tree | d19275295e31cd87bebaa93578eda059a0979344 /Kernel/FileSystem | |
parent | 3550f12543d851fcfe5b477dd0569839c467d3e8 (diff) | |
download | serenity-08e927f084cec99586b50fafa2ba03d5c70c6992.zip |
Kernel: Synchronize removals from TmpFS inode map
Previously we were uncaching inodes from TmpFSInode::one_ref_left().
This was not safe, since one_ref_left() was effectively being called
on a raw pointer after decrementing the local ref count and observing
it become 1. There was a race here where someone else could trigger
the destructor by unreffing to 0 before one_ref_left() got called,
causing us to call one_ref_left() on a deleted inode.
We fix this by using the new remove_from_secondary_lists() mechanism
in ListedRefCounted and synchronizing all access to the TmpFS inode
map with the main Inode::all_instances() lock.
There's probably a nicer way to solve this.
Diffstat (limited to 'Kernel/FileSystem')
-rw-r--r-- | Kernel/FileSystem/Inode.h | 2 | ||||
-rw-r--r-- | Kernel/FileSystem/TmpFS.cpp | 29 | ||||
-rw-r--r-- | Kernel/FileSystem/TmpFS.h | 4 |
3 files changed, 18 insertions, 17 deletions
diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h index 6ad3d0dd81..a502b6dc8b 100644 --- a/Kernel/FileSystem/Inode.h +++ b/Kernel/FileSystem/Inode.h @@ -31,7 +31,7 @@ class Inode : public ListedRefCounted<Inode, LockType::Spinlock> public: virtual ~Inode(); - virtual void one_ref_left() { } + virtual void remove_from_secondary_lists() { } FileSystem& fs() { return m_file_system; } FileSystem const& fs() const { return m_file_system; } diff --git a/Kernel/FileSystem/TmpFS.cpp b/Kernel/FileSystem/TmpFS.cpp index 440e63cd4c..1d9c2efc41 100644 --- a/Kernel/FileSystem/TmpFS.cpp +++ b/Kernel/FileSystem/TmpFS.cpp @@ -37,19 +37,21 @@ Inode& TmpFS::root_inode() void TmpFS::register_inode(TmpFSInode& inode) { - MutexLocker locker(m_lock); VERIFY(inode.identifier().fsid() == fsid()); - auto index = inode.identifier().index(); - m_inodes.set(index, inode); + Inode::all_instances().with([&](auto&) { + auto index = inode.identifier().index(); + m_inodes.set(index, &inode); + }); } void TmpFS::unregister_inode(InodeIdentifier identifier) { - MutexLocker locker(m_lock); VERIFY(identifier.fsid() == fsid()); - m_inodes.remove(identifier.index()); + Inode::all_instances().with([&](auto&) { + m_inodes.remove(identifier.index()); + }); } unsigned TmpFS::next_inode_index() @@ -61,13 +63,13 @@ unsigned TmpFS::next_inode_index() ErrorOr<NonnullRefPtr<Inode>> TmpFS::get_inode(InodeIdentifier identifier) const { - MutexLocker locker(m_lock, Mutex::Mode::Shared); - VERIFY(identifier.fsid() == fsid()); - - auto it = m_inodes.find(identifier.index()); - if (it == m_inodes.end()) - return ENOENT; - return it->value; + return Inode::all_instances().with([&](auto&) -> ErrorOr<NonnullRefPtr<Inode>> { + VERIFY(identifier.fsid() == fsid()); + auto it = m_inodes.find(identifier.index()); + if (it == m_inodes.end()) + return ENOENT; + return *it->value; + }); } TmpFSInode::TmpFSInode(TmpFS& fs, const InodeMetadata& metadata, InodeIdentifier parent) @@ -363,9 +365,8 @@ ErrorOr<void> TmpFSInode::set_mtime(time_t t) return {}; } -void TmpFSInode::one_ref_left() +void TmpFSInode::remove_from_secondary_lists() { - // Destroy ourselves. fs().unregister_inode(identifier()); } diff --git a/Kernel/FileSystem/TmpFS.h b/Kernel/FileSystem/TmpFS.h index 741575f2ca..d4c0f10d24 100644 --- a/Kernel/FileSystem/TmpFS.h +++ b/Kernel/FileSystem/TmpFS.h @@ -33,7 +33,7 @@ private: RefPtr<TmpFSInode> m_root_inode; - HashMap<InodeIndex, NonnullRefPtr<TmpFSInode>> m_inodes; + HashMap<InodeIndex, TmpFSInode*> m_inodes; ErrorOr<NonnullRefPtr<Inode>> get_inode(InodeIdentifier identifier) const; void register_inode(TmpFSInode&); void unregister_inode(InodeIdentifier); @@ -67,7 +67,7 @@ public: virtual ErrorOr<void> set_atime(time_t) override; virtual ErrorOr<void> set_ctime(time_t) override; virtual ErrorOr<void> set_mtime(time_t) override; - virtual void one_ref_left() override; + virtual void remove_from_secondary_lists() override; private: TmpFSInode(TmpFS& fs, const InodeMetadata& metadata, InodeIdentifier parent); |