Buffered FileStream incorrectly reads past end of FileStream, causing IOException on raw disk device streams.
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
### Description
**Summary**
When using FileStream on a raw disk device (\\.\PhysicalDriveN) with default buffering, reads near the end of the device can fail with:
`IOException: Incorrect function. (ERROR_INVALID_FUNCTION)`
This happens even though:
- The LBA is valid
- The sector is readable
- The device size reported by .Length is correct
- The same LBA succeeds when buffering is disabled
- The same LBA succeeds when read sequentially
This indicates that BufferedFileStreamStrategy is issuing internal reads past the end of the device when attempting to refill or realign its internal buffer.
This is a correctness bug in the buffering layer, not in the OS or the device.
**Environment**
- .NET 10
- Windows 11
- Using FileStream on \\.\PhysicalDrive1
- Any physical disk (HDD/SSD/NVMe) assuming they all reject out of range reads with `ERROR_INVALID_FUNCTION`.
- No special flags (default buffering enabled)
### Reproduction Steps
**Minimal Repro**
On C# Interactive:
```Csharp
using System;
using System.IO;
var disk = File.OpenRead(@"\\.\PhysicalDrive1");
var buffer = new byte[512];
long length = disk.Length;
long sectorCount = length / 512;
long lastSector = sectorCount - 1;
void ReadLBA(long lba)
{
disk.Position = lba * 512;
disk.Read(buffer, 0, 512);
}
// This loop succeeds for all LBAs, including the last one
for (long lba = lastSector - 200; lba <= lastSector; lba++)
{
ReadLBA(lba);
}
// But isolated calls to the same LBAs fail:
ReadLBA(lastSector - 2); // throws IOException: Incorrect function
ReadLBA(lastSector - 1); // throws
ReadLBA(lastSector); // throws
```
Stack trace:
System.IO.IOException: Incorrect function. : '\\.\PhysicalDrive1'
at System.IO.Strategies.OSFileStreamStrategy.Read(Span`1 buffer)
at System.IO.Strategies.BufferedFileStreamStrategy.ReadSpan(...)
at System.IO.Strategies.BufferedFileStreamStrategy.Read(...)
### Expected behavior
- Reading a valid LBA within the device’s reported size should succeed.
- BufferedFileStreamStrategy should not issue internal reads past the end of the device.
- Behavior should be consistent regardless of access pattern.
### Actual behavior
- Sequential reads succeed all the way to the last sector.
- Isolated reads near the end fail with ERROR_INVALID_FUNCTION.
- Disabling buffering fixes the issue completely:
var disk = new FileStream(
@"\\.\PhysicalDrive1",
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: 0,
FileOptions.None);
### Regression?
Unkown, although a related issue from 2022 suggests raw disk access with FileStream was less stable, and related issues got fixed at the time. This appears to be an undigged remainder of the problematic patterns partially fixed back then.
### Known Workarounds
Disabling buffering fixes the issue completely:
```Csharp
var disk = new FileStream(
@"\\.\PhysicalDrive1",
FileMode.Open,
FileAccess.Read,
FileShare.ReadWrite,
bufferSize: 0,
FileOptions.None);
```
With buffering disabled, all LBAs are readable consistently, including the last one.
### Configuration
- Visual Studio 2026 Community
- .NET 10
- Windows 11 Pro
- x64
- Tested on SATA SSD device.
### Other information
**Root Cause (most likely from the stack trace clues)**
BufferedFileStreamStrategy attempts to:
- refill its internal buffer
- realign the buffer
- or perform read‑ahead
…by issuing a read that extends past the end of the raw device.
Raw disk devices do not behave like regular files:
- Reading past the end does not return 0 bytes
- It returns ERROR_INVALID_FUNCTION
This causes .Read to throw even though the requested 512‑byte region is valid.
**Why this matters**
This makes FileStream unreliable for:
- disk imaging tools
- forensic tools
- partition editors
- backup utilities
- any software that reads raw disks
The failure is non‑deterministic and depends on:
- access pattern
- buffer state
- previous reads
- internal alignment
This is extremely confusing for developers and leads to false assumptions about disk corruption, driver issues, unstable Windows storage stack or the validity of using FileStream for raw disk access.
**Related Issue**
This appears closely related to the closed issue:
- https://github.com/dotnet/runtime/issues/73669
…but that issue focuses on .Length and .ReadAsync.
This report provides:
- a minimal repro
- a confirmed root cause
- a clear explanation of the buffering behavior
- evidence that the underlying device is fine
- a reliable workaround
**Suggested Fix (conceptual)**
- The buffering layer should not assume the stream reaches the end gracefully.
- It should guard ranges before issuing reads on the underlying stream.
- `FileStream.Length` returns a reliable values for different disk types (tested with SATA SSD, USB Flash Drive and SATA HDD) matching the disk size value retrieved with IOCTL, this could be reliably used to prevent out of range reading attempts.
Contributor guide
Assessment
This issue has not been assessed yet.