impalabs space base graphics
Huawei TrustZone TEE_EID Vulnerabilities
This advisory contains information about the following vulnerabilities:

OOB Access in the get_sec_image_zip Function

There is an OOB access in the function get_sec_image_zip:

int get_sec_image_zip(void *ibuf0_addr, unsigned int *ibuf0_20028, unsigned int *ibuf0_20038, void *obuf3_addr) {
    // [...]
    img_buf = g_img_buf;
    // [...]
    for (int x = ibuf0_20028[0]; x <= ibuf0_20028[1]; x++) {
        for (int y = ibuf0_20028[2]; y <= ibuf0_20028[3]; y++) {
            *(uint8_t *)(eid_buf.addr + ...) = *(uint8_t *)(img_buf + x * 0x1E0 + y);
        }
    }
    // [...]
}

g_img_buf is stored in the local variable img_buf which is then used in many loops without ever checking that it has been allocated. If it has indeed not been allocated, the accesses in the loops will be OOB.

We triggered this bug with a proof of concept and obtained the following crash:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1f00000028 (tid: 40) data abort:
[HM] [ERROR][2498]Bad memory access on address: 0x0, fault_code: 0x92000006
[HM]
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_EID] tid=40 is-idle=0 is-curr=0
[HM]     state=BLOCKED@MEMFAULT sched.pol=0 prio=46 queued=1
[HM]     aff[0]=ff
[HM]     flags=1000 smc-switch=0 ca=7253 prefer-ca=7253
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <get_sec_image_zip+0x200/0x360>
[HM] <?>+0x0/0x0
[HM] <proc_sec_zip_cmd>+0x34/0x48
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM]
[HM] [TRACE][1212]pid=48 exit_status=130

OOB Access in the Encap_tlv_for_hash_zip Function

This is another OOB access in the Encap_tlv_for_hash_zip function:

int Encap_tlv_for_hash_zip(int hash, int hash_len, int zip, int zip_len, uint8_t *hash_tlv) {
    // [...]
    *(uint32_t *)(hash_tlv + 0) = 2;
    SLog("%s: Tlv hash type = %u\n", "[Trace]", 2);
    *(uint32_t *)(hash_tlv + 4) = hash_len;
    SLog("%s: Tlv hash_len = %u\n", "[Trace]", hash_len);
    if (memcpy_s(hash_tlv + 8, 0x1FFF8, hash, hash_len)) {
        SLog("%s: memcpy_s err size is %d, len is %d\n", "[Error]", 0x1FFF8, hash_len);
        return -1;
    }
    printHexWithTag("Sm3 hash", hash, hash_len);

    *(uint32_t *)(hash_tlv + hash_len + 8) = 1;
    SLog("%s: Tlv zip type = %u\n", "[Trace]", 1);
    *(uint32_t *)(hash_tlv + hash_len + 0xc) = zip_len;
    SLog("%s: Tlv zip_len = %u\n", "[Trace]", zip_len);
    if (memcpy_s(hash_tlv + hash_len + 0x10, 0x1FFF0 - hash_len, zip, zip_len)) {
        SLog("%s: memcpy_s err size is %d, len is %d\n", "[Error]", 0x1FFF0 - hash_len, zip_len);
        return -1;
    }

    return hash_len + 0x10 + zip_len;
}

The value of hash_len is capped to 0x1FFF8 because of the first memcpy_s. But for hash_len values between 0x1FFF1 and 0x1FFF8, the following behavior can be observed:

  • the write of the TLV type (1) at offset hash_len + 8 can be OOB
  • the write of the TLV length (zip_len) at offset hash_len + 0xC can be OOB
  • the destination of the memcpy_s at offset hash_len + 0x10 can be OOB
  • the size of the memcpy_s (0x1FFF0 - hash_len) can underflow

The memcpy_s function will detect a negative size, so this behavior cannot be exploited. The OOB write of the TLV type and length will happen out-of-bounds, but they don't crash the trustlet/allocator. We suspect this is because the allocation is mmap'ed, and the size (including the metadata) is page aligned, resulting in padding after the allocation.

Nevertheless, by triggering this bug with a proof of concept, we can see the negative size (-4), proving the TLV type and length OOB write accesses:

