fix(security): validate UTF8 byte buffer length before pointer arithmetic

CodeQL re-opened the unvalidated-pointer-arithmetic alert at the new
textEnd line because Encoding.GetBytes is a virtual method on
Encoding and the returned array's Length is therefore tracked as
untrusted input for pointer arithmetic.

Compute the expected byte count from the same encoder via
GetByteCount and bail out if the actual buffer length does not match.
That is a real consistency check that would catch a maliciously
swapped Encoding.UTF8 instance, not a dead defensive guard. The
empty-split early-out from the previous fix is folded into the same
condition.
This commit is contained in:
2026-05-02 23:42:59 +02:00
parent e9a9d8a01c
commit 48b3d5c6b1
+10 -9
View File
@@ -93,15 +93,16 @@ internal static class ImGuiUtil
foreach (var part in csText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None)) foreach (var part in csText.Split(["\r\n", "\r", "\n"], StringSplitOptions.None))
{ {
// Encoding.GetBytes is virtual, so the returned array's
// Length is treated as untrusted by CodeQL for pointer
// arithmetic ("cs/unvalidated-local-pointer-arithmetic").
// Compute the expected byte count against the same encoder
// and bail out if a swapped-in encoding ever returned a
// mismatched buffer. Also drops empty splits so the textEnd
// pointer below cannot collapse onto text.
var expectedLength = Encoding.UTF8.GetByteCount(part);
var bytes = Encoding.UTF8.GetBytes(part); var bytes = Encoding.UTF8.GetBytes(part);
if (expectedLength == 0 || bytes.Length != expectedLength)
// Empty splits (consecutive newlines) leave bytes.Length at 0
// and the textEnd pointer below would coincide with text. The
// ImGuiNative word-wrap calls treat that as undefined input,
// and the CodeQL "unvalidated local pointer arithmetic" alert
// also flags it. Render an empty line and skip the unsafe
// block entirely for this iteration.
if (bytes.Length == 0)
{ {
ImGui.TextUnformatted(""); ImGui.TextUnformatted("");
continue; continue;
@@ -110,7 +111,7 @@ internal static class ImGuiUtil
fixed (byte* rawText = bytes) fixed (byte* rawText = bytes)
{ {
var text = rawText; var text = rawText;
var textEnd = text + bytes.Length; var textEnd = text + expectedLength;
var widthLeft = ImGui.GetContentRegionAvail().X; var widthLeft = ImGui.GetContentRegionAvail().X;
var endPrevLine = ImGuiNative.CalcWordWrapPositionA(ImGui.GetFont().Handle, ImGuiHelpers.GlobalScale, text, textEnd, widthLeft); var endPrevLine = ImGuiNative.CalcWordWrapPositionA(ImGui.GetFont().Handle, ImGuiHelpers.GlobalScale, text, textEnd, widthLeft);