PowerPC: fix uninitialized read of insn->mnemonic in PPC_post_printer (#3005)
Some checks failed
RELEASE BUILD - PyPI 📦 Distribution / Build wheels on ubuntu-latest (push) Failing after 26s
RELEASE BUILD - PyPI 📦 Distribution / Make SDist (push) Failing after 34s
Run Test / ubuntu-22.04 x64 ASAN (push) Failing after 49s
Python Package CI / build (ubuntu-24.04, 3.12) (push) Has been cancelled
Python Package CI / build (ubuntu-24.04, 3.8) (push) Has been cancelled
Python Package CI / build (windows-2022, 3.12) (push) Has been cancelled
Python Package CI / build (windows-2022, 3.8) (push) Has been cancelled
Cross Build Tests / [BUILD ONLY] Android 35 (arm64_v8a) NDK 29 (push) Has been cancelled
Cross Build Tests / [BUILD ONLY] Windows i686 mingw (push) Has been cancelled
Cross Build Tests / QEMU Linux ARM (push) Has been cancelled
Cross Build Tests / QEMU Linux MIPS 32 BE (push) Has been cancelled
Cross Build Tests / QEMU Linux Mips64el (push) Has been cancelled
Cross Build Tests / QEMU Linux PPC64 (push) Has been cancelled
Cross Build Tests / QEMU Linux s390x (push) Has been cancelled
Run Test / ubuntu-22.04 x64 make (push) Has been cancelled
Run Test / ubuntu-22.04 x64 cmake (push) Has been cancelled
Python Package CI / build (macOS-14, 3.8) (push) Has been cancelled
Python Package CI / build (macOS-14, 3.12) (push) Has been cancelled
Run Test / windows x64 MSVC 64bit (push) Has been cancelled
RELEASE BUILD - PyPI 📦 Distribution / Build wheels on macos-latest (push) Has been cancelled
RELEASE BUILD - PyPI 📦 Distribution / Build wheels on windows-latest (push) Has been cancelled
RELEASE BUILD - PyPI 📦 Distribution / publish (push) Has been cancelled

* arch/PowerPC: fix uninitialized read of insn->mnemonic in PPC_post_printer and add regression tests

Derive the update_cr0 and branch-hint checks from the last char of the
mnemonic token of insn_asm, which always holds the current instruction's
text; also clear insn->mnemonic in fill_insn() before the post printer
runs so no post printer can rely on stale contents again.

* Run test_ppc_iter_detail in the CITest workflow

---------

Co-authored-by: phix33 <122955334+phix33@user.noreply.github.com>
This commit is contained in:
phix33 2026-07-25 01:09:34 +10:00 committed by GitHub
parent 9291d4166b
commit 49e5aef5bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 122 additions and 8 deletions

View file

@ -89,6 +89,12 @@ jobs:
python cstest_report.py -D -t build/cstest -d ../MC;
python cstest_report.py -D -t build/cstest -f issues.cs; cd ..;
- name: test_ppc_iter_detail
shell: 'script -q -e -c "bash -e {0}"'
run: |
export LD_LIBRARY_PATH="${PWD}:${LD_LIBRARY_PATH:-}"
if [ -x build/test_ppc_iter_detail ]; then ./build/test_ppc_iter_detail; else ./tests/test_ppc_iter_detail; fi
Windows:
runs-on: ${{ matrix.config.os }}
name: ${{ matrix.config.name }}

View file

@ -298,7 +298,7 @@ if(CAPSTONE_PPC_SUPPORT)
arch/PowerPC/PPCGenRegisterInfo.inc
arch/PowerPC/PPCGenInstrInfo.inc
)
set(TEST_SOURCES ${TEST_SOURCES} test_ppc.c)
set(TEST_SOURCES ${TEST_SOURCES} test_ppc.c test_ppc_iter_detail.c)
endif()
if(CAPSTONE_X86_SUPPORT)

View file

@ -79,14 +79,16 @@ void PPC_post_printer(csh ud, cs_insn *insn, char *insn_asm, MCInst *mci)
if (((cs_struct *)ud)->detail != CS_OPT_ON)
return;
// check if this insn has branch hint
if (strrchr(insn->mnemonic, '+') != NULL && !strstr(insn_asm, ".+")) {
insn->detail->ppc.bh = PPC_BH_PLUS;
} else if (strrchr(insn->mnemonic, '-') != NULL) {
insn->detail->ppc.bh = PPC_BH_MINUS;
}
// insn->mnemonic is not filled yet; the record-form '.' and the branch
// hint '+'/'-' are always the last char of the mnemonic token of insn_asm
size_t n = strcspn(insn_asm, " \t");
char c = n ? insn_asm[n - 1] : 0;
if (strrchr(insn->mnemonic, '.') != NULL) {
if (c == '+') {
insn->detail->ppc.bh = PPC_BH_PLUS;
} else if (c == '-') {
insn->detail->ppc.bh = PPC_BH_MINUS;
} else if (c == '.') {
insn->detail->ppc.update_cr0 = true;
}
}

3
cs.c
View file

@ -581,6 +581,9 @@ static void fill_insn(struct cs_struct *handle, cs_insn *insn, char *buffer, MCI
// we might skip some redundant bytes in front in the case of X86
memcpy(insn->bytes, code + insn->size - copy_size, copy_size);
insn->op_str[0] = '\0';
// mnemonic is filled below, after the post printer runs: post printers
// must derive any checks from insn_asm, never from insn->mnemonic
insn->mnemonic[0] = '\0';
insn->size = copy_size;
// alias instruction might have ID saved in OpcodePub

View file

@ -1118,3 +1118,15 @@
!# MOVSXD r16, m32 (memory form, 0x66 prefix)
!# CS_ARCH_X86, CS_MODE_64, CS_OPT_DETAIL
0x0: 0x66, 0x63, 0x20 == movsxd sp, dword ptr [rax]
!# issue PPC record form sets Update-CR0
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x7c,0x85,0x32,0x15 == add. r4, r5, r6 ; Update-CR0: True
!# issue PPC branch hint plus
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x40,0xe2,0x00,0x10 == bne+ 0x10 ; Branch hint: 1
!# issue PPC branch hint minus
!# CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, CS_OPT_DETAIL
0x40,0xc2,0x00,0x10 == bne- 0x10 ; Branch hint: 2

View file

@ -84,6 +84,7 @@ endif
ifneq (,$(findstring powerpc,$(CAPSTONE_ARCHS)))
CFLAGS += -DCAPSTONE_HAS_POWERPC
SOURCES += test_ppc.c
SOURCES += test_ppc_iter_detail.c
endif
ifneq (,$(findstring sparc,$(CAPSTONE_ARCHS)))
CFLAGS += -DCAPSTONE_HAS_SPARC

View file

@ -0,0 +1,90 @@
/* Capstone Disassembly Engine */
/* By phix33, 2026 */
// Regression test: update_cr0 and bh (branch hint) must be derived from the
// current instruction only. PPC_post_printer used to read insn->mnemonic,
// which the alias early-return paths in PPC_printInst (mr, slwi/srwi, sldi,
// dcbt/dcbtst, dcbf) never fill before it runs. When one cs_insn buffer is
// reused across cs_disasm_iter() calls - the documented usage pattern - those
// aliases inherited update_cr0/bh from the previously decoded instruction.
#include <stdio.h>
#include <capstone/platform.h>
#include <capstone/capstone.h>
struct step {
const char *code; // 4 bytes, big-endian
const char *asm_text;
bool update_cr0;
ppc_bh bh;
};
// Each alias is decoded right after an instruction that legitimately sets
// update_cr0 or a branch hint, into the same reused cs_insn.
static const struct step steps[] = {
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x29\x0b\x78", "mr r9, r1", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x54\xa4\x18\x38", "slwi r4, r5, 3", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x78\xa4\x1f\x24", "sldi r4, r5, 3", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x04\x2a\x2c", "dcbt r4, r5", false, PPC_BH_INVALID },
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x04\x28\xac", "dcbf r4, r5", false, PPC_BH_INVALID },
// non-alias path after a record form: was already correct
{ "\x7c\x85\x32\x15", "add. r4, r5, r6", true, PPC_BH_INVALID },
{ "\x7c\x85\x32\x14", "add r4, r5, r6", false, PPC_BH_INVALID },
// branch hints must not leak into the aliases either
{ "\x40\xe2\x00\x10", "bne+ 0x1010", false, PPC_BH_PLUS },
{ "\x7c\x29\x0b\x78", "mr r9, r1", false, PPC_BH_INVALID },
{ "\x40\xc2\x00\x10", "bne- 0x1010", false, PPC_BH_MINUS },
{ "\x7c\x04\x2a\x2c", "dcbt r4, r5", false, PPC_BH_INVALID },
};
int main(void)
{
csh handle;
cs_insn *insn;
int i, errors = 0;
if (cs_open(CS_ARCH_PPC, CS_MODE_64 | CS_MODE_BIG_ENDIAN, &handle) != CS_ERR_OK) {
printf("ERROR: Failed to initialize engine!\n");
return 1;
}
cs_option(handle, CS_OPT_DETAIL, CS_OPT_ON);
insn = cs_malloc(handle);
for (i = 0; i < (int)(sizeof(steps) / sizeof(steps[0])); i++) {
const uint8_t *code = (const uint8_t *)steps[i].code;
size_t size = 4;
uint64_t address = 0x1000;
if (!cs_disasm_iter(handle, &code, &size, &address, insn)) {
printf("ERROR: failed to decode '%s'\n", steps[i].asm_text);
errors++;
continue;
}
bool ok = insn->detail->ppc.update_cr0 == steps[i].update_cr0 &&
insn->detail->ppc.bh == steps[i].bh;
printf("%s\t%s: update_cr0=%u (expected %u), bh=%u (expected %u)\n",
ok ? "OK " : "FAIL", steps[i].asm_text,
insn->detail->ppc.update_cr0, steps[i].update_cr0,
insn->detail->ppc.bh, steps[i].bh);
if (!ok)
errors++;
}
cs_free(insn, 1);
cs_close(&handle);
if (errors) {
printf("%d test(s) failed\n", errors);
return 1;
}
printf("all tests passed\n");
return 0;
}