Skip to content

Commit

Permalink
Add ctl tests
Browse files Browse the repository at this point in the history
  • Loading branch information
elicn committed Oct 10, 2024
1 parent 4c2b30e commit d124260
Showing 1 changed file with 177 additions and 0 deletions.
177 changes: 177 additions & 0 deletions tests/regress/core_ctl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#!/usr/bin/env python

import regress

from unicorn import *
from unicorn.arm_const import *
from unicorn.x86_const import *


# count down from maxint to zero
_VALID_CODE = (
b'\x31\xc9' # xor ecx, ecx
b'\x49' # _top: dec ecx
b'\xf3\x90' # pause
b'\xe3\xfb' # jecxz _top
b'\xf4' # _end: hlt
)

_INVALID_CODE = (
b'\xff\xff' # (invalid)
)

CODE = _VALID_CODE + _INVALID_CODE

BASE = 0x100000


class TestCtl(regress.RegressTest):

def test_mode(self):
uc = Uc(UC_ARCH_ARM, UC_MODE_BIG_ENDIAN)

# changing cpu model to one that supports EB
uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_M0)

# changing cpu model to one that does not support EB; this is expected to fail
with self.assertRaises(UcError) as ex:
uc.ctl_set_cpu_model(UC_CPU_ARM_CORTEX_A8)

self.assertEqual(UC_ERR_ARG, ex.exception.errno)

# make sure we stay with the configuration we set beforehand
self.assertEqual(UC_ARCH_ARM, uc.ctl_get_arch())
self.assertEqual(UC_MODE_BIG_ENDIAN, uc.ctl_get_mode())
self.assertEqual(UC_CPU_ARM_CORTEX_M0, uc.ctl_get_cpu_model())

def test_page_size(self):
SIZE_4KB = 4 * 1024 ** 1
SIZE_2MB = 2 * 1024 ** 2

uc = Uc(UC_ARCH_ARM, UC_MODE_ARM)

# set page size to 2 MiB; this should work
uc.ctl_set_page_size(SIZE_2MB)

# BUG! was it set properly?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())

# set a page size which is not a power of 2
with self.assertRaises(UcError) as ex:
uc.ctl_set_page_size(SIZE_2MB + 0xbad)

self.assertEqual(UC_ERR_ARG, ex.exception.errno)

# BUG! are we still with the valid value?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())

# force uc to complete its initialization by triggering a random api
uc.ctl_flush_tb()

# set a page size after uc has completed its initialization
with self.assertRaises(UcError) as ex:
uc.ctl_set_page_size(SIZE_4KB)

self.assertEqual(UC_ERR_ARG, ex.exception.errno)

# BUG! are we still with the valid value?
# self.assertEqual(SIZE_2MB, uc.ctl_get_page_size())

def test_timeout(self):
MILLIS_1S = 1000

uc = Uc(UC_ARCH_X86, UC_MODE_32)

uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)

# start a long emulation bound by timeout
uc.emu_start(BASE, BASE + len(CODE), timeout=MILLIS_1S)

# was timeout set properly? uc returns timeout in nanoseconds
self.assertEqual(MILLIS_1S * 1000, uc.ctl_get_timeout())

# no way we made it all the way down to zero
self.assertNotEqual(0, uc.reg_read(UC_X86_REG_ECX))

def test_exits(self):
WRONG_EXIT = BASE + len(CODE)
GOOD_EXIT = BASE + len(_VALID_CODE)

uc = Uc(UC_ARCH_X86, UC_MODE_32)

uc.mem_map(BASE, 0x1000)
uc.mem_write(BASE, CODE)

def __hook_code(uc, *args):
ecx = uc.reg_read(UC_X86_REG_ECX)

# 16 down to the count
if ecx == 0xfffffff0:
# fast-forward the counter and let it run for another 16 iterations
uc.reg_write(UC_X86_REG_ECX, 0x10)

uc.hook_add(UC_HOOK_CODE, __hook_code)

# enable exits
uc.ctl_exits_enabled(True)

# fix the exit point so we don't reach invalid code
uc.ctl_set_exits([GOOD_EXIT])

# start emulation, setting a wrong stopping point that will get us to invalid code
# since we enabled exits, the stopping point should be ignored
uc.emu_start(BASE, WRONG_EXIT, count=1)

# only one exit point was set
self.assertEqual(1, uc.ctl_get_exits_cnt())

# only one exit point, and it is the wrong one
self.assertSequenceEqual([GOOD_EXIT], uc.ctl_get_exits(), int)

# resume execution; rely on code hook to fast-forward the emulation
# stopping point is ignored due to enabled exits
uc.emu_start(BASE, WRONG_EXIT)

# did we exit on the right place?
# not failing on an invalid instruction is another good indication for that
self.assertEqual(GOOD_EXIT, uc.reg_read(UC_X86_REG_EIP))

def test_tlb_mode(self):
MAPPING_LO = 0x0000000001000000
MAPPING_HI = 0x0010000000000000
NOPSLED = b'\x90' * 8

uc = Uc(UC_ARCH_X86, UC_MODE_64)

uc.mem_map(MAPPING_LO, 0x1000)
uc.mem_write(MAPPING_LO, NOPSLED)

uc.emu_start(MAPPING_LO, MAPPING_LO + len(NOPSLED))

# # we should be fine here
# uc.emu_start(BASE, BASE + len(_VALID_CODE), count=1)

uc.mem_map(MAPPING_HI, 0x1000)
uc.mem_write(MAPPING_HI, NOPSLED)

# this should prevents us from mapping to high addresses
uc.ctl_tlb_mode(UC_TLB_CPU)

# this should fail
with self.assertRaises(UcError) as ex:
uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED))

self.assertEqual(UC_ERR_FETCH_UNMAPPED, ex.exception.errno)

# ------------------------------------------------------

# this should allow us mapping to high addresses
uc.ctl_tlb_mode(UC_TLB_VIRTUAL)

# this should ok now
uc.emu_start(MAPPING_HI, MAPPING_HI + len(NOPSLED))


if __name__ == '__main__':
regress.main()

0 comments on commit d124260

Please sign in to comment.