Fix three more out-of-bounds writes

GetCurrentDrawAsDebugVertices (GE debugger vertex preview) sized its index scratch
buffer at a fixed 65536 and then ran both expanding steps into it: index generation
turns strips/fans into up to 3 indices per input index, and RunSoftwareTransform can
then expand points/lines/rects into 6 more each. A 30000-vertex triangle strip wrote
~90000 entries. Size the buffer from the count instead.

The Expand{Rectangles,Lines,Points} capacity checks were also off: they compared the
expansion against indsSize but write the expanded indices at inds + vertexCount, so
the input count has to be part of the sum.

ControlMapper::Axis wrote rawAxisValue_[axis.axisId] with no bounds check, one line
below an explicit check on axis.deviceId. axisId comes straight from the device -
Android reports AXIS_GENERIC_13..16 as 44..47, against a 44-entry array - so it wrote
into the neighbouring deviceTimestamps_. NativeAxis had the same unchecked write into
HLEPlugins::PluginDataAxis, where it goes out of the object entirely.

Rewind's LockedDecompress computed its copy-from-base block size as
base.size() - result.size() in size_t and truncated to int, so it went negative once
the output grew past the base, and insert() then ran with last < first. That happens
because a state can outlive the base it was compressed against: there are 20 states
but only 2 bases, rotated every 16 saves. Track a generation per base and refuse to
decode a state whose base is gone, and bound the block size against the base itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
This commit is contained in:
Henrik Rydgård
2026-08-30 22:45:50 +02:00
co-authored by Claude Opus 5
parent e4a0f649fa
commit 9f4dcf359b
5 changed files with 44 additions and 10 deletions
+4 -1
View File
@@ -683,7 +683,10 @@ void ControlMapper::Axis(const AxisInput *axes, size_t count) {
if (deviceIndex < (size_t)DEVICE_ID_COUNT) {
deviceTimestamps_[deviceIndex] = now;
}
rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping
// Same as deviceId above, axisId comes straight from the device and can be out of range.
if ((size_t)axis.axisId < JOYSTICK_AXIS_MAX) {
rawAxisValue_[axis.axisId] = axis.value; // these are only used for co-axis mapping
}
if (axis.value >= 0.0f) {
InputMapping mapping(axis.deviceId, axis.axisId, 1);
InputMapping opposite(axis.deviceId, axis.axisId, -1);