sceIo: Validate PGD block sizes and dir listing state

The PGD block size comes from the file header (or a savestate), and is
used to size the block buffer and as a divisor and mask, so require a sane
power of two. On load, force the next read to fill the buffer, which isn't
saved. sceIoDread only checks the listing index against the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-09 15:32:06 +02:00
1 parent 864778a11b
commit 9372d4b490
3 files changed
+34 -1

No files matched your search

+20 -1
View File
@@ -275,7 +275,19 @@ public:
if (pgdInfo)
p.DoVoid(pgdInfo, sizeof(PGD_DESC));
if (p.mode == p.MODE_READ && pgdInfo) {
pgdInfo->block_buf = (u8 *)malloc(pgdInfo->block_size * 2);
pgdInfo->block_buf = nullptr;
if (pgd_valid_block_size(pgdInfo->block_size)) {
pgdInfo->block_buf = (u8 *)malloc(pgdInfo->block_size * 2);
}
if (!pgdInfo->block_buf) {
ERROR_LOG(Log::sceIo, "Savestate has an invalid PGD block size");
p.SetError(p.ERROR_FAILURE);
pgd_close(pgdInfo);
pgdInfo = nullptr;
return;
}
// The buffer isn't saved, so make the next read fill it.
pgdInfo->current_block = -1;
}
}
@@ -2510,6 +2522,13 @@ public:
// TODO: Is this the right way for it to wake up?
int count = (int) listing.size();
Do(p, count);
// sceIoDread only checks for index == count.
if (count < 0 || index < 0 || index > count || !p.CheckRead(count)) {
ERROR_LOG(Log::sceIo, "Savestate has an invalid dir listing");
p.SetError(p.ERROR_FAILURE);
index = 0;
return;
}
listing.resize(count);
for (int i = 0; i < count; ++i) {
listing[i].DoState(p);
+9
View File
@@ -678,6 +678,11 @@ PGD_DESC *pgd_open(KirkState *kirk, u8 *pgd_buf, int pgd_flag, u8 *pgd_vkey)
pgd->data_offset = *(u32*)(pgd_buf+0x4c);
memcpy(pgd->dkey, pgd_buf+0x30, 16);
if(!pgd_valid_block_size(pgd->block_size)){
free(pgd);
return NULL;
}
pgd->align_size = (pgd->data_size+15)&~15;
pgd->table_offset = pgd->data_offset+pgd->align_size;
pgd->block_nr = (pgd->align_size+pgd->block_size-1)&~(pgd->block_size-1);
@@ -686,6 +691,10 @@ PGD_DESC *pgd_open(KirkState *kirk, u8 *pgd_buf, int pgd_flag, u8 *pgd_vkey)
pgd->file_offset = 0;
pgd->current_block = -1;
pgd->block_buf = (u8*)malloc(pgd->block_size*2);
if(pgd->block_buf==NULL){
free(pgd);
return NULL;
}
return pgd;
}
+5
View File
@@ -41,6 +41,11 @@ typedef struct {
u32 file_offset;
}PGD_DESC;
// block_size comes from the file (or a savestate). Reads mask with block_size - 1, so it must be a power of two.
static inline int pgd_valid_block_size(u32 block_size) {
return block_size >= 16 && block_size <= 0x100000 && (block_size & (block_size - 1)) == 0;
}
// type:
// 2: use fuse id