A bit of context
swift-system is Apple's low-level library that gives Swift idiomatic, typed access to raw system calls: file descriptors, errno, paths and the rest of the POSIX surface. It's the foundation a lot of Swift tooling quietly stands on, so correctness there matters more than the line count suggests.
On Windows there's no real pread/pwrite, so the library emulates them on top of the Win32 API in WindowsSyscallAdapters.swift. That adapter layer is where I ended up spending an afternoon, and where I found a bug that fails in the worst possible way: silently.
The silent bug
POSIX hands you the buffer size as a pointer-sized integer. On 64-bit Windows that's a 64-bit Int. But the Win32 calls underneath, ReadFile and WriteFile, only accept a DWORD, a 32-bit unsigned integer that maxes out at 4,294,967,295 bytes (4 GB).
The adapter converted the incoming size straight to a DWORD with no guard. So the moment a caller asked to read or write more than 4 GB in a single call, the high bits simply fell off the edge:
// nbyte is an Int (64-bit on Windows x64).// ReadFile / WriteFile only take a DWORD (32-bit unsigned) for the count.var bytesRead: DWORD = 0ReadFile(hFile, buffer, DWORD(nbyte), &bytesRead, nil)// ^^^^^^^^^^^^// When nbyte > 4_294_967_295, those high bits had nowhere to go.And here's the part that bothered me: it didn't crash, and it didn't return an error. The call would happily report success for a truncated amount of data. A caller doing a large file copy could think the whole buffer went through while a chunk of it never did. Classic silent data loss, the kind that only shows up in production, on the one machine, with the one huge file.
The fix
The right move here isn't to invent a behaviour. It's to fail loudly and early. If the requested size can't fit in a DWORD, that's an invalid argument, so I set errno to EINVAL and return -1 before we ever touch the Win32 call:
// Sources/System/Internals/WindowsSyscallAdapters.swift let handle: intptr_t = _get_osfhandle(fd) if handle == /* INVALID_HANDLE_VALUE */ -1 { ucrt._set_errno(EBADF); return -1 } +// Windows ReadFile accepts DWORD (32-bit) for buffer size, so validate nbyte doesn't exceed it+if nbyte > Int(DWORD.max) {+ ucrt._set_errno(EINVAL)+ return -1+}+ // NOTE: this is a non-owning handle, do *not* call CloseHandle on it let hFile: HANDLE = HANDLE(bitPattern: handle)!The exact same guard goes into pwrite against WriteFile. It's a tiny change, but it flips the failure mode from "quietly wrong" to "clearly wrong", which is exactly what you want from a systems library. No public API changes, Windows-only, fully backward compatible for any valid buffer size.
Proving it works
A fix for a 4 GB edge case is awkward to test: you really don't want CI allocating four gigabytes of memory just to prove a bounds check. The trick is that the validation reads the buffer's count, not its actual backing store. So I allocate a tiny 1 KB buffer and hand the syscall a pointer whose declared count is DWORD.max + 1:
/// Test that buffer sizes exceeding DWORD.max (4GB) are properly rejectedfunc testBufferSizeLimit() throws { try withTemporaryFilePath(basename: "testBufferSizeLimit") { path in let fd = try FileDescriptor.open( path.appending("test.txt"), .readWrite, options: [.create, .truncate], permissions: .ownerReadWrite ) defer { try? fd.close() } try fd.writeAll("test data".utf8) // A real, small allocation. We only fake the *count*. let buffer = UnsafeMutableRawBufferPointer.allocate(byteCount: 1024, alignment: 1) defer { buffer.deallocate() } // count > DWORD.max (UInt32.max = 4,294,967,295) must return EINVAL let oversizedCount = Int(DWORD.max) + 1 let oversizedBuffer = UnsafeMutableRawBufferPointer( start: buffer.baseAddress, count: oversizedCount ) // pread should fail with EINVAL do { _ = try fd.read(fromAbsoluteOffset: 0, into: oversizedBuffer) XCTFail("Expected EINVAL for buffer size exceeding DWORD.max") } catch let err as Errno { XCTAssertEqual(err, .invalidArgument, "Expected EINVAL, got \(err)") } // pwrite should also fail with EINVAL do { _ = try fd.write(toAbsoluteOffset: 0, UnsafeRawBufferPointer(oversizedBuffer)) XCTFail("Expected EINVAL for buffer size exceeding DWORD.max") } catch let err as Errno { XCTAssertEqual(err, .invalidArgument, "Expected EINVAL, got \(err)") } }}Both read and write are expected to throw Errno.invalidArgument. The test exercises the exact boundary the fix protects, runs in milliseconds, and allocates almost nothing.
Why it matters
On the surface this is four lines of validation. But the value isn't the code. It's the failure mode it removes. Silent truncation is one of the nastiest classes of bug because everything downstream keeps trusting a number that lied. By the time the symptom surfaces, you're debugging corrupted output far away from the actual cause.
Fail-fast turns an invisible correctness bug into an immediate, attributable error. That's almost always the right trade in foundational code, where a quiet mistake gets multiplied across every project that depends on you.
Takeaways
This was my first contribution to merge into an Apple repository, and honestly the most satisfying part wasn't the diff. It was the review conversation around how to ship it. We talked through whether to target a fast bug-fix release branch before settling on landing it in main. Reading how maintainers reason about that kind of thing taught me more than the patch itself.
The lesson I keep coming back to: in systems programming the bugs that hurt the most are the ones that don't make any noise. Hunt for the conversions that can lose information, and make them speak up. If you want the full context, the PR and its discussion are linked below.