SIGN IN SIGN UP

Bound the name-termination scan in the module loader

The only bound on a record's name was "there is a NUL somewhere before
the end of the blob". Name length was uncapped and the record count is
bounded only by (natives - publics) / defsize, so both scale with the
blob and the walk was O(size^2). Six copies of the scan, across the
counting and copying passes for publics, natives and pubvars.

A blob with defsize=8, the record tables filling half of it, and every
record pointing its name at a run whose only NUL sits at size-1 passes
every structural check, so the loader walks it. Measured with a host
harness before the fix: 6.1 s at 1 MiB, 97.8 s at 4 MiB -- sixteen-fold
data, sixteen-fold time, extrapolating to ~26 minutes at 16 MiB. It
runs at PASSIVE_LEVEL in the IRP_MJ_DEVICE_CONTROL dispatch routine, so
the calling thread is stuck inside the driver and the process cannot be
terminated until it returns. The module is rejected in the end; the
time is spent entirely in the counting pass.

Fold the six copies into find_name_end and stop after 64 bytes. pawncc
truncates exported symbols to sEXPMAX = 31 characters, so nothing it
emits comes close, and PawnIO's own get_public/get_native cannot look
up anything longer than 32 anyway.

std::min needs the extra parentheses because the WDK headers define min
as a macro.

Same harness after the fix: both blobs rejected in under a millisecond,
a 63-character name still loads, 64 does not, and pawn/test.amx is
unaffected.
N
namazso committed
4965cd7210983ba563cd94710f837c0bddb82bc8
Parent: 74018cb