[TEE_EID-1] [Trace]: ------ TA_InvokeCommandEntryPoint ------
[TEE_EID-1] [Trace]: Recived the commond, id is 9
[TEE_EID-1] [Trace]: Recived the unsec zip message
[TEE_EID-1] [Trace]: Into the proc_unsec_zip_cmd function
[TEE_EID-1] [Trace]: Malloc_eid_buffer, addr = 0x375f010, len = 131072
[TEE_EID-1] [Trace]: Tlv hash type = 2
[TEE_EID-1] [Trace]: Tlv hash_len = 131060
[TEE_EID-1] [Trace]: Tlv zip type = 1
[TEE_EID-1] [Trace]: Tlv zip_len = 4096
[TEE_EID-1] [Error]: memcpy_s err size is -4, len is 4096
[TEE_EID-1] [Error]: Encap_tlv_for_hash_zip failed!
[TEE_EID-1] [Trace]: Free_eid_buffer, addr = 0x375f010

Parameter Pointers Information Leak in the check_xxx_params Functions

There are information leaks in the functions checking the input parameters for each of the commands, including the check_common_params function (at 0x71A8):

unsigned int check_common_params(int paramTypes, TEE_Param *params) {
    // [...]
    if (params[0].memref.size != 4 || !params[0].memref.buffer) {
        SLog("%s: Invalid param[0], size is %u, buffer address is %p\n\n", "[Error]",
             params[0].memref.size, params[0].memref.buffer);
        return 0xFFFF0006;
    }
    // [...]
}

All of these functions print the parameter buffer address when its size is incorrect. This will reveal that the buffer is always mapped at the same address. Below is a log message sample:

[TEE_EID-1] [Trace]: Recived the commond, id is 6
[TEE_EID-1] [Trace]: Recived the id info message
[TEE_EID-1] [Error]: Invalid param[3], size is 2056, buffer address is 0x70004000

Heap Pointers Information Leak in the eid_malloc, eid_free, malloc_eid_buffer and free_eid_buffer Functions

There are information leaks in the allocation and deallocation functions, including eid_malloc, eid_free, malloc_eid_buffer, and free_eid_buffer:

void *eid_malloc(int len, int hint) {
    // [...]
    addr = TEE_Malloc(len, hint);
    SLog("%s: Eid_malloc, addr = %p, len = %u\n", "[Trace]", addr, len);
    return addr;
}
void eid_free(void *addr) {
    SLog("%s: Eid_free, addr = %p\n", "[Trace]", addr);
    TEE_Free(addr);
}
int malloc_eid_buffer(eid_buffer_t *buf, int size) {
    // [...]
    buf->addr = TEE_Malloc(size, 0);
    // [...]
    buf->size = size;
    SLog("%s: Malloc_eid_buffer, addr = %p, len = %u\n", "[Trace]", buf->addr, size);
    return 0;
}
int free_eid_buffer(eid_buffer_t *buf) {
    // [...]
    buf->size = 0;
    SLog("%s: Free_eid_buffer, addr = %p\n", "[Trace]", buf->addr);
    return TEE_Free(buf->addr);
}

These functions print the address of the allocated/freed buffer. This can be useful information when developing a heap exploit. Below is a log message sample:

[TEE_EID-1] [Trace]: Malloc_eid_buffer, addr = 0x375f010, len = 131072
...
[TEE_EID-1] [Trace]: Free_eid_buffer, addr = 0x375f010

Affected Devices

We have verified that the vulnerabilities impacted the following device(s):

  • Kirin 990: P40 Pro (ELS)

Please note that other models might have been affected.

Patch

Name Severity CVE Patch
OOB Access in the Encap_tlv_for_hash_zip Function Critical CVE-2021-40028 January 2022
OOB Access in the get_sec_image_zip Function High CVE-2021-40018 January 2022
Parameter Pointers Information Leak in the check_xxx_params Functions Medium CVE-2021-40021 January 2022
Heap Pointers Information Leak in the eid_malloc, eid_free, malloc_eid_buffer and free_eid_buffer Functions Medium CVE-2021-40025 January 2022

Timeline

  • Nov. 05, 2021 - A vulnerability report is sent to Huawei PSIRT.
  • Nov. 16, 2021 - Huawei PSIRT acknowledges the vulnerability report.
  • Jan. 01, 2022 - Huawei PSIRT states that these issues were fixed in the January 2022 update.
  • From Nov. 30, 2022 to Jul, 19 2023 - We exchange regularly about the release of our advisories.