From c1c239fefb6198f014b0d62c9de459fee3f509dc Mon Sep 17 00:00:00 2001 From: Augustin Cavalier Date: Mon, 27 Apr 2020 22:30:08 -0400 Subject: [PATCH] kernel: Add mechanism in IOBuffer & IORequest to clamp the last iovec. This is needed by CreateSubRequest, which creates a sub-request using some subset of the passed IO vectors. In the case that the last vector will not be fully used, we need to clamp its size in the sub-request to the remaining length. do_iterative_fd_io, used by vfs_read_pages (which is in turn used by the file cache) used this to split up requests into their constituent block-run requests. So, previously, the invalid IO requests created by this could, under one possible interpretation, overwrite valid file data and cause disk corruption. It is slightly unfortunate that generic_size_t has no unsigned equivalent, so we are left with 0 as the magic number here, instead of -1. However, the passed "length" remains unchanged, so any callers that pass the wrong value for lastVecSize will be trapped by the assert added in the previous commit Fixes #15912 (the assert added in the previous commit), and potentially disk corruption caused by this. --- src/system/kernel/device_manager/IORequest.cpp | 17 ++++++++++------- src/system/kernel/device_manager/IORequest.h | 4 +++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/system/kernel/device_manager/IORequest.cpp b/src/system/kernel/device_manager/IORequest.cpp index f638979af7..9c38a0a104 100644 --- a/src/system/kernel/device_manager/IORequest.cpp +++ b/src/system/kernel/device_manager/IORequest.cpp @@ -112,8 +112,8 @@ IOBuffer::Delete() void -IOBuffer::SetVecs(generic_size_t firstVecOffset, const generic_io_vec* vecs, - uint32 count, generic_size_t length, uint32 flags) +IOBuffer::SetVecs(generic_size_t firstVecOffset, generic_size_t lastVecSize, + const generic_io_vec* vecs, uint32 count, generic_size_t length, uint32 flags) { memcpy(fVecs, vecs, sizeof(generic_io_vec) * count); @@ -121,6 +121,8 @@ IOBuffer::SetVecs(generic_size_t firstVecOffset, const generic_io_vec* vecs, fVecs[0].base += firstVecOffset; fVecs[0].length -= firstVecOffset; } + if (lastVecSize > 0) + fVecs[count - 1].length = lastVecSize; fVecCount = count; fLength = length; @@ -750,8 +752,8 @@ IORequest::Init(off_t offset, generic_addr_t buffer, generic_size_t length, status_t IORequest::Init(off_t offset, generic_size_t firstVecOffset, - const generic_io_vec* vecs, size_t count, generic_size_t length, bool write, - uint32 flags) + generic_size_t lastVecSize, const generic_io_vec* vecs, size_t count, + generic_size_t length, bool write, uint32 flags) { ASSERT(offset >= 0); @@ -759,7 +761,7 @@ IORequest::Init(off_t offset, generic_size_t firstVecOffset, if (fBuffer == NULL) return B_NO_MEMORY; - fBuffer->SetVecs(firstVecOffset, vecs, count, length, flags); + fBuffer->SetVecs(firstVecOffset, lastVecSize, vecs, count, length, flags); fOwner = NULL; fOffset = offset; @@ -825,8 +827,9 @@ IORequest::CreateSubRequest(off_t parentOffset, off_t offset, if (subRequest == NULL) return B_NO_MEMORY; - status_t error = subRequest->Init(offset, vecOffset, vecs + startVec, - endVec - startVec + 1, length, fIsWrite, fFlags & ~B_DELETE_IO_REQUEST); + status_t error = subRequest->Init(offset, vecOffset, remainingLength, + vecs + startVec, endVec - startVec + 1, length, fIsWrite, + fFlags & ~B_DELETE_IO_REQUEST); if (error != B_OK) { delete subRequest; return error; diff --git a/src/system/kernel/device_manager/IORequest.h b/src/system/kernel/device_manager/IORequest.h index 4601f7a70b..1e754b8c12 100644 --- a/src/system/kernel/device_manager/IORequest.h +++ b/src/system/kernel/device_manager/IORequest.h @@ -40,6 +40,7 @@ public: bool IsUser() const { return fUser; } void SetVecs(generic_size_t firstVecOffset, + generic_size_t lastVecSize, const generic_io_vec* vecs, uint32 count, generic_size_t length, uint32 flags); @@ -219,10 +220,11 @@ struct IORequest : IORequestChunk, DoublyLinkedListLinkImpl { status_t Init(off_t offset, const generic_io_vec* vecs, size_t count, generic_size_t length, bool write, uint32 flags) - { return Init(offset, 0, vecs, count, + { return Init(offset, 0, 0, vecs, count, length, write, flags); } status_t Init(off_t offset, generic_size_t firstVecOffset, + generic_size_t lastVecSize, const generic_io_vec* vecs, size_t count, generic_size_t length, bool write, uint32 flags);