Guard diagnostic stub builder against argument-count overflow
The x64 frame math (0x20 + 8*stackArgs) and x86 arg buffer size grow with the argument count. An absurdly large count could overflow int and produce a bogus or negative frame. Add a MaxArguments (256) bound checked at the public entry — far above any real calling convention — so the arithmetic stays in range. Add a test asserting the cap is inclusive and count+1 throws. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,13 @@ namespace WhiteMagic.Assembly;
|
||||
/// </remarks>
|
||||
public sealed class StubAssembler : IAssembler
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum number of call arguments a single stub may pass. Far above any real
|
||||
/// calling convention; exists only to keep frame-size and stack-offset math from
|
||||
/// overflowing <see cref="int"/>.
|
||||
/// </summary>
|
||||
public const int MaxArguments = 256;
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] Assemble(string assemblyText, ulong origin = 0)
|
||||
{
|
||||
@@ -51,11 +58,18 @@ public sealed class StubAssembler : IAssembler
|
||||
/// <param name="pointerSize">4 (x86) or 8 (x64).</param>
|
||||
/// <param name="convention">Calling convention (ignored on x64; Windows has a single ABI).</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="pointerSize"/> is not 4 or 8,
|
||||
/// or <paramref name="convention"/> is not known, or the distance between stub and target
|
||||
/// exceeds the E8 rel32 range.</exception>
|
||||
/// or <paramref name="convention"/> is not known, or <paramref name="arguments"/> exceeds
|
||||
/// <see cref="MaxArguments"/>, or the distance between stub and target exceeds the E8 rel32
|
||||
/// range.</exception>
|
||||
public byte[] BuildCallStub(IntPtr stubAddress, IntPtr targetAddress,
|
||||
nuint[] arguments, int pointerSize, CallConvention convention)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(arguments);
|
||||
// Bound the argument count. No real calling convention passes anywhere near this
|
||||
// many; the cap keeps the frame-size and stack-offset arithmetic (0x20 + 8*stackArgs)
|
||||
// well inside int range, so it can never overflow into a bogus/negative frame.
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(arguments.Length, MaxArguments, nameof(arguments));
|
||||
|
||||
var buffer = new List<byte>(96);
|
||||
|
||||
if (pointerSize == 4)
|
||||
|
||||
@@ -444,6 +444,20 @@ public class StubAssemblerTests
|
||||
Create().BuildCallStub((IntPtr)0x10000000, (IntPtr)0x12345678, [], 4, (CallConvention)99));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Too_many_arguments_throws_before_frame_math_overflows()
|
||||
{
|
||||
// Guards the 0x20 + 8*stackArgs frame arithmetic against int overflow.
|
||||
// MaxArguments is honored (accepted) and MaxArguments+1 is rejected.
|
||||
var atCap = new nuint[StubAssembler.MaxArguments];
|
||||
// At the cap the call still builds (cdecl x64), proving the bound is inclusive.
|
||||
_ = Create().BuildCallStub((IntPtr)0x10000000, (IntPtr)0x10001000, atCap, 8, CallConvention.Cdecl);
|
||||
|
||||
var overCap = new nuint[StubAssembler.MaxArguments + 1];
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() =>
|
||||
Create().BuildCallStub((IntPtr)0x10000000, (IntPtr)0x10001000, overCap, 8, CallConvention.Cdecl));
|
||||
}
|
||||
|
||||
// ── No-FASM ─────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user