-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Base64Url.DecodeFromChars causes a fatal AccessViolationException (process crash, exit code 0xC0000005)
when the input ReadOnlySpan<char> contains non-ASCII characters with sufficiently high code point values.
The method should return OperationStatus.InvalidData for invalid input, but instead performs an
out-of-bounds memory read in the scalar decode path via Unsafe.Add into the 256-element DecodingMap.
Reproduction Steps
- Create a .NET 8 console app referencing
Microsoft.Bcl.Memory(any version — tested with 9.0.5) - Run the following code:
using System.Buffers;
using System.Buffers.Text;
const string input = "AB\u1000D";
byte[] buffer = new byte[Base64Url.GetMaxDecodedLength(input.Length)];
Base64Url.DecodeFromChars(input, buffer, out _, out _);Expected behavior
DecodeFromChars returns OperationStatus.InvalidData, since \u1000 is not a valid Base64Url character.
Actual behavior
The process crashes with a fatal AccessViolationException:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at System.Buffers.Text.Base64Helper.DecodeFrom[[...Base64UrlDecoderChar...],[System.UInt16...]](...)
at System.Buffers.Text.Base64Url.DecodeFromChars(...)
Regression?
This is not a regression — the bug has existed since Base64Url was first made available for .NET 8
via the Microsoft.Bcl.Memory NuGet package.
Known Workarounds
A safe workaround is to call System.Text.Ascii.IsValid(input) before decoding.
Base64 only uses ASCII characters, so any non-ASCII input is inherently invalid.
Configuration
- Crashes on: .NET 8.0.24, Windows 10 x64,
Microsoft.Bcl.Memory9.0.5 - Works correctly on: .NET 10.0.2 (in-framework
Base64Url— returnsInvalidData) - Not architecture-specific, but only affects the scalar decode path (short inputs or tail processing)
Other information
Root cause
The scalar fallback path in Base64Helper.DecodeFrom calls decoder.DecodeFourElements(src, ref decodingMap).
For the char/ushort decoder, this uses Unsafe.Add(ref decodingMap, (IntPtr)charValue) to index into
the DecodingMap, which is only 256 elements. A char value > 255 (e.g. \u1000 = 4096) reads
4096 bytes past the start of the map, hitting unmapped memory.
Characters in the range 128–255 do not crash (they read adjacent mapped memory that happens to
contain -1 / invalid values), but characters >= ~4096 reliably trigger the access violation.
The SIMD paths (AVX2, SSE, NEON) are not affected because they narrow ushort to byte before the
lookup, which naturally clamps the value to 0–255.
Notes
Base64Url.DecodeFromChars(the 2-parameter overload that throwsFormatException) and
Base64Url.TryDecodeFromCharsare equally affected since they call the same internalDecodeFrommethod.