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.
This commit is contained in:
Augustin Cavalier 2020-04-27 22:30:08 -04:00
parent d939cacf6b
commit c1c239fefb
2 changed files with 13 additions and 8 deletions

View File

@ -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;

View File

@ -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<IORequest> {
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);