Reject lengths that do not fit the ioctl length fields
The public ABI takes SIZE_T lengths, but NtDeviceIoControlFile takes
ULONG ones, and every conversion was a bare cast.
pawnio_load_nt cast size with no check at all. pawnio_execute_nt and
pawnio_execute_async_nt bounded in_size against SIZE_MAX, which only
stops allocsize from wrapping size_t, not from exceeding ULONG_MAX --
in_size = 0x20000000 gives allocsize = 0x100000020, so the library
HeapAllocs four gigabytes, RtlMoveMemory copies in_size * 8 bytes out
of the caller's buffer, and then tells the driver the buffer is 32
bytes long. Neither bounded out_size.
pawnio_execute_nt also had (ULONG)out_size * sizeof(*out), where the
cast binds to out_size before the multiply, so the product widens back
to 64 bits and is narrowed again at the call; out_size = 0x20000000
came out as a byte count of zero. The async path at the matching line
already parenthesised it correctly.
Bound all three against ULONG_MAX before anything is allocated or
copied, next to the existing name-length check, and parenthesise the
byte count so both paths read the same. With the guards in place
out_size can no longer reach a value where that product overflows, but
the two call sites should not differ.
Measured by driving the exported entry points with a bogus handle --
every guard runs before the handle is touched -- against this build and
against a build of the previous revision of the same file:
before after
load size=0x100000000 INVALID_HANDLE INVALID_PARAMETER
load size=0x100000004 INVALID_HANDLE INVALID_PARAMETER
exec out_size=0x20000000 INVALID_HANDLE INVALID_PARAMETER
exec in_size=0x20000000 0xC0000005 INVALID_PARAMETER
async out_size=0x20000000 INVALID_HANDLE INVALID_PARAMETER
async in_size=0x20000000 0xC0000005 INVALID_PARAMETER
INVALID_HANDLE in the before column means the truncated length went
through to the syscall and only the bogus handle stopped it. The two
0xC0000005 rows are the four gigabyte allocation succeeding and
RtlMoveMemory then reading off the end of a four-cell input buffer,
which faults in the caller's process. Ordinary calls still reach
STATUS_INVALID_HANDLE on both builds, and the test module still runs
ioctl_test_callback and ioctl_test to success through PawnIOUtil. N
namazso committed
3d3b67d15949a95c7f4a27dde8886e145df4934a
Parent: 9d27806