diff options
author | Sahan Fernando <sahan.h.fernando@gmail.com> | 2021-05-18 21:49:34 +1000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-18 16:33:15 +0200 |
commit | d0f314b23cbf430848010669d9ccd92f2aee5879 (patch) | |
tree | 3130a0115d30a6877129ed8eac6d739d58bcdbc8 /Kernel/Syscalls/write.cpp | |
parent | 208cfcb0a5634c76efc6129fd78edfd37fe846b0 (diff) | |
download | serenity-d0f314b23cbf430848010669d9ccd92f2aee5879.zip |
Kernel: Fix subtle race condition in sys$write implementation
There is a slight race condition in our implementation of write().
We call File::can_write() before attempting to write to it (blocking if
it returns false). If it returns true, we assume that we can write to
the file, and our code assumes that File::write() cannot possibly fail
by being blocked. There is, however, the rare case where another process
writes to the file and prevents further writes in between the call to
Files::can_write() and File::write() in the first process. This would
result in the first process calling File::write() when it cannot be
written to.
We fix this by adding a mechanism for File::can_write() to signal that
it was blocked, making it the responsibilty of File::write() to check
whether it can write and then finally making sys$write() check if the
write failed due to it being blocked.
Diffstat (limited to 'Kernel/Syscalls/write.cpp')
-rw-r--r-- | Kernel/Syscalls/write.cpp | 20 |
1 files changed, 9 insertions, 11 deletions
diff --git a/Kernel/Syscalls/write.cpp b/Kernel/Syscalls/write.cpp index c0185f9b11..0ba686ca48 100644 --- a/Kernel/Syscalls/write.cpp +++ b/Kernel/Syscalls/write.cpp @@ -60,10 +60,6 @@ KResultOr<ssize_t> Process::sys$writev(int fd, Userspace<const struct iovec*> io KResultOr<ssize_t> Process::do_write(FileDescription& description, const UserOrKernelBuffer& data, size_t data_size) { ssize_t total_nwritten = 0; - if (!description.is_blocking()) { - if (!description.can_write()) - return EAGAIN; - } if (description.should_append() && description.file().is_seekable()) { auto seek_result = description.seek(0, SEEK_END); @@ -72,11 +68,12 @@ KResultOr<ssize_t> Process::do_write(FileDescription& description, const UserOrK } while ((size_t)total_nwritten < data_size) { - if (!description.can_write()) { + while (!description.can_write()) { if (!description.is_blocking()) { - // Short write: We can no longer write to this non-blocking description. - VERIFY(total_nwritten > 0); - return total_nwritten; + if (total_nwritten > 0) + return total_nwritten; + else + return EAGAIN; } auto unblock_flags = Thread::FileBlocker::BlockFlags::None; if (Thread::current()->block<Thread::WriteBlocker>({}, description, unblock_flags).was_interrupted()) { @@ -87,12 +84,13 @@ KResultOr<ssize_t> Process::do_write(FileDescription& description, const UserOrK } auto nwritten_or_error = description.write(data.offset(total_nwritten), data_size - total_nwritten); if (nwritten_or_error.is_error()) { - if (total_nwritten) + if (total_nwritten > 0) return total_nwritten; + if (nwritten_or_error.error() == EAGAIN) + continue; return nwritten_or_error.error(); } - if (nwritten_or_error.value() == 0) - break; + VERIFY(nwritten_or_error.value() > 0); total_nwritten += nwritten_or_error.value(); } return total_nwritten; |