mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
This commit is contained in:
1 parent
e4a0f649fa
commit
9f4dcf359b
5 files changed
+44
-10
No files matched your search
@@ -29,6 +29,7 @@ CChunkFileReader::Error StateRingbuffer::Save() {
|
||||
{
|
||||
base_ = (base_ + 1) % ARRAY_SIZE(bases_);
|
||||
baseUsage_ = 0;
|
||||
baseGeneration_[base_] = nextBaseGeneration_++;
|
||||
err = SaveToRam(bases_[base_]);
|
||||
// Let's not bother savestating twice.
|
||||
compressBuffer = &bases_[base_];
|
||||
@@ -42,7 +43,7 @@ CChunkFileReader::Error StateRingbuffer::Save() {
|
||||
states_[n].clear();
|
||||
}
|
||||
|
||||
baseMapping_[n] = base_;
|
||||
baseMapping_[n] = baseGeneration_[base_];
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -59,8 +60,17 @@ CChunkFileReader::Error StateRingbuffer::Restore(std::string *errorString, std::
|
||||
|
||||
auto pa = GetI18NCategory(I18NCat::PAUSE);
|
||||
|
||||
const int generation = baseMapping_[n];
|
||||
const int baseSlot = generation < 0 ? -1 : generation % (int)ARRAY_SIZE(bases_);
|
||||
if (baseSlot < 0 || baseGeneration_[baseSlot] != generation) {
|
||||
// The base this state was compressed against has since been overwritten, so it can't be
|
||||
// decoded any more. Only two bases are kept, but the state ring is longer.
|
||||
WARN_LOG(Log::SaveState, "Rewind: state %d was compressed against a base that's gone", n);
|
||||
return CChunkFileReader::ERROR_BAD_FILE;
|
||||
}
|
||||
|
||||
static std::vector<u8> buffer;
|
||||
LockedDecompress(buffer, states_[n].stateBuffer, bases_[baseMapping_[n]]);
|
||||
LockedDecompress(buffer, states_[n].stateBuffer, bases_[baseSlot]);
|
||||
CChunkFileReader::Error error = LoadFromRam(buffer, errorString);
|
||||
*metadata = pa->T("Rewind");
|
||||
|
||||
@@ -117,7 +127,12 @@ void StateRingbuffer::LockedDecompress(std::vector<u8> &result, const std::vecto
|
||||
for (size_t i = 0; i < compressed.size(); ) {
|
||||
if (compressed[i] == 0) {
|
||||
++i;
|
||||
int blockSize = std::min(BLOCK_SIZE, (int)(base.size() - result.size()));
|
||||
// Bound against what's actually left of the base: the subtraction this used to do
|
||||
// (base.size() - result.size()) wraps once the output is longer than the base.
|
||||
const int blockSize = (int)std::min((size_t)BLOCK_SIZE, (size_t)(base.end() - basePos));
|
||||
if (blockSize <= 0) {
|
||||
break;
|
||||
}
|
||||
result.insert(result.end(), basePos, basePos + blockSize);
|
||||
basePos += blockSize;
|
||||
} else {
|
||||
@@ -145,6 +160,10 @@ void StateRingbuffer::Clear() {
|
||||
for (auto &b : bases_) {
|
||||
b.clear();
|
||||
}
|
||||
for (int &g : baseGeneration_) {
|
||||
g = -1;
|
||||
}
|
||||
nextBaseGeneration_ = 0;
|
||||
baseMapping_.clear();
|
||||
baseMapping_.resize(size_);
|
||||
for (auto &s : states_) {
|
||||
|
||||
Reference in new issue
Block a user