Merge pull request #4880 from David-cmrg/fix/libc-mspace-realloc-usable

fix(libc): keep a moved mspace block's usable bytes in realloc
This commit is contained in:
Dean 2026-10-13 15:15:29 +13:00 committed by GitHub
commit 495bf91a04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 16 deletions

View File

@ -22,7 +22,6 @@ constexpr std::uintptr_t ArenaHeaderBytes = 64;
struct Chunk {
std::uintptr_t end;
bool used;
std::size_t requested;
};
struct Arena {
@ -51,7 +50,7 @@ Arena* Find(void* handle) {
}
void AddFree(Arena& arena, std::uintptr_t start, std::uintptr_t end) {
arena.chunks[start] = {end, false, 0};
arena.chunks[start] = {end, false};
arena.free.emplace(end - start, start);
}
@ -78,7 +77,7 @@ void* Allocate(Arena* arena, std::size_t size, std::size_t alignment) {
if (aligned > start) AddFree(*arena, start, aligned);
const auto finish = aligned + needed;
if (finish < end) AddFree(*arena, finish, end);
arena->chunks[aligned] = {finish, true, size};
arena->chunks[aligned] = {finish, true};
arena->inUse += needed;
arena->peakInUse = std::max(arena->peakInUse, arena->inUse);
return reinterpret_cast<void*>(aligned);
@ -220,10 +219,7 @@ void* APS5_VABI sceLibcMspaceRealloc_nid_postfix(void* handle, void* pointer, st
if (needed < size) { Error(12); return nullptr; }
const auto start = chunk->first;
const auto capacity = chunk->second.end - start;
if (needed <= capacity) {
chunk->second.requested = size;
return pointer;
}
if (needed <= capacity) return pointer;
const auto next = std::next(chunk);
if (next != arena->chunks.end() && !next->second.used && next->first == chunk->second.end && next->second.end - start >= needed) {
const auto nextEnd = next->second.end;
@ -233,13 +229,12 @@ void* APS5_VABI sceLibcMspaceRealloc_nid_postfix(void* handle, void* pointer, st
if (finish < nextEnd) AddFree(*arena, finish, nextEnd);
arena->inUse += needed - capacity;
arena->peakInUse = std::max(arena->peakInUse, arena->inUse);
arena->chunks[start] = {finish, true, size};
arena->chunks[start] = {finish, true};
return pointer;
}
const auto previousSize = chunk->second.requested;
void* result = Allocate(arena, size, Granule);
if (result) {
std::memcpy(result, pointer, previousSize);
std::memcpy(result, pointer, std::min<std::size_t>(capacity, size));
Release(*arena, arena->chunks.find(start));
}
return result;
@ -281,14 +276,11 @@ void* APS5_VABI sceLibcMspaceReallocalign_nid_postfix(void* handle, void* pointe
const auto needed = AlignUp(size, Granule);
if (needed < size) { Error(12); return nullptr; }
const auto start = chunk->first;
if (needed <= chunk->second.end - start && (start & (effective - 1)) == 0) {
chunk->second.requested = size;
return pointer;
}
const auto previousSize = chunk->second.requested;
const auto capacity = chunk->second.end - start;
if (needed <= capacity && (start & (effective - 1)) == 0) return pointer;
void* result = Allocate(arena, size, effective);
if (result) {
std::memcpy(result, pointer, std::min(previousSize, size));
std::memcpy(result, pointer, std::min<std::size_t>(capacity, size));
Release(*arena, arena->chunks.find(start));
}
return result;

View File

@ -79,9 +79,36 @@ static void CheckAdjacentBlocks() {
Require(stats.currentInuseSize == static_cast<std::size_t>(end(after) - static_cast<unsigned char*>(blocks[0])));
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}
static void CheckMovedBlockKeepsUsableBytes() {
alignas(32) static std::array<unsigned char, 65536> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("moved", storage.data(), storage.size(), 0);
Require(arena != nullptr);
const auto fill = [](void* block) {
const auto usable = sceLibcMspaceMallocUsableSize_nid_postfix(block);
Require(usable > 1);
for (std::size_t index = 0; index < usable; ++index) static_cast<unsigned char*>(block)[index] = static_cast<unsigned char>(index + 1);
return usable;
};
const auto kept = [](const void* block, std::size_t usable) {
for (std::size_t index = 0; index < usable; ++index) if (static_cast<const unsigned char*>(block)[index] != static_cast<unsigned char>(index + 1)) return false;
return true;
};
void* block = sceLibcMspaceMalloc_nid_postfix(arena, 1);
const auto usable = fill(block);
Require(sceLibcMspaceMalloc_nid_postfix(arena, 1) != nullptr);
void* moved = sceLibcMspaceRealloc_nid_postfix(arena, block, 4096);
Require(moved != nullptr && moved != block && kept(moved, usable));
void* other = sceLibcMspaceMalloc_nid_postfix(arena, 1);
const auto otherUsable = fill(other);
Require(sceLibcMspaceMalloc_nid_postfix(arena, 1) != nullptr);
void* realigned = sceLibcMspaceReallocalign_nid_postfix(arena, other, 4096, 256);
Require(realigned != nullptr && realigned != other && kept(realigned, otherUsable));
Require(sceLibcMspaceDestroy_nid_postfix(arena) == 0);
}
int main() {
CheckAlignment();
CheckAdjacentBlocks();
CheckMovedBlockKeepsUsableBytes();
alignas(16) std::array<unsigned char, 65536> storage{};
void* arena = sceLibcMspaceCreate_nid_postfix("test", storage.data(), storage.size(), 0);
Require(arena != nullptr);