CVE-2021-36979: Out-of-bounds Write (CWE-787)
value: Unicorn Engine 1.0.2 has an out-of-bounds write in tb_flush_armeb (called from cpu_arm_exec_armeb and tcg_cpu_exec_armeb).
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 5.5
Published: 2021-07-20T07:15Z
Repository: https://github.com/unicorn-engine/unicorn
Language: C
Files changed: samples/sample_arm.c
Commit date: 2021-03-30 12:06:26 +0800
Commit message: Add arm ite blocks samples from #853 (#1381)


Diff: diff --git a/samples/sample_arm.c b/samples/sample_arm.c
index 1e142c1..bcaabc8 100644
--- a/samples/sample_arm.c
+++ b/samples/sample_arm.c
@@ -9,10 +9,12 @@
 
 // code to be emulated
 #define ARM_CODE "\x37\x00\xa0\xe3\x03\x10\x42\xe0" // mov r0, #0x37; sub r1, r2, r3
 #define THUMB_CODE "\x83\xb0" // sub    sp, #0xc
 
+#define ARM_THUM_COND_CODE "\x9a\x42\x14\xbf\x68\x22\x4d\x22" // 'cmp r2, r3\nit ne\nmov r2, #0x68\nmov r2, #0x4d'
+
 // memory address where emulation starts
 #define ADDRESS 0x10000
 
 static void hook_block(uc_engine *uc, uint64_t address, uint32_t size, void *user_data)
 {
@@ -128,10 +130,73 @@ static void test_thumb(void)
     printf(">>> SP = 0x%x\n", sp);
 
     uc_close(uc);
 }
 
+static void test_thumb_ite() {
+    uc_engine *uc;
+    uc_err err;
+
+    uint32_t sp = 0x1234;
+    uint32_t r2 = 0, r3 = 1;
+    uint32_t step_r2, step_r3;
+
+    int i, addr=ADDRESS;
+
+    printf("Emulate a THUMB ITE block as a whole or per instruction.\n");
+    err = uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &uc);
+    if (err) {
+        printf("Failed on uc_open() with error returned: %u (%s)\n",
+                err, uc_strerror(err));
+        return;
+    }
+
+    uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);
+
+    uc_mem_write(uc, ADDRESS, ARM_THUM_COND_CODE, sizeof(ARM_THUM_COND_CODE) - 1);
+
+    uc_reg_write(uc, UC_ARM_REG_SP, &sp);
+
+    uc_reg_write(uc, UC_ARM_REG_R2, &r2);
+    uc_reg_write(uc, UC_ARM_REG_R3, &r3);
+
+    // Run once.
+    printf("Running the entire binary.\n");
+    err = uc_emu_start(uc, ADDRESS | 1, ADDRESS + sizeof(ARM_THUM_COND_CODE) - 1, 0, 0);
+    if (err) {
+        printf("Failed on uc_emu_start() with error returned: %u\n", err);
+    }
+    uc_reg_read(uc, UC_ARM_REG_R2, &r2);
+    uc_reg_read(uc, UC_ARM_REG_R3, &r3);
+
+    printf(">>> R2: %d\n", r2);
+    printf(">>> R3: %d\n\n", r3);
+
+    // Step each instruction.
+    printf("Running the binary one instruction at a time.\n");
+    for (i = 0; i < sizeof(ARM_THUM_COND_CODE) / 2; i++) {
+        err = uc_emu_start(uc, addr | 1, ADDRESS + sizeof(ARM_THUM_COND_CODE) - 1, 0, 1);
+        if (err) {
+            printf("Failed on uc_emu_start() with error returned: %u\n", err);
+        }
+        uc_reg_read(uc, UC_ARM_REG_PC, &addr);
+    }
+
+    uc_reg_read(uc, UC_ARM_REG_R2, &step_r2);
+    uc_reg_read(uc, UC_ARM_REG_R3, &step_r3);
+
+    printf(">>> R2: %d\n", step_r2);
+    printf(">>> R3: %d\n\n", step_r3);
+
+    if (step_r2 != r2 || step_r3 != r3) {
+        printf("Failed with ARM ITE blocks stepping!\n");
+    }
+
+    uc_close(uc);
+}
+
+
 int main(int argc, char **argv, char **envp)
 {
     // dynamically load shared library
 #ifdef DYNLOAD
     if (!uc_dyn_load(NULL, 0)) {
@@ -144,11 +209,12 @@ int main(int argc, char **argv, char **envp)
 #endif
     
     test_arm();
     printf("==========================\n");
     test_thumb();
-
+    printf("==========================\n");
+    test_thumb_ite();
     // dynamically free shared library
 #ifdef DYNLOAD
     uc_dyn_free();
 #endif
     

Fixed code: #define ARM_THUM_COND_CODE "\x9a\x42\x14\xbf\x68\x22\x4d\x22" // 'cmp r2, r3\nit ne\nmov r2, #0x68\nmov r2, #0x4d'

static void test_thumb_ite() {
    uc_engine *uc;
    uc_err err;

    uint32_t sp = 0x1234;
    uint32_t r2 = 0, r3 = 1;
    uint32_t step_r2, step_r3;

    int i, addr=ADDRESS;

    printf("Emulate a THUMB ITE block as a whole or per instruction.\n");
    err = uc_open(UC_ARCH_ARM, UC_MODE_THUMB, &uc);
    if (err) {
        printf("Failed on uc_open() with error returned: %u (%s)\n",
                err, uc_strerror(err));
        return;
    }

    uc_mem_map(uc, ADDRESS, 2 * 1024 * 1024, UC_PROT_ALL);

    uc_mem_write(uc, ADDRESS, ARM_THUM_COND_CODE, sizeof(ARM_THUM_COND_CODE) - 1);

    uc_reg_write(uc, UC_ARM_REG_SP, &sp);

    uc_reg_write(uc, UC_ARM_REG_R2, &r2);
    uc_reg_write(uc, UC_ARM_REG_R3, &r3);

    // Run once.
    printf("Running the entire binary.\n");
    err = uc_emu_start(uc, ADDRESS | 1, ADDRESS + sizeof(ARM_THUM_COND_CODE) - 1, 0, 0);
    if (err) {
        printf("Failed on uc_emu_start() with error returned: %u\n", err);
    }
    uc_reg_read(uc, UC_ARM_REG_R2, &r2);
    uc_reg_read(uc, UC_ARM_REG_R3, &r3);

    printf(">>> R2: %d\n", r2);
    printf(">>> R3: %d\n\n", r3);

    // Step each instruction.
    printf("Running the binary one instruction at a time.\n");
    for (i = 0; i < sizeof(ARM_THUM_COND_CODE) / 2; i++) {
        err = uc_emu_start(uc, addr | 1, ADDRESS + sizeof(ARM_THUM_COND_CODE) - 1, 0, 1);
        if (err) {
            printf("Failed on uc_emu_start() with error returned: %u\n", err);
        }
        uc_reg_read(uc, UC_ARM_REG_PC, &addr);
    }

    uc_reg_read(uc, UC_ARM_REG_R2, &step_r2);
    uc_reg_read(uc, UC_ARM_REG_R3, &step_r3);

    printf(">>> R2: %d\n", step_r2);
    printf(">>> R3: %d\n\n", step_r3);

    if (step_r2 != r2 || step_r3 != r3) {
        printf("Failed with ARM ITE blocks stepping!\n");
    }

    uc_close(uc);
}


    printf("==========================\n");
    test_thumb_ite();