cap read+pread POSIX read sizes at Int32.max (#2323)

Cap NonBlockingFileIO reads at Int32.max

Motivation:

We wish to avoid overly large reads resulting in EINVAL signals being
triggered resulting in errors. We workaround the issiue in the
NonBlockingFileIO level to keep the lower levels as simple as possible.

Modifications:

`NonBlockingFileIO` `read0` amends read `byteCount`s to be `Int32.max` if they are larger than that value.

Result:

Large `NonBlockingFileIO` reads no longer result in precondition
failures.
This commit is contained in:
Rick Newton-Rogers 2022-11-28 13:19:12 +00:00 committed by GitHub
parent 597fa409ee
commit 00341c9277
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 14 additions and 2 deletions

View File

@ -435,12 +435,13 @@ public struct NonBlockingFileIO: Sendable {
private func read0(fileHandle: NIOFileHandle,
fromOffset: Int64?, // > 2 GB offset is reasonable on 32-bit systems
byteCount: Int,
byteCount rawByteCount: Int,
allocator: ByteBufferAllocator,
eventLoop: EventLoop) -> EventLoopFuture<ByteBuffer> {
guard byteCount > 0 else {
guard rawByteCount > 0 else {
return eventLoop.makeSucceededFuture(allocator.buffer(capacity: 0))
}
let byteCount = rawByteCount < Int32.max ? rawByteCount : size_t(Int32.max)
var buf = allocator.buffer(capacity: byteCount)
return self.threadPool.runIfActive(eventLoop: eventLoop) { () -> ByteBuffer in

View File

@ -40,6 +40,7 @@ extension NonBlockingFileIOTest {
("testReadingDifferentChunkSize", testReadingDifferentChunkSize),
("testReadDoesNotReadShort", testReadDoesNotReadShort),
("testChunkReadingWhereByteCountIsNotAChunkSizeMultiplier", testChunkReadingWhereByteCountIsNotAChunkSizeMultiplier),
("testReadMoreThanIntMaxBytesDoesntThrow", testReadMoreThanIntMaxBytesDoesntThrow),
("testChunkedReadDoesNotReadShort", testChunkedReadDoesNotReadShort),
("testChunkSizeMoreThanTotal", testChunkSizeMoreThanTotal),
("testFileRegionReadFromPipeFails", testFileRegionReadFromPipeFails),

View File

@ -282,6 +282,16 @@ class NonBlockingFileIOTest: XCTestCase {
XCTAssertEqual(2, numCalls)
}
func testReadMoreThanIntMaxBytesDoesntThrow() throws {
try XCTSkipIf(MemoryLayout<size_t>.size == MemoryLayout<UInt32>.size)
// here we try to read way more data back from the file than it contains but it serves the purpose
// even on a small file the OS will return EINVAL if you try to read > INT_MAX bytes
try withTemporaryFile(content: "some-dummy-content", { (filehandle, path) -> Void in
let content = try self.fileIO.read(fileHandle: filehandle, byteCount:Int(Int32.max)+10, allocator: .init(), eventLoop: self.eventLoop).wait()
XCTAssertEqual(String(buffer: content), "some-dummy-content")
})
}
func testChunkedReadDoesNotReadShort() throws {
var innerError: Error? = nil
try withPipe { readFH, writeFH in