Skip to content

AArch64 implementation of instruction 'ccmp' may be incorrect #1440

@Bri1987

Description

@Bri1987

I wrote this unit test:

#!/usr/bin/env python4

from __future__          import print_function

from triton              import *
from unicorn             import *
from unicorn.arm64_const import *
from struct              import pack

import sys
import pprint

ADDR  = 0x100000
STACK = 0x200000
SIZE  = 5 * 1024 * 1024

CODE  = [
    (b"\xbf\x80\x01\x71", 'cmp w5, #0x60'),
    (b"\xc4\xc8\x40\x7a", 'ccmp w6, #0x0, #0x4, gt'),
]

def emu_with_unicorn(opcode, istate):
    # Initialize emulator in aarch64 mode
    mu = Uc(UC_ARCH_ARM64, UC_MODE_ARM)

    # map memory for this emulation
    mu.mem_map(ADDR, SIZE)

    # write machine code to be emulated to memory
    index = 0
    for op, _ in CODE:
        mu.mem_write(ADDR+index, op)
        index += len(op)

    mu.reg_write(UC_ARM64_REG_X5,   istate['x5'])
    mu.reg_write(UC_ARM64_REG_X6,   istate['x6'])
    mu.reg_write(UC_ARM64_REG_PC,   istate['pc'])
    mu.reg_write(UC_ARM64_REG_NZCV, istate['n'] << 31 | istate['z'] << 30 | istate['c'] << 29 | istate['v'] << 28)

    # emulate code in infinite time & unlimited instructions
    mu.emu_start(istate['pc'], istate['pc'] + len(opcode))

    ostate = {
        "x5":    mu.reg_read(UC_ARM64_REG_X5),
        "x6":    mu.reg_read(UC_ARM64_REG_X6),
        "pc":    mu.reg_read(UC_ARM64_REG_PC),
        "n":   ((mu.reg_read(UC_ARM64_REG_NZCV) >> 31) & 1),
        "z":   ((mu.reg_read(UC_ARM64_REG_NZCV) >> 30) & 1),
        "c":   ((mu.reg_read(UC_ARM64_REG_NZCV) >> 29) & 1),
        "v":   ((mu.reg_read(UC_ARM64_REG_NZCV) >> 28) & 1),
    }
    return ostate

def emu_with_triton(opcode, istate):
    ctx = TritonContext()
    ctx.setArchitecture(ARCH.AARCH64)

    inst = Instruction(opcode)
    inst.setAddress(istate['pc'])

    ctx.setConcreteRegisterValue(ctx.registers.x5,  istate['x5'])
    ctx.setConcreteRegisterValue(ctx.registers.x6,  istate['x6'])
    ctx.setConcreteRegisterValue(ctx.registers.pc,  istate['pc'])
    ctx.setConcreteRegisterValue(ctx.registers.n,   istate['n'])
    ctx.setConcreteRegisterValue(ctx.registers.z,   istate['z'])
    ctx.setConcreteRegisterValue(ctx.registers.c,   istate['c'])
    ctx.setConcreteRegisterValue(ctx.registers.v,   istate['v'])

    ctx.processing(inst)

    #print
    #print inst
    #for x in inst.getSymbolicExpressions():
    #    print x
    #print

    ostate = {
        "x5":    ctx.getSymbolicRegisterValue(ctx.registers.x5),
        "x6":    ctx.getSymbolicRegisterValue(ctx.registers.x6),
        "pc":    ctx.getSymbolicRegisterValue(ctx.registers.pc),
        "n":     ctx.getSymbolicRegisterValue(ctx.registers.n),
        "z":     ctx.getSymbolicRegisterValue(ctx.registers.z),
        "c":     ctx.getSymbolicRegisterValue(ctx.registers.c),
        "v":     ctx.getSymbolicRegisterValue(ctx.registers.v),
    }
    return ostate


def diff_state(state1, state2):
    for k, v in list(state1.items()):
        if (k == 'heap' or k == 'stack') and v != state2[k]:
            print('\t%s: (UC) != (TT)' %(k))
        elif not (k == 'heap' or k == 'stack') and v != state2[k]:
            print('\t%s: %#x (UC) != %#x (TT)' %(k, v, state2[k]))
    return


if __name__ == '__main__':
    # initial state
    state = {
        "x5":    0x41,
        "x6":    1,
        "pc":    ADDR,
        "n":     0,
        "z":     0,
        "c":     0,
        "v":     0,
    }

    for opcode, disassembly in CODE:
        try:
            uc_state = emu_with_unicorn(opcode, state)
            tt_state = emu_with_triton(opcode, state)
        except Exception as e:
            print('[EKO] %s' %(disassembly))
            print('\t%s' %(e))
            sys.exit(-1)

        if uc_state != tt_state:
            print('[KO] %s' %(disassembly))
            diff_state(uc_state, tt_state)
            sys.exit(-1)

        print('[OK] %s' %(disassembly))
        state = tt_state

    sys.exit(0)

and get output result:

[OK] cmp w5, #0x60
[KO] ccmp w6, #0x0, #0x4, gt
        z: 0x1 (UC) != 0x0 (TT)

It seems when the condition is false, Triton fails to update the flags from the provided nzcv operand (which is 4 in this case), and instead performs a comparison or do some other wrong things.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions