dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.2k stars 4.72k forks source link

Convert.ToBase64CharArray throws IndexOutOfRangeException when encoding a zero-length slice of a non-empty source array into the zero-length slice at the end of a destination array #92016

Closed Stevie-O closed 3 months ago

Stevie-O commented 1 year ago

While writing a wrapper for Convert.ToBase64String to support Base64Url-encoding (slightly different character set, no padding), I encountered this bug:

var bytes = new byte[1];
var chars = new char[0];
Convert.ToBase64CharArray(bytes, 0, 0, chars, 0, Base64FormattingOptions.None).Dump(); // throws IndexOutOfRangeException

(The bug also exists in FX -- in fact, I encountered it there first.)

https://github.com/dotnet/runtime/blob/3eddabc26510e0df4bad31aa465177ca7cbbb391/src/libraries/System.Private.CoreLib/src/System/Convert.cs#L2400

The issue is that the referenced line is trying to check for a zero-length conversion, but instead of checking length (the number of input bytes to be converted) it checks inArrayLength (the length of the array that the input bytes come from). As a result, if the source array itself has nonzero length, the check fails and the remainder of the method continues executing.

In such instances, this line of code:

https://github.com/dotnet/runtime/blob/3eddabc26510e0df4bad31aa465177ca7cbbb391/src/libraries/System.Private.CoreLib/src/System/Convert.cs#L2418

tries to take the address of outArray[offsetOut]. If offsetOut == outArray.Length (destination is the zero-length slice at the end of outArray -- which can most easily occur if outArray is the empty array and offsetOut is zero), then outArray[offsetOut] does not exist, and trying to take its address causes IndexOutOfRangeException to be thrown.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-system-runtime See info in area-owners.md if you want to be subscribed.

Issue Details
While writing a wrapper for Convert.ToBase64String to support Base64Url-encoding (slightly different character set, no padding), I encountered this bug: ```cs var bytes = new byte[1]; var chars = new char[0]; Convert.ToBase64CharArray(bytes, 0, 0, chars, 0, Base64FormattingOptions.None).Dump(); // throws IndexOutOfRangeException ``` (The bug also exists in FX -- in fact, I encountered it there first.) https://github.com/dotnet/runtime/blob/3eddabc26510e0df4bad31aa465177ca7cbbb391/src/libraries/System.Private.CoreLib/src/System/Convert.cs#L2400 The issue is that the referenced line is trying to check for a zero-length conversion, but instead of checking `length` (the number of input bytes to be converted) it checks `inArrayLength` (the length of the array that the input bytes come from). As a result, if the source array itself has nonzero length, the check fails and the remainder of the method continues executing. In such instances, this line of code: https://github.com/dotnet/runtime/blob/3eddabc26510e0df4bad31aa465177ca7cbbb391/src/libraries/System.Private.CoreLib/src/System/Convert.cs#L2418 tries to take the address of `outArray[offsetOut]`. If `offsetOut == outArray.Length` (destination is the zero-length slice at the end of `outArray` -- which can most easily occur if `outArray` is the empty array and `offsetOut` is zero), then `outArray[offsetOut]` does not exist, and trying to take its address causes `IndexOutOfRangeException` to be thrown.
Author: Stevie-O
Assignees: -
Labels: `area-System.Runtime`, `untriaged`
Milestone: -