Harden memory layer: fix char sizing, ref structs, count guards, ReadString advance
Second-review fixes, each covered by a regression test in MemoryHardeningTests: - MarshalCache: special-case char (Size=2; Marshal.SizeOf reports 1/ANSI but the blittable path reads a 2-byte UTF-16 unit). TypeRequiresMarshal now also trips on RuntimeHelpers.IsReferenceOrContainsReferences<T>() so reference-carrying structs route to the marshal path instead of throwing in MemoryMarshal.Read. Document that the MarshalAs scan is top-level only. - MemoryBase.Read<T>(count): reject negative count (ArgumentOutOfRangeException) and guard elementSize*count overflow. Same overflow guard on Write<T>(values). - MemoryBase.ReadString: advance by bytes actually read, not the requested amount, so a partial read no longer skips the unread tail of the window. - ExternalReader: default to a minimal access set (not AllAccess, which over-requests and fails on protected processes); wrap Process.MainModule in try/catch so a bitness-mismatched or protected target yields ImageBase=Zero instead of throwing. - NativeMethods: WaitForSingleObject and CreateRemoteThread's threadId are DWORD (uint), not int — the signatures no longer sign-flip. Deferred: hoisting the identical ExternalReader/InProcessReader byte-IO into MemoryBase (cosmetic; skipped to avoid colliding with concurrent Phase 3 edits). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -76,12 +76,16 @@ public abstract class MemoryBase : IDisposable
|
||||
/// returns fewer bytes than expected.</returns>
|
||||
public T[] Read<T>(IntPtr address, int count, bool isRelative = false) where T : struct
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(count);
|
||||
|
||||
if (isRelative)
|
||||
address = GetAbsolute(address);
|
||||
|
||||
int elementSize = MarshalCache<T>.Size;
|
||||
int totalSize = elementSize * count;
|
||||
byte[] raw = ReadBytes(address, totalSize);
|
||||
long totalSize = (long)elementSize * count;
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(totalSize, int.MaxValue, nameof(count));
|
||||
|
||||
byte[] raw = ReadBytes(address, (int)totalSize);
|
||||
int actualCount = Math.Min(count, raw.Length / elementSize);
|
||||
|
||||
var result = new T[actualCount];
|
||||
@@ -124,7 +128,9 @@ public abstract class MemoryBase : IDisposable
|
||||
return true;
|
||||
|
||||
int elementSize = MarshalCache<T>.Size;
|
||||
int totalSize = elementSize * values.Length;
|
||||
long total = (long)elementSize * values.Length;
|
||||
ArgumentOutOfRangeException.ThrowIfGreaterThan(total, int.MaxValue, nameof(values));
|
||||
int totalSize = (int)total;
|
||||
byte[] raw = new byte[totalSize];
|
||||
|
||||
Span<byte> span = raw;
|
||||
@@ -181,8 +187,10 @@ public abstract class MemoryBase : IDisposable
|
||||
}
|
||||
|
||||
accumulated.Add(chunk);
|
||||
address += take;
|
||||
remaining -= take;
|
||||
// Advance by the bytes actually read, not the amount requested: a partial
|
||||
// read (chunk.Length < take) must not skip the unread tail of the window.
|
||||
address += chunk.Length;
|
||||
remaining -= chunk.Length;
|
||||
}
|
||||
|
||||
int totalLength = 0;
|
||||
|
||||
Reference in New Issue
Block a user