diff --git a/Kernel/FileSystem/Ext2FileSystem.cpp b/Kernel/FileSystem/Ext2FileSystem.cpp index 1e4004068d3..2a1c4ebd283 100644 --- a/Kernel/FileSystem/Ext2FileSystem.cpp +++ b/Kernel/FileSystem/Ext2FileSystem.cpp @@ -760,6 +760,10 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, ssize_t count, const u8* data, Fi Locker inode_locker(m_lock); Locker fs_locker(fs().m_lock); + auto result = prepare_to_write_data(); + if (result.is_error()) + return result; + if (is_symlink()) { ASSERT(offset == 0); if (max((size_t)(offset + count), (size_t)m_raw_inode.i_size) < max_inline_symlink_length) { diff --git a/Kernel/FileSystem/Inode.cpp b/Kernel/FileSystem/Inode.cpp index aa67d33cf43..cfae2dc331d 100644 --- a/Kernel/FileSystem/Inode.cpp +++ b/Kernel/FileSystem/Inode.cpp @@ -217,4 +217,19 @@ void Inode::set_metadata_dirty(bool metadata_dirty) } } +KResult Inode::prepare_to_write_data() +{ + // FIXME: It's a poor design that filesystems are expected to call this before writing out data. + // We should funnel everything through an interface at the VFS layer so this can happen from a single place. + LOCKER(m_lock); + if (fs().is_readonly()) + return KResult(-EROFS); + auto metadata = this->metadata(); + if (metadata.is_setuid() || metadata.is_setgid()) { + dbg() << "Inode::prepare_to_write_data(): Stripping SUID/SGID bits from " << identifier(); + return chmod(metadata.mode & ~(04000 | 02000)); + } + return KSuccess; +} + } diff --git a/Kernel/FileSystem/Inode.h b/Kernel/FileSystem/Inode.h index 0a0884a37db..d19637a6b02 100644 --- a/Kernel/FileSystem/Inode.h +++ b/Kernel/FileSystem/Inode.h @@ -119,6 +119,7 @@ protected: void set_metadata_dirty(bool); void inode_contents_changed(off_t, ssize_t, const u8*); void inode_size_changed(size_t old_size, size_t new_size); + KResult prepare_to_write_data(); mutable Lock m_lock { "Inode" }; diff --git a/Kernel/FileSystem/ProcFS.cpp b/Kernel/FileSystem/ProcFS.cpp index 765e7e4bbf8..f3c1880be42 100644 --- a/Kernel/FileSystem/ProcFS.cpp +++ b/Kernel/FileSystem/ProcFS.cpp @@ -1435,6 +1435,10 @@ void ProcFSInode::flush_metadata() ssize_t ProcFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, FileDescription*) { + auto result = prepare_to_write_data(); + if (result.is_error()) + return result; + auto* directory_entry = fs().get_directory_entry(identifier()); Function callback_tmp; diff --git a/Kernel/FileSystem/TmpFS.cpp b/Kernel/FileSystem/TmpFS.cpp index 4c7552f2a2c..e916a339802 100644 --- a/Kernel/FileSystem/TmpFS.cpp +++ b/Kernel/FileSystem/TmpFS.cpp @@ -207,6 +207,10 @@ ssize_t TmpFSInode::write_bytes(off_t offset, ssize_t size, const u8* buffer, Fi ASSERT(!is_directory()); ASSERT(offset >= 0); + auto result = prepare_to_write_data(); + if (result.is_error()) + return result; + off_t old_size = m_metadata.size; off_t new_size = m_metadata.size; if ((offset + size) > new_size) diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index b4a74d3a5fe..08e2253e323 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -511,6 +511,14 @@ KResult VFS::chown(Inode& inode, uid_t a_uid, gid_t a_gid) } dbg() << "VFS::chown(): inode " << inode.identifier() << " <- uid:" << new_uid << " gid:" << new_gid; + + if (metadata.is_setuid() || metadata.is_setgid()) { + dbg() << "VFS::chown(): Stripping SUID/SGID bits from " << inode.identifier(); + auto result = inode.chmod(metadata.mode & ~(04000 | 02000)); + if (result.is_error()) + return result; + } + return inode.chown(new_uid, new_gid); }