unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.32k stars 1.31k forks source link

ARM32 Cortex A9 MRRC instruction UC_ERR_INSN_INVALID #1954

Open TobalJackson opened 1 month ago

TobalJackson commented 1 month ago

Hello, I've come across an issue with unicorn treating the following instruction as invalid:

mrrc p15, 0x1, r1, r0, cr15

the arm documentation describes this instruction as reading a coprocessor register into 2 normal registers: https://developer.arm.com/documentation/dui0489/h/arm-and-thumb-instructions/mrc--mrc2--mrrc-and-mrrc2

Below is an example script which demonstrates this behavior:

#!/usr/bin/env python3
from pwn import *
from unicorn import *
from unicorn.arm_const import *

context.arch = 'arm'
context.endian = 'little'

error = asm("mrrc p15, 0x1, r1, r0, cr15")
errbytes = disasm(error)

print(unicorn.__version__)
print(error)
print(errbytes)

mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
# mu.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_A15) #tried with A7, A8, A9, A15, and
# MAX with same result
mu.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_A9)
mu.mem_map(0, 2 * 1024 * 1024)

mu.mem_write(0x1000, error)

def dump_regs(mu):
    regs = {"r0": UC_ARM_REG_R0,
            "r1": UC_ARM_REG_R1,
            "r2": UC_ARM_REG_R2,
            "r3": UC_ARM_REG_R3,
            "r4": UC_ARM_REG_R4,
            "r5": UC_ARM_REG_R5,
            "r6": UC_ARM_REG_R6,
            "r7": UC_ARM_REG_R7,
            "r8": UC_ARM_REG_R8,
            "r9": UC_ARM_REG_R9,
            "r10": UC_ARM_REG_R10,
            "r11": UC_ARM_REG_R11,
            "r12": UC_ARM_REG_R12,
            "sp": UC_ARM_REG_SP,
            "lr": UC_ARM_REG_LR,
            "pc": UC_ARM_REG_PC,
            "CPSR": UC_ARM_REG_CPSR
            }
    regs_out = {}
    for reg in regs:
        regs_out[reg] = mu.reg_read(regs[reg])
    for reg in regs_out:
        print("{}: {}".format(reg, hex(regs_out[reg])))
    return regs_out

try:
    mu.emu_start(0x1000, 0x1010)
except Exception as e:
    print(e)
    regs = dump_regs(mu)
    inst = mu.mem_read(regs['pc'], 4)
    print(inst)

And the output:

-> % ./error.py
2.0.1
b'\x1f\x1fP\xec'
   0:   ec501f1f        mrrc    15, 1, r1, r0, cr15
Invalid instruction (UC_ERR_INSN_INVALID)
r0: 0x0
r1: 0x0
r2: 0x0
r3: 0x0
r4: 0x0
r5: 0x0
r6: 0x0
r7: 0x0
r8: 0x0
r9: 0x0
r10: 0x0
r11: 0x0
r12: 0x0
sp: 0x0
lr: 0x0
pc: 0x1000
CPSR: 0x400001d3
bytearray(b'\x1f\x1fP\xec')

Please let me know if you'd like additional information, Thank you

TobalJackson commented 1 month ago

I just built the dev branch (2.0.2 - c39e80231c439a6b2231fb47ad02c5057c0d68b6) and re-ran, but received the same error.

wtdcode commented 1 month ago

Thanks for the reproduction, I will have a look.

vlasta-labsky commented 1 month ago

I think it is correctly reported as invalid instruction and therefore not a bug. MRRC variant is supposed to be 64bit access to coprocessor that implements 64bit registers. P15 on Cortex-A is "System Control Coprocessor" and all its registers are 32bit wide. Correct instruction for accessing P15 coprocessor is MRC (32bit access) something like this MRC p15, 0, r0, c1, c0, 0

Please refer to Cortex-A documentation https://developer.arm.com/documentation/den0013/d/ARM-Processor-Modes-and-Registers/Registers/Coprocessor-15

TobalJackson commented 1 month ago

I did some digging on this subject, and it seems that ARMv7-A added something calle LPAE (Large Physical Address Extension) to support >4GB of ram. In doing so, it seems like there are one or more 64-bit coprocessor registers that were added:

The Large Physical Address Extension, Virtualization Extensions, and the optional Generic Timer introduce a small number of 64-bit control registers.

From https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/Virtual-Memory-System-Architecture--VMSA-/About-the-control-registers-for-VMSA/About-system-control-register-accesses?lang=en#BEIHGCFD

vlasta-labsky commented 1 month ago

Yes there are 64bit registers with LPAE (TTBR0 TTBR1 and PAR). https://developer.arm.com/documentation/ddi0406/c/System-Level-Architecture/Virtual-Memory-System-Architecture--VMSA-/About-the-control-registers-for-VMSA/Effect-of-the-LPAE-and-Virtualization-Extensions-on-the-system-control-registers?lang=en

If I change the architecture to Cortex-A15 and try for example PAR register mrrc p15, 0, r1, r0, c7 it works as expected.