mirror of
https://github.com/capstone-engine/capstone
synced 2026-08-11 16:26:07 -04:00
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
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:
parent
9291d4166b
commit
49e5aef5bc
7 changed files with 122 additions and 8 deletions
6
.github/workflows/CITest.yml
vendored
6
.github/workflows/CITest.yml
vendored
|
|
@ -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 }}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
3
cs.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
90
tests/test_ppc_iter_detail.c
Normal file
90
tests/test_ppc_iter_detail.c
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue