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

Integer Overflow in FR_TA_CoAuthSignImg

There is an integer overflow in the FR_TA_CoAuthSignImg function that leads to a heap buffer overflow.

int GetCameraCtrlRemoteData(void **data_buf_p, uint32_t *data_len_p, TEE_Param *params) {
    // ...
    *data_len_p = params[2].memref.size + 0x18;
    *data_buf_p = TEE_Malloc(*data_len_p, 0);
    memcpy_s(*data_buf_p, *data_len_p, params[3].memref.buffer, 0x18);
    memcpy_s(*data_buf_p + 0x18, *data_len_p - 0x18, params[2].memref.buffer, params[2].memref.size);
    // ...
}

In the call to GetCameraCtrlRemoteData, a buffer that we named data_buf of size 0x18 + size of params[2] is allocated from the heap, and the first 0x18 bytes of params[3] and all the bytes of params[2] are copied into it.

int FR_TA_CoAuthSignImg(int paramTypes, TEE_Param *params, int nb_params) {
    // ...
    GetCameraCtrlRemoteData(&data_buf, &data_buf_len, params);
    // ...
    imagesLen = *((uint32_t *)data_buf + 8);
    size = *((uint32_t *)data_buf + 0x12);
    extraLen = *((uint32_t *)data_buf + 9);
    // ...
    if (imagesLen != 1 || extraLen + size + 0x88 != data_buf_len) { /* ... */ }
    // ...
    if (paramTypes >> 4 == 8) {
        phys_addr = params[1].memref.buffer;
        phys_size = params[1].memref.size;
    } else {
        phys_size = params[1].value.b;
        _fr_get_static_phy_addr(&phys_addr, 0, params[1].value.a, phys_size);
    }
    // ...
    offset = params[0].value.a;
    if (HIDWORD(phys_addr) == 0xFFFFFFFF && phys_addr > ~offset) { /* ... */ }
    phys_addr += offset;
    // ...
    virt_addr = 0;
    MapVirtAddr(phys_addr, phys_size, &virt_addr, 0);
    memcpy_s(data_buf + 0x22, size, virt_addr, phys_size);
    // ...
}

Then 3 dword values, imagesLen, size and extraLen are extracted from data_buf, and their values are somewhat verified. However, in the second check extraLen + size + 0x88 != data_buf_len, there is a possible integer overflow when adding extraLen and size. Thus, it is possible to make size have any value by carefully choosing the value of extraLen.

Next, a physical address is obtained from the params (specified directly or using an ION buffer), and an arbitrary offset is added to it. This physical address range is mapped into the TA. Finally, its content is copied into the heap-allocated buffer. Since the source address and size are user-controlled, and the destination size can also be controlled, this results in a fully-controlled heap-buffer overflow.

In our proof of concept code, we overflow the object by 8 bytes, resulting in the corrupted footer being detected when the object is freed. Nonetheless, we can also trigger bigger overflow, and make use of classic heap exploitation techniques such as the heap unlinking, to gain a write primitive.

[HM] ERROR: free: corrupted footer

Unverified Param Types in FI_onExec

The TEE parameter types are unverified when reaching all the command handlers in FI_onExec. While the command handlers seem to be expecting an output value in params[0], an input buffer in params[1], and an output buffer in params[2], this is never actually enforced anywhere (in particular not in the TA_InvokeCommandEntryPoint and FI_onExec functions).

TEE_Result TA_InvokeCommandEntryPoint(
        void *sessionContext,
        uint32_t commandID,
        uint32_t paramTypes,
        TEE_Param params[4]) {
    // ...
    if ((commandID & 0x80000) != 0) {
        algo_api = GetAlgoApi();
        // ...
        params[0].value.b = algo_api->FI_onExec(
                commandID,
                params[1].memref.buffer,
                params[1].memref.size,
                params[2].memref.buffer,
                params[2].memref.size);
        // ...
    }
    // ...
}
int FI_onExec(
        unsigned int commandID,
        void *buffer1,
        unsigned int length1,
        void *buffer2,
        unsigned int length2,
        uint32_t paramTypes) {
    // ...
    switch (commandID) {
        case 0x80001:
            FaceIdCore::set_log_level(g_faceid_core, *buffer1);
            return 0;
        case 0x80002:
            image_info.field_0 = 0;
            FaceIdCore::get_version(g_faceid_core, &image_info.field_0);
            *(uint32_t *)buffer2 = image_info.field_0;
            return 0;
    }
    // ...
}

As a result, it is very easy to obtain arbitrary memory read and write by passing values where buffers are expected.

For example, one can pass a value for params[1] and call the command 0x80001, resulting in the value at an arbitrary address being set as the log level. This is demonstrated using a first proof of concept code that results in an invalid memory read at address 0xdeadbeef:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort: 
[HM] [ERROR][2498]Bad memory access on address: 0xdeadbeef, fault_code: 0x92000005
[HM] 
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_SERVICE_FAC] 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=7827 prefer-ca=7827
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_Z9FI_onExecjPvjS_j+0x8c/0x9e4>
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM] 

For example, one can also pass a value for params[2] can call the command 0x80002, resulting in the version being written to an arbitrary address. This is demonstrated using a second proof of concept code that results in an invalid memory write at address 0xdeadbeef:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000028 (tid: 40) data abort: 
[HM] [ERROR][2498]Bad memory access on address: 0xdeadbeef, fault_code: 0x92000045
[HM] 
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_SERVICE_FAC] 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=7895 prefer-ca=7895
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_Z9FI_onExecjPvjS_j+0xcc/0x9e4>
[HM] <_Z9FI_onExecjPvjS_j>+0xc4/0x9e4
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM] 

OOB Write in HiAiManager::loadModelFromBuffers

There is an OOB write in the HiAiManager::loadModelFromBuffers that allows writing user-controlled data, of user-controller size to a user-controller address. This vulnerability can be triggered using two commands which are detailed in the next sections.

Dynamic Initialization

The first command to execute is DynamicInit.

Its handler function HandleDynamicInitCommand calls FR_TA_DynamicInit, that does the following:

  • it calls FaceIonCheck, that extracts (among other things) isFinish and configSize from the params[3] input buffer;
  • it calls FaceIonGet, that copies the physical address and size of the param ION buffers into g_ion_buffers (and g_ion_count);
  • it calls FaceIonMap, that maps the physical address ranges into the virtual address space;
  • it calls FI_dynamicInit with the ION buffers and the config (in the params[3] input buffer).
int HandleDynamicInitCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    FR_TA_DynamicInit(params, paramTypes);
}
int FR_TA_DynamicInit(TEE_Param *params, int paramTypes) {
    // ...
    FaceIonCheck(params[3].memref.buffer, params[3].memref.size, &isFinish, &configSize, g_ion_count);
    // ...
    FaceIonGet(params, paramTypes, g_ion_buffers, &g_ion_count);
    // ...
    if (!isFinish)
        return 0;
    // ...
    FaceIonMap(g_ion_buffers, g_ion_count, 0, &ion_count);
    // ...
    g_algo_api->FI_dynamicInit(
            g_ion_buffers,
            g_ion_count,
            params[3].memref.buffer + 0x10,
            configSize);
    // ...
}

The function FI_dynamicInit simply calls FaceIdCore::init, that does the following (among other things):

  • it saves the config into a class member;
  • it subdivides the first ION buffer of ionBuffers into the subbuffers array of buffers:
    • the number of subbuffers comes from config.field_4C (user-controlled);
    • the size of each of the subbuffers comes from config.field_24 (also user-controlled);
  • it initializes the MemoryManager with the first buffer of subbuffers;
  • it initializes the NpuRunMem with the second buffer;
  • it initializes the NpuLoadModelMem with the third buffer;
  • it initializes the CpuModelMem with the forth buffer.
int FI_dynamicInit(FIIonBuf *ionBuffers, int ionCount, void *config, uint configSize) {
    // ...
    FaceIdCore::init(g_faceid_core, ionBuffers, ionCount, config, configSize);
}
int FaceIdCore::init(FaceIdCore *this, FIIonBuf *ionBuffers, int ionCount, void *config, uint configSize) {
    // ...
    memcpy(&this->config, config, sizeof(this->config));
    // ...
    memset(subbuffers, 0, sizeof(subbuffers));
    offset = 0;
    for (int i = 0; i < this->config.field_4C; i++) {
        subbuffers[i].vaddr = ionBuffers[0].vaddr + offset;
        subbuffers[i].paddr = ionBuffers[0].paddr + offset;
        subbuffers[i].size = this->config.field_24[i];
        offset += subbuffers[i].size;
    }
    // ...
    MemoryManager::initMemory(&this->memory_manager, &subbuffers[0], 1);
    // ...
    HiAiManager = HiAiManager::getHiAiManager();
    HiAiManager::setSysFunc(HiAiManager, &this->sysfuncs, &this->memory_manager, this->config.field_14);
    HiAiManager::setNpuRunMem(HiAiManager, &subbuffers[1]);
    HiAiManager::setNpuLoadModelMem(HiAiManager, &subbuffers[2]);
    HiAiManager::setCpuModelMem(HiAiManager, &subbuffers[3]);
    // ...
}

Because the size of the sub-buffers is never checked before it is added, by specifying a bogus size for a buffer i, the next buffer i+1 can be made to have a paddr/vaddr at a chosen offset from the first ION buffer of ionBuffers.

Thus, HiAiManager::setNpuLoadModelMem can be called with a buffer having a virtual address located anywhere in memory (if we know the virtual address of the original buffer). This function sets (among other things) the field this->npuLoadMem to our chosen values.

int HiAiManager::setNpuLoadModelMem(HiAiManager *this, FIIonBuf *buffer) {
    // ...
    this->npuLoadMem.vaddr = buffer->vaddr;
    this->npuLoadMem.paddr = buffer->paddr;
    this->npuLoadMem.size = buffer->size;
    return result;
}

Load Model

The first command to execute is LoadModel.

Its handler function HandleLoadModelCommand calls FR_TA_LoadModel. It does about the same things as FR_TA_DynamicInit - mapping the ION buffers, and calls the FI_loadModel function and with only the ION buffers.

int HandleLoadModelCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    FR_TA_LoadModel(params, paramTypes);
}
int FR_TA_LoadModel(TEE_Param params[4], uint32_t paramTypes) {
    // ...
    FaceIonCheck(params[3].memref.buffer, params[3].memref.size, &isFinish, 0, g_ion_count2);
    // ...
    FaceIonGet(params, paramTypes, g_ion_buffers2, &g_ion_count2);
    // ...
    if (!isFinish)
        return 0;
    // ...
    FaceIonMap(g_ion_buffers2, g_ion_count2, 1, &ion_count);
    // ...
    g_algo_api->FI_loadModel(g_ion_buffers2, g_ion_count2);
    // ...
}

The function FI_loadModel simply calls FaceIdCore::load_model, that calls MsgController::sendMsg with a message type of 0x10, and the ION buffers as argument. This message is handled by another thread in the HiAiManager::loadModelFromBuffers function.

int FI_loadModel(FIIonBuf *ionBuffers, int ionCount) {
    // ...
    return FaceIdCore::load_model(g_faceid_core, ionBuffers, ionCount);
}
int FaceIdCore::load_model(FaceIdCore *this, FIIonBuf *ionBuffers, int ionCount) {
    // ...
    msg_controller = MsgController::getMsgController();
    msgArg[0] = ionBuffers;
    msgArg[1] = ionCount;
    msgArg[2] = 0;
    MsgController::sendMsg(msg_controller, 0x10, msgArg, 0xC);
}

The HiAiManager::loadModelFromBuffers function, among other things, copies the ION buffers (containing the model) into the memory region defined in this->npuLoadMem. Since we have previously set this->npuLoadMem to user-controlled values, all the arguments of the memcpy_s call are under our control.

int HiAiManager::loadModelFromBuffers(HiAiManager *this, FIIonBuf *ionBuffers, int ionCount) {
    // ...
    vaddr = this->npuLoadMem.vaddr;
    sizeOfBufs = 0;
    for (int i = 0; i < ionCount; i++) {
        memcpy_s(vaddr, ionBuffer[i].size, ionBuffer[i].vaddr, ionBuffer[i].size);
        vaddr += ionBuffer[i].size;
        sizeOfBufs += ionBuffer[i].size;
    }
    // ...
}

Physical Address Leak

The last missing piece is knowing the virtual address at which the first ION buffer was mapped. This is really easy since this virtual address is written to the logs using tee_print in MemoryManager::initMemory (called from FaceIdCore::init).

int MemoryManager::initMemory(MemoryManager *this, FIIonBuf *buffers, int count) {
    // ...
    for (int index = 0; index < count; index++) {
        this->field_8[index] = buffers[index].vaddr;
        this->field_30[index] = buffers[index].size;
        ++this->count;
        if (FILog::mLogLevel > 2)
            tee_print(
                0,
                "%s %d:[%s][%s]%s index:%d, size:%d , addr:%p\n",
                "[info]",
                0x21,
                &FILogTag,
                "MemoryManager",
                "initMemory",
                index,
                buffers[index].size,
                buffers[index].vaddr);
    }
    // ...
}

In our proof of concept code, we set this->npuLoadMem.vaddr to 0xdeadbeef, thus triggering a crash at this address when memcpy_s is called:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000036 (tid: 54) data abort: 
[HM] [ERROR][2498]Bad memory access on address: 0xdeadbeef, fault_code: 0x92000045
[HM] 
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_SERVICE_FAC] tid=54 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=6970 prefer-ca=6970
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <?>+0x0/0x0
[HM] invalid fp. backtrace abort
[HM] Dump task states END
[HM] 

Param OOB Access in FI_onExec

In the FI_onExec function (seen previously), even if the TEE parameter types are correct, the size of the input buffer params[1] is never checked. Thus an OOB access is possible if the buffer is smaller than expected.

int FI_onExec(
        unsigned int commandID,
        void *buffer1,
        unsigned int length1,
        void *buffer2,
        unsigned int length2,
        uint32_t paramTypes) {
    // ...
    switch (commandID) {
        case 0x80013:
            // ...
            image_info.width = ((uint32_t *)buffer1)[0x12C01];
            // ...
        case 0x80014:
            // ...
            image_info.width = ((uint32_t *)buffer1)[0x380];
            // ...
        case 0x80015:
            // ...
            image_info.width = ((uint32_t *)buffer1)[0x12C01];
            // ...
        case 0x80016u:
            // ...
            image_info.width = ((uint32_t *)buffer1)[0x12C01];
            // ...
    }
    // ...

For example, in the code handling command 0x80013, a value is extracted from offset 0x4b004 into this buffer, resulting in a crash if the buffer is smaller than this offset. A proof of concept code can be used to trigger this bug:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1d00000028 (tid: 40) data abort: 
[HM] [ERROR][2498]Bad memory access on address: 0x7004e004, fault_code: 0x92000007
[HM] 
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_SERVICE_FAC] 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=7022 prefer-ca=7022
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_Z9FI_onExecjPvjS_j+0x420/0x9e4>
[HM] <_Z9FI_onExecjPvjS_j>+0x4e4/0x9e4
[HM] <TA_InvokeCommandEntryPoint>+0x4a0/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM] 

Null Pointer Dereference in MsgController::_sendMsg

To interact with the trustlet asynchronously, the CA can register an agent that can wait for events and send responses. This communication channel can be setup:

  • on the NWd side using the ioctl TC_NS_CLIENT_IOCTL_REGISTER_AGENT;
  • on the SWd side using the command HandlePrepareAgentCommand (#ID 0x17).

As can be seen below, HandlePrepareAgentCommand ends up calling the FI_prepare4Agent function from the lib_faceidalgo library.

uint32_t HandlePrepareAgentCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // [...]
    ret = FR_TA_PrepareAgent();
    // [...]
}
uint32_t FR_TA_PrepareAgent() {
    // [...]
    g_algo_api->FI_prepare4Agent()
    // [...]
}

FI_prepare4Agent calls FaceIdCore::prepare4Agent. This function then calls MsgController::threadLoop which starts a thread loop that can receive and send responses depending on the message code this->msgCode set in the MsgController object.

uint32_t FI_prepare4Agent() {
    // [...]
    FaceIdCore::prepare4Agent(g_faceid_core);
    // [...]
}
uint32_t FaceIdCore::prepare4Agent(FaceIdCore *this) {
    // [...]
    MsgController = MsgController::getMsgController();
    MsgController::threadLoop(MsgController);
    // [...]
}
uint32_t MsgController::threadLoop(MsgController *this) {
    // [...]
    MsgController::agentLock(this);
    // [...]
    do {
        MsgController::wait4Send(this);
        // [...]
        switch (this->msgCode) {
            // [...]
            // Handler called in function of msgCode
            // [...]
        }
    } while (!this->field_60);
    // [...]
}

During the initialization process on the NWd side, an agent buffer allocated over shared memory is provided. This buffer is retrieved and mapped in the SWd during the call to MsgController::agentLock, which sets this->agentBuffer using the function TEE_EXT_GetAgentBuffer.

uint32_t MsgController::agentLock(MsgController *this) {
    // [...]
    result = TEE_EXT_GetAgentBuffer(this->agentId, &this->agentBuffer,
        &this->agentBufsize);
    // [...]
}

Conversely, the agent buffer can be removed from the MsgController object by using the HandleDynamicUnloadCommand command (ID #0x11). This handler will call the FI_dynamicFini function in the lib_faceidalgo library which ends up calling MsgController::deInit.

uint32_t HandleDynamicUnloadCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // [...]
    ret = FR_TA_Finish();
    // [...]
}
uint32_t FR_TA_Finish() {
    // [...]
    g_algo_api->FI_dynamicFini();
    // [...]
}
int FI_dynamicFini() {
    // [...]
    return FaceIdCore::deinit(g_faceid_core);
    // [...]
}
int FaceIdCore::deinit(FaceIdCore *this) {
    // [...]
    msg_controller = MsgController::getMsgController();
    // [...]
    MsgController::deInit(msg_controller);
    // [...]
}
MsgController *MsgController::deInit(MsgController *this) {
    this->sysfuncs = 0;
    this->agentBuffer = 0;
    this->agentBufsize = 0;
    return this;
}

However, it's possible to set this->agentBuffer to 0 and then have the trustlet use it without any check. For instance, we can spell out the execution flow of the command HandleDynamicInitCommand (#ID 0x10) and see that it uses the agent buffer even after being removed.

HandleDynamicInitCommand starts by calling the FI_dynamicInit function in the lib_faceidalgo library, which sends a message to the message controller using the function MsgController::send with msgCode set to 0x12.

uint32_t HandleDynamicInitCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // [...]
    ret = FR_TA_DynamicInit(params, paramTypes);
    // [...]
}
uint32_t FR_TA_DynamicInit(TEE_Param *params, int paramTypes) {
    // [...]
    g_algo_api->FI_dynamicInit(
        g_ion_buffers, g_ion_count, iobuf3_addr + 0x10, config_size);
    // [...]
}
uint32_t FI_dynamicInit(FIIonBuf *ionBuffers, int ionCount, void *config,
        uint configSize) {
    // [...]
    FaceIdCore::init(g_faceid_core, ionBuffers, ionCount, config, configSize);
    // [...]
}
uint32_t FaceIdCore::init(FaceIdCore *this, FIIonBuf *ionBuffers, int ionCount,
        void *config, uint configSize) {
    // [...]
    HiAiManager::sendModelInfo2CA(HiAiManager);
    // [...]
}
uint32_t HiAiManager::sendModelInfo2CA(HiAiManager *this) {
    // [...]
    MsgController = MsgController::getMsgController();
    MsgController::sendMsg(MsgController, 0x12, &this->field_C, 0xAA4);
    return 0;
}
int MsgController::sendMsg(MsgController *this, int msgCode, char *msgArg, int msgArgSize) {
    // [...]
    pthread_mutex_lock(&this->field_64);
    this->msgCode = msgCode;
    this->msgArg = msgArg;
    this->msgArgSize = msgArgSize;
    MsgController::send(this);
    pthread_mutex_unlock(&this->field_64);
    return 0;
}

This message is then received in MsgController::threadLoop and since msgCode is 0x12 it will be handled by the function MsgController::_sendMsg called in the default case.

uint32_t MsgController::threadLoop(MsgController *this) {
    // [...]
    MsgController::agentLock(this);
    // [...]
    do {
        MsgController::wait4Send(this);
        // [...]
        switch (this->msgCode) {
            // [...]
            default:
                MsgController::_sendMsg(this);
                break;
        }
        // [...]
        MsgController::finishNotify(this);
    } while (!this->field_60);
    // [...]
}

However, this function will dereference this->agentBuffer which was set to 0 prior to the call to HandleDynamicInitCommand and will result in a null pointer dereference.

uint32_t MsgController::_sendMsg(MsgController *this) {
    // [...]
    *(uint32_t *)this->agentBuffer = this->msgCode;
    // [...]
}

This bug can be triggered using a proof of concept code and results in a crash because of a write at address 0:

[HM] [ERROR][2171]vmem_as_ondemand_prepare failed
[HM] [ERROR][2496]process 1e00000036 (tid: 54) data abort: 
[HM] [ERROR][2498]Bad memory access on address: 0x0, fault_code: 0x92000046
[HM] 
[HM] Dump task states for tcb
[HM] ----------
[HM]     name=[TEE_SERVICE_FAC] tid=54 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=6744 prefer-ca=6744
[HM] Registers dump:
[HM] ----------
[HM] 32 bits userspace stack dump:
[HM] ----------
[HM] <_ZN13MsgController8_sendMsgEv+0x14/0x48>
[HM] <_ZN13MsgController10threadLoopEv>+0x1f0/0x248
[HM] <_ZN13MsgController10threadLoopEv>+0x1f0/0x248
[HM] <_ZN10FaceIdCore13prepare4AgentEv>+0x2c/0xa0
[HM] <_Z16FI_prepare4Agentv>+0x34/0x98
[HM] <TA_InvokeCommandEntryPoint>+0x738/0x8e8
[HM] <tee_task_entry>+0x398/0xcd4
[HM] Dump task states END
[HM] 

Physical Address Leak in the Trustlet Function FR_TA_CoAuthSignImg

There is a leak of a static physical address in the FR_TA_CoAuthSignImg function.

int FR_TA_CoAuthSignImg(int paramTypes, TEE_Param *params, int nb_params) {
    // ...
    _fr_get_static_phy_addr(&phys_addr, 0, params[1].value.a, params[1].value.b);
    // ...
    tee_print(
        0,
        "%s %10.10s: check phy addr fail phy:%llu offset:%u \n",
        "[error]",
        "GetCoAuthImgPhyAddr",
        phys_addr,
        offset);
    // ...
}

Param Pointer Leak in the Trustlet Function FR_GetHwAuthToken

There is a TEE param pointer leak in the FR_GetHwAuthToken function.

int HandleGetAuthTokenCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    FR_GetHwAuthToken(params[1].memref.buffer, &params[1].memref.size);
    // ...
}
int FR_GetHwAuthToken(void *buffer, uint32_t *length_p) {
    // ...
    tee_print(
        0,
        "%s %10.10s: check param fail, token 0x%x, len 0x%x \n",
        "[error]",
        "FR_GetHwAuthToken",
        buffer,
        length_p);
    // ...
}

Param Pointer Leaks in the Trustlet Function FR_ActiveUserSet

There are TEE Param pointer leaks in the FR_ActiveUserSet function.

int HandleActiveUserSetCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    active = FR_ActiveUserSet(params[0].value.a, params[1].memref.buffer, &obuf1_size);
    // ...
}
int FR_ActiveUserSet(int activeid, int *faceids, unsigned int *length) {
    // ...
    tee_print(
        0,
        "%s %10.10s: active user set:param is invalid.activeid:%u,faceids:0x%x,length:0x%x \n",
        "[error]",
        "FR_ActiveUserSet",
        activeid,
        faceids,
        length);
    // ...
    tee_print(
        0,
        "%s %10.10s: get face ids fail.ret:0x%x,faceids:0x%x,length:0x%x,length:%u \n",
        "[error]",
        "FR_ActiveUserSet",
        DataBase,
        faceids,
        length,
        *length);
    // ...
}

ION Virtual Address Leak in the Trustlet Function FR_HashCheck

There is a leak of the virtual address at which an ION buffer is mapped in the FR_HashCheck function.

int HiAiManager::loadModelFromBuffers(HiAiManager *this, FIIonBuf *ionBuffers, int ionCount)
{
    // ...
    npuLoadMemAddr = this->npuLoadMem.vaddr;
    // ...
    HiAiManager::modelBuffShaCheck(this, npuLoadMemAddr, sizeOfBufs);
    // ...
}
int HiAiManager::modelBuffShaCheck(HiAiManager *this, void *npuLoadMemAddr, unsigned int sizeOfBufs) {
    // ...
    return this->sysfuncs->FR_HashCheck(
               npuLoadMemAddr,
               sizeOfBufs,
               &Product::mSha256,
               0x20);
}
int32_t FR_HashCheck(void *model, int modelLen, void *hash, int hashLen) {
    // ...
    tee_print(
        0,
        "%s %10.10s: param invalid,model=0x%x,modelLen=0x%x,hash=0x%x,hashLen=0x%x \n",
        "[error]",
        "FR_HashCheck",
        model,
        modelLen,
        hash,
        hashLen);
    // ...
}

Param Pointer Leak in the Trustlet Function FR_GetResultAuthToken

There is a TEE_Param pointer leak in the FR_GetResultAuthToken function.

TEE_Result TA_InvokeCommandEntryPoint(
        void *sessionContext,
        uint32_t commandID,
        uint32_t paramTypes,
        TEE_Param params[4]) {
    // ...
    FR_GetResultAuthToken(params[0].memref.size, params[1].memref.size);
    // ...
}
int FR_GetResultAuthToken(void *buffer, int length) {
    // ...
    tee_print(
        0,
        "%s %10.10s: param is not valid,authtoken = 0x%x, length = %u \n",
        "[error]",
        "FR_GetResultAuthToken",
        buffer,
        length);
    // ...
}

Heap Pointer Leak in the Trustlet Function FR_LoadDataBase

There is a heap pointer leak in the FR_LoadDataBase function.

int FR_LoadDataBase() {
    // ...
    FR_AllocMemToReadFile(v11, fileOps, &loadData, &dataLen);
    // ...
    tee_print(
        0,
        "%s %10.10s: [db_v3] this user has already enrolled faces, and need to restore DB: userId = %u, loadData = %p, dataLen = %u \n",
        "[info]",
        "FR_AllocMemToGetUserInfo",
        ActiveUserId,
        loadData,
        dataLen);
    // ...
}

Heap Pointer Leak in the Trustlet Function FR_FaceFeatureAdd

There is a heap pointer leak in the FR_FaceFeatureAdd function.

unsigned int HwRecognitionAlgo::processEnroll(HwRecognitionAlgo *this, CImageBuffer *a2) {
    // ...
    FeatureManager::writeFaceFeature(
             *((FeatureManager **)this + 7),
             (unsigned int *)this + 0x21, // <- faceIdPtr
             v19,
             v9,
             v14,
             v40);
    // ...
}
int STRecognitionAlgo::writeFaceFeature(int a1, void *a2, void *a3, unsigned int a4, unsigned int a5) {
    // ...
    FeatureManager::writeFaceFeature(
               *(FeatureManager **)(a1 + 0x1C),
               (unsigned int *)(a1 + 0x5C), // <- faceIdPtr
               a4,
               a5,
               *(const void **)(a1 + 0x78),
               *(_DWORD *)(a1 + 0x7C));
}
int FR_FaceFeatureAdd(uint32_t *faceIdPtr, unsigned int typeId, unsigned int poseId, int feat, int featLen) {
    // ...
    tee_print(
        0,
        "%s %10.10s: [db_v3] face feature add, param is invalid: feat = 0x%x, featLen = %u, faceIdPtr = %p \n",
        "[error]",
        "FR_FaceFeatureAdd",
        feat,
        featLen,
        faceIdPtr);
    // ...
}

Param Pointer Leaks in the Trustlet Function FR_SetFidoParam

There are TEE param pointer leaks in the FR_SetFidoParam function.

TEE_Result TA_InvokeCommandEntryPoint(
        void *sessionContext,
        uint32_t commandID,
        uint32_t paramTypes,
        TEE_Param params[4]) {
    // ...
    FR_SetHuksParam(
                params[0].memref.buffer,
                params[0].memref.size,
                params[1].memref.buffer,
                params[1].memref.size,
                *params[2].memref.size);
    // ...
}
int FR_SetHuksParam(
        void *ibuf0_addr,
        uint32_t ibuf0_size,
        void *ibuf1_addr,
        uint32_t ibuf1_size,
        uint32_t ibuf2_val) {
    // ...
    return FR_SetFidoParam(ibuf0_addr, ibuf0_size, ibuf1_addr, ibuf1_size, ibuf2_val);
    // ...
}
int FR_SetFidoParam(
        void *ibuf0_addr,
        uint32_t ibuf0_size,
        void *ibuf1_addr,
        uint32_t ibuf1_size,
        uint32_t ibuf2_val) {
    // ...
    tee_print(
        0,
        "%s %10.10s: param is invalid, aaid:0x%x, aaidLength:%u, nonce:0x%x, nonceLength:%u \n",
        "[error]",
        "FR_SetFidoParam",
        ibuf0_addr,
        ibuf0_size,
        ibuf1_addr,
        ibuf1_size);
    return 0x7A000003;
}

Stack Pointer Leak in the Trustlet Function FidoWrapUvt

There is a stack pointer leak in the FidoWrapUvt function.

int HandleGetuvtCommand(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    uint32_t uvtLen;
    char uvt[4096];
    // ...
    FidoWrapUvt(uvt, &uvtLen);
    // ...
}
int FidoWrapUvt(char *wrappedUvt, uint32_t *wrappedUvtLen_p) {
    tee_print(
        0,
        "%s %10.10s: wrap failure, param is NULL.uvt=0x%x,uvtLen=%u,wrappedUvt=0x%x \n",
        "[error]",
        "FidoWrapUvt",
        g_uvt,
        g_uvtLen,
        wrappedUvt);
    // ...
}

Heap Pointer Leaks in the Trustlet Function FR_UnwrapFeatureData

There are heap pointer leaks in the FR_UnwrapFeatureData function.

int FR_LoadDataBase() {
    // ...
    FR_AllocMemToReadFile(v31, v32, &databuf, &datalen);
    FR_UnwrapFeatureData(databuf, datalen, &datalen);
    // ...
}
int FR_UnwrapFeatureData(void *buf, int size, int *outlen_p) {
    // ...
    tee_print(
        0,
        "%s %10.10s: buf: %p, size: 0x%x, outlen: 0x%x, isWrapped:%d \n",
        "[info]",
        "FR_UnwrapFeatureData",
        buf,
        size,
        *outlen_p,
        isWrapped);
    // ...
    sub_13F7C(buf, size, &oputbuf, &bufSize);
    // ...
    tee_print(0, "%s %10.10s: oputbuf: %p, bufSize:0x%x \n", "[info]", "FR_UnwrapFeatureData", oputbuf, bufSize);
}

Heap Pointer Leak in the Library Function AlgoManager::createAlgo

There is a heap pointer leak in the function AlgoManager::createAlgo.

AlgoInterface *AlgoManager::createAlgo(
        AlgoManager *this,
        const char *name,
        FeatureManager *feature_manager,
        MemoryManager *memory_manager,
        MsgController *msg_controller)
{
    // [...]
    algo_fptrs = &g_algorithms;
    // [...]
    alloc_ptr = algo_fptr->newAlgo(feature_manager, memory_manager,
        msg_controller);
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s, name %s, %p \n", "[info]", 0x3C,
        &FILogTag, "AlgoManager", "createAlgo", name, alloc_ptr);
    // [...]
}
HwDetectionAlgo *HwDetectionAlgo::newAlgo(...)
{
    HwDetectionAlgo *alloc = new HwDetectionAlgo;
    // [...]
    return alloc;
}

ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers

In the function HiAiManager::loadModelFromBuffers, the trustlet library processes a list of model buffers model_list which contain information about trustlet-mapped ION buffers provided by the user. In the call to tee_print highlighted above, the function leaks the virtual address of one these mapped ION buffers.

uint32_t HiAiManager::loadModelFromBuffers(...) {
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s : name:%s, addr = %d, size = %d, perf = %d\n",
        "[info]", 0x100, &FILogTag, "HiAiManager", "loadModelFromBuffers",
        path_data, model_list->buffers[0].addr, size, perf);
    // [...]
}

ION Physical Memory Leak in the Library Function HiAiManager::runModelInMainThread

The function HiAiManager::runModelInMainThread leaks the physical address of the trustlet-mapped ION buffers provided by the user.

uint32_t HiAiManager::runModelInMainThread(HiAiManager *this, char *a2) {
    // [...]
    paddr = this->npuRunMem.paddr;
    vaddr = this->npuRunMem.vaddr;
    // [...]
    tee_print(0,
        "%s %d:[%s][%s]%s(), output %d-%d: n:%d, c:%d, h:%d, w:%d, size:0x%x, allignedSize:0x%x, inout_phy_addr:%p",
        "[info]", 0x1C7, &FILogTag, "HiAiManager", "runModelInMainThread",
        o1, o2, n, c, h, w, size, alignedSize, paddr);
    // [...]
}

ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers

In the function MemoryManager::initMemory, the trustlet library initializes a MemoryManager object using information about trustlet-mapped ION buffers provided by the user. In the call to tee_print highlighted above, the function leaks the virtual addresses of these mapped ION buffers.

uint32_t MemoryManager::initMemory(MemoryManager *this, FIIonBuf *buffers, int count) {
    // [...]
    for (int i = 0; i < count; i++) {
        // [...]
        tee_print(0, "%s %d:[%s][%s]%s index:%d, size:%d , addr:%p\n", "[info]", 0x21, &FILogTag,
        "MemoryManager", "initMemory", index, buffers->size, buffers->vaddr);
        // [...]
        buffers++;
    }
    // [...]
}

ION Virtual Memory Address Leak in the Library Function MemoryManager::alloc

The function MemoryManager::alloc leaks an address pointing inside one of the trustlet-mapped ION buffers. These pointers are set for the MemoryManager object in MemoryManager::initMemory.

char *MemoryManager::alloc(MemoryManager *this, int size, const char *name) {
    idx = 0;
    // [...]
    obj_addr = &this->vtable + idx;
    ion_ptr = obj_addr[2] + *this->field_5C;
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s size:%d, name:%s, addr:%p \n",
        "[info]", 0x33, &FILogTag, "MemoryManager", "alloc",
        size, name, fptr);
}

ION Virtual Memory Address Leak in the Library Function MemoryManager::free

Similarly to the vulnerability in MemoryManager::alloc, MemoryManager::free leaks the address passed as argument.

void MemoryManager::free(MemoryManager *this, void *addr, const char *name) {
    if ( FILog::mLogLevel > 2 )
        tee_print(0, "%s %d:[%s][%s]%s addr:%p, name %s \n", "[info]",
            0x3A, &FILogTag, "MemoryManager", "free", addr, name);
}

Pointer Leak in the Library Function MsgController::agentLock

The function MsgController::agentLock leaks the address of the buffer this->agentBuffer.

void MsgController::agentLock(MsgController *this)
{
    // [...]
    tee_print(0,
        "%s %d:[%s][%s]%s GetAgentBuffer success, agentBuffer=%p, agentBufsize=%d\n",
        "[info]", 0x4C, &FILogTag, "MsgController", "agentLock",
        this->agentBuffer, this->agentBufsize);
    // [...]
}

Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation

The function CImageBufferAllocator::endAllocatation leaks the address of a heap-allocated element from the linked list this->list.

uint32_t CImageBufferAllocator::endAllocatation(CImageBufferAllocator *this) {
    // [...]
    prev = this->list.prev;
    // [...]
    next = prev->list.next;
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s delete CImageBuffer %p\n", "[info]",
        0x32, &FILogTag, "CImageBufferAllocator", "endAllocatation",
        next);
    // [...]
}

Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation

The function CImageBufferAllocator::endAllocatation leaks the address of the heap-allocated object cimg_buf.

uint32_t CImageBufferAllocator::putBuffer(CImageBufferAllocator *this,
        CImageBuffer *cimg_buf, const char *a3)
{
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s delete CImageBuffer %p, name %s\n",
        "[info]", 0x54, &FILogTag, "CImageBufferAllocator", "putBuffer",
        cimg_buf, cimg_buf);
    // [...]
}

Heap Pointer Leak in the Library Function CImageBufferAllocator::beginAllocatation

The function CImageBufferAllocator::beginAllocatation leaks the address of the heap-allocated object cimg_buf.

uint32_t CImageBufferAllocator::beginAllocatation(
        CImageBufferAllocator *this,
        int width,
        int height,
        int stride,
        int size,
        int type,
        int count,
        int a8)
{
    cimg_buf = new CImageBuffer;
    // [...]
    tee_print(0, "%s %d:[%s][%s]%s new CImageBuffer %p\n",
        "[info]", 0x20, &FILogTag, "CImageBufferAllocator", "beginAllocatation",
        cimg_buf);
    // [...]
}

Pointer Leak in the Library Function CImageBuffer::delStride

The function CImageBuffer::delStride leaks the addresses of the addr fields of CImageBuffer and ImageBuffer objects.

uint32_t CImageBuffer::fillImage(CImageBuffer *this, ImageBuffer *image, int index)
{
    // [...]
    CImageBuffer::delStride(this, image->addr, image->width, image->height, image->stride,
        this->addr, 1);
    // [...]
}
uint32_t CImageBuffer::delStride(CImageBuffer *this, const void *src, int width,
        int height, int stride, void *dest, int a7)
{
    // [...]
    tee_print(0,
        "%s %d:[%s][%s]%s(), error, src=%p, dst=%p, width=%d, height=%d, stride=%d\n ",
        "[error]", 0xDC, &FILogTag, "CImageBuffer", "delStride",
        src, dest, width, height, stride);
    // [...]
}

The addr field is set as follows:

uint32_t CImageBufferAllocator::beginAllocatation(
        CImageBufferAllocator *this, int width, int height, int stride,
        int size, int type, int count, int a8)
{
    // [...]
    addr = MemoryManager::alloc(this->memory_manager, size, "CImageBufferAllocator");
    // [...]
    HwImage::HwImage(hwimg, width, height, stride, type, addr, size, this);
    // [...]
}
void HwImage::HwImage(HwImage *this, int width, int height, int stride,
        int type, void *addr, unsigned int size,
        CImageBufferAllocator *buffer_allocator)
{
    CImageBuffer::CImageBuffer(&this->parent, width, height, stride, type, addr, size, buffer_allocator);
    // [...]
}
void CImageBuffer::CImageBuffer(CImageBuffer *this, int width, int height,
        int stride, int type, void *addr, unsigned int size,
        CImageBufferAllocator *buffer_allocator)
{
    // [...]
    this->addr = addr;
    // [...]
}

Heap Pointer Leak in the Library Function CImageBuffer::fillImage

The function CImageBuffer::fillImage leaks the address of the heap-allocated object image.

uint32_t CImageBuffer::fillImage(CImageBuffer *this,
        ImageBuffer *image, int index)
{
    // [...]
    tee_print(
        0,
        "%s %d:[%s][%s]%s(), img=%p, size=%d, %x %x %x %x %x %x %x %x %x\n",
        "[verb]", 0xD2, &FILogTag, "CImageBuffer", "fillImage",
        image, /* [...] */ );
    // [...]
}

Pointer Leak in the Library Function CImageBuffer::attachBuffer

The function CImageBuffer::attachBuffer leaks the address of an ImageBuffer object.

uint32_t CImageBuffer::attachBuffer(CImageBuffer *this_1, ImageBuffer *img, int idx)
{
    // [...]
    if (this->mAttachedBuffer[idx]) {
        // [...]
        tee_print(0,
            "%s %d:[%s][%s]%s() fail, mAttachedBuffer[%d] exist %p\n ",
            "[error]", 0x1A0, &FILogTag, "CImageBuffer", "attachBuffer",
            idx, this->mAttachedBuffer[idx]);
        // [...]
    }
    // [...]
}

Pointer Leak in the Library Function ImageSourceBase::clear

The function ImageSourceBase::clear leaks the address of a CImageBuffer object.

void ImageSourceBase::clear(ImageSourceBase *this) {
    // [...]
    tee_print(
        0,
        "%s %d:[%s][%s]%s buffer %p\n",
        "[info]",
        0x32,
        (const char *)&FILogTag,
        "ImageSourceBase",
        "clear",
        prev->alg_img_buf[2]);
    // [...]
}

Heap Pointer Leaks in the Library Function PipelineBuilder::createPipeline

The function PipelineBuilder::createPipeline leaks multiple heap pointers.

uint32_t PipelineBuilder::createPipeline(
        int type,
        Pipeline **pipeline_p,
        AlgoManager *algo_manager,
        MemoryManager *memory_manager,
        MsgController *msg_controller,
        FeatureManager *feature_manager,
        FIAlgoConfig *fi_algo_config)
{
            // [...]
            *pipeline_p = &stpipeline_unlock->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_UNLOCK %p\n",
                "[info]", 0x27, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &stpipeline_enroll->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_ENROLL %p\n",
                "[info]", 0x3B, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &pipeline_unlock_test->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_UNLOCK_TEST %p\n",
                "[info]", 0x30, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &pipeline_liveliness_test->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_LIVENESS_TEST %p\n",
                "[info]", 0x42, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &stpipeline4_load_model->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_LOADMODEL %p\n",
                "[info]", 0x4D, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &stpipeline4_upgrade_model->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_UPGRADE_MODEL %p\n",
                "[info]", 0x5C, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
            *pipeline_p = &pipeline_gaze_test->parent;
            tee_print(0, "%s %d:[%s][%s]%s FI_PIPELINE_MODE_GAZE_UNLOCK_TEST %p\n",
                "[info]", 0x65, &FILogTag, "PipelineBuilder", "createPipeline",
                *pipeline_p);
            // [...]
}

ION Virtual Addresses Leaks in the Library Function STFaceidAlgo::loadCpuModel

There are leaks of the virtual address at which ION buffers are mapped in the STFaceidAlgo::loadCpuModel function.

int HiAiManager::loadModelFromBuffers(HiAiManager *this, FIIonBuf *ionBuffers, int ionCount) {
    // ...
    HiAiManager::cpuModelCheck(this, name, this->npuLoadMem.vaddr + offset, size);
    // ...
}
int HiAiManager::cpuModelCheck(HiAiManager *this, char *name, void *addr, int size) {
    // ...
    this->cpu_manager->vtable + 2))(
        this->cpu_manager,
        addr,
        size,
        this->cpuModelMem.vaddr,
        this->cpuModelMem.size);
    // ...
}
int STFaceidAlgo::loadCpuModel(STFaceidAlgo *this, void *model, int size, void *modelMem, int modelMemSize) {
    // ...
    tee_print(
        0,
        "%s %d:[%s][%s]%s model:%p, size:%d, modelMem:%p, modelMemSize:%d\n",
        "[info]",
        0x88,
        FILogTag,
        "STAlgo",
        "loadCpuModel",
        model,
        size,
        modelMem,
        modelMemSize);
    // ...
}

ION Virtual Address and Heap Pointer Leak in the Library Function hw_face_quality_estimation

There are leaks of the virtual address at which an ION buffer is mapped and of a heap pointer in the hw_face_quality_estimation function.

int HwQualityAlgo::init(HwQualityAlgo *this) {
    // ...
    data = MemoryManager::alloc(this->field_20, size, HwQualityAlgo::NAME);
    img_quality_setMemory(this->handle, data, size);
    // ...
}
unsigned int img_quality_setMemory(Img_Quality_t *handle, const void *memory, int size) {
    // ...
    handle->memory = memory;
    // ...
}
int img_quality_estimation_register(Img_Quality_t *handle, ImageBuffer *image, int *a3, int a4) {
    // ...
    v13[3] = image->addr;
    // ...
    hw_face_quality_estimation(
        v13,
        &v9,
        &handle->field_48,
        handle->memory + handle->field_4,
        a4);
    // ...
}
int hw_face_quality_estimation(void *a1, void *a2, void *a3, uint8_t *a4, void *a5) {
    // ...
    tee_print(
        0,
        "%s %d:[%s][%s]%d,%d,%d,%d, %p, %p, %d\n",
        "[info]",
        0xDB,
        (const char *)&FILogTag,
        "FaceIdDefaultTag",
        a2[2],
        *a2,
        a2[3],
        a2[1],
        a4,
        a1[3],
        *a1);
    }
    // ...
}

Heap Pointer Leaks in the Library Function buffered_free

There are leaks of heap pointers in the buffered_free function.

int buffered_free(void *ptr) {
    // ...
    g_secOsCb(
        0,
        "%s(%d):Fatal: release %p which not belong to pool checksum does not match\n",
        "buffered_free",
        0x319,
        ptr);
    // ...
    g_secOsCb(
        0,
        "%s(%d):Fatal: release memory %p not belong to largepool%d\n",
        "release",
        0xD1,
        ptr,
        largepool);
    // ...
    g_secOsCb(
        0,
        "%s(%d):Fatal: release %p which not belong to pool %d, checksum match\n",
        "buffered_free",
        0x328,
        ptr,
        pool);
    // ...
}

ION Virtual Memory Address Leak in the Library Function st_tee_initialize

There is a leak of the virtual address at which an ION buffer is mapped in the st_tee_initialize function.

int HiAiManager::loadModelFromBuffers(HiAiManager *this, FIIonBuf *ionBuffers, int ionCount) {
    // ...
    HiAiManager::cpuModelCheck(this, name, this->npuLoadMem.vaddr + offset, size);
    // ...
}
int HiAiManager::cpuModelCheck(HiAiManager *this, char *name, void *addr, int size) {
    // ...
    this->cpu_manager->vtable + 2))(
        this->cpu_manager,
        addr,
        size,
        this->cpuModelMem.vaddr,
        this->cpuModelMem.size);
    // ...
}
unsigned int STHiAiCpuManager::loadModel(
        STHiAiCpuManager *this,
        void *model,
        int size,
        void *modelMem,
        int modelMemSize) {
    // ...
    STFaceidAlgo::loadCpuModel(st_faceid_algo, model, size, modelMem, modelMemSize);
    // ...
}
int STFaceidAlgo::loadCpuModel(STFaceidAlgo *this, void *model, int size, void *modelMem, int modelMemSize) {
    // ...
    st_tee_initialize(this->handle, model);
    // ...
}
unsigned int st_tee_initialize(handle_t *handle, void *model) {
    // ...
    g_secOsCb(0, "%s(%d):handle: %p, modelptr: %p\n", "st_tee_initialize", 0x501, handle, model);
    // ...
}

Heap Pointer Leak in the Library Function st_tee_detect

There is a heap pointer leak in the st_tee_detect function.

unsigned int STFaceidAlgo::faceDetect(STFaceidAlgo *this, void *a2, int a3) {
    // ...
    st_tee_detect(this->handle, a3, ptr, buf1, buf2);
    // ...
}
unsigned int st_tee_detect(void *handle, int a2, void *a3, int a4, void *a5) {
    // ...
    g_secOsCb(3, "%s(%d):invalid handle %p\n", "st_tee_detect", 0x6FD, handle);
    // ...

Heap Pointer Leak in the Library Function st_tee_extract

There is a heap pointer leak in the st_tee_extract function.

int STFaceidAlgo::extract(STFaceidAlgo *this, void *a2, int a3, int a4, int a5) {
    // ...
    st_tee_extract(this->handle, a3, a2, 0, buf1, buf2, a4, a5);
    // ...
}
int st_tee_extract(void *handle, int a2, void *a3, int a4, void* a5, void *a6, uint32_t *a7, uint32_t *a8) {
    // ...
    g_secOsCb(3, "%s(%d):invalid handle %p\n", "st_tee_extract", 0xC55, handle);
    // ...
}

Stack Pointer and ION Virtual Address Leaks in the Library Function st_tee_create_handle

There are leaks of a stack address and the virtual address at which an ION buffer is mapped in st_tee_create_handle.

int HiAiManager::cpuModelCheck(HiAiManager *this, char *name, void *addr, int size) {
    // ...
    this->cpu_manager->vtable + 2))(
        this->cpu_manager,
        addr,
        size,
        this->cpuModelMem.vaddr,
        this->cpuModelMem.size);
    // ...
}
unsigned int STHiAiCpuManager::loadModel(
        STHiAiCpuManager *this,
        void *model,
        int size,
        void *modelMem,
        int modelMemSize) {
    // ...
    STFaceidAlgo::loadCpuModel(st_faceid_algo, model, size, modelMem, modelMemSize);
    // ...
}
int STFaceidAlgo::loadCpuModel(STFaceidAlgo *this, void *model, int size, void *modelMem, int modelMemSize) {
    // ...
    STFaceidAlgo::init(this, modelMem, modelMemSize);
    // ...
}
int STFaceidAlgo::init(STFaceidAlgo *this, void *modelMem, int modelMemSize) {
    // ...
    memory_config[0] = modelMem;
    memory_config[1] = modelMemSize;
    // ...
    st_tee_create_handle(&this->handle, memory_config);
    // ...
}
unsigned int st_tee_create_handle(handle_t **handle_p, const void **memory_config) {
    // ...
    g_secOsCb(0, "%s(%d):memory_config: %p\n", "st_tee_create_handle", 0x455, memory_config);
    g_secOsCb(
        0,
        "%s(%d):memory_config->ptr_pool_mem: %p\n",
        "st_tee_create_handle",
        0x456,
        *memory_config);
    // ...
}

Heap Pointer Leaks in the Library Function gray16to8_hist

There are heap pointer leaks in the gray16to8_hist function.

char *gray16to8_hist(uint16_t *a1, uint8_t *a2, int a3, int a4) {
    // ...
    hist = buffered_malloc(0x40000);
    g_secOsCb(0, "%s(%d):after buffered_malloc hist: %p\n", "gray16to8_hist", 0x5B4, hist);
    // ...
    hist_ptr = hist;
    // ...
    g_secOsCb(
        0,
        "%s(%d):min_val %d, max_val: %d, hist: %p, hist_ptr:%p\n",
        "gray16to8_hist",
        0x5D8,
        min_val,
        max_val,
        hist,
        hist_ptr);
    // ...
}

Virtual Address Leak in the Library Function HIAI_TensorBuffer_createFromTensorDesc

There is a leak of a virtual address in the HIAI_TensorBuffer_createFromTensorDesc function.

uint32_t *HIAI_TensorBuffer_createFromTensorDesc(uint32_t *a1) {
    // ...
    data = _SRE_MemAllocShared(0, size);
    tee_print(0, "%s %d:%s:data is 0x%x, size %d\n", "[info]", 0xCC, "HIAIModelManager.cpp", data, size);
    // ...
}

ION Virtual Memory Address Leak in the Library Function HIAI_ModelManager_loadFromModelBuffers

There is a leak of the virtual address at which ION buffers are mapped in the HIAI_ModelManager_loadFromModelBuffers function.

int HIAI_ModelManager_loadFromModelBuffers(HIAI_ModelManager *this, ModelBuffer **buffers, int nBuffers) {
    // ...
    for (int i = 0; i < model_list.nBuffers; i++) {
        tee_print(
            0,
            "%s %d:%s:addr[%d] = 0x%x.\n",
            "[debug]",
            0x1A2,
            "HIAIModelManager.cpp",
            index,
            model_list.buffers[index].addr);
        // ...
    }
    // ...
}

Stack and Heap Pointer Leak in FR_AloEnroll

There is a double heap pointer leak and a stack pointer leak in a log string printed by the function FR_AloEnroll called in the command EndEnroll (ID #0x3). The pointer to the stack variable enrolled_face_id_p is passed to FR_TA_EndEnroll and thus FR_AloEnroll by EndEnroll.

int EndEnroll(uint32_t paramTypes, TEE_Param params[4]) {
    // ...
    enrolled_face_id = 0;
    params[3].value.b = FR_TA_EndEnroll(
        ibuf0_addr,
        params->memref.size,
        ibuf1_addr,
        params[1].memref.size,
        &enrolled_face_id);
    params[2].value.b = enrolled_face_id;
}
int FR_TA_EndEnroll(
        template_t *templates_array,
        uint32_t templates_len,
        void *authtoken,
        uint32_t authtoken_len,
        int enrolled_face_id_p)
{
    // ...
    first_feature_len = 0;
    first_feature = GetPlainDataWhenEnroll(
        templates_array,
        templates_len,
        &templates_offset,
        template_cpy,
        &first_feature_len);
    middle_feature_len = 0;
    middle_feature = GetPlainDataWhenEnroll(
        templates_array,
        templates_len,
        &templates_offset,
        template_cpy,
        &middle_feature_len);
    FR_AloEnroll(
        first_feature,
        first_feature_len,
        enrolled_face_id_p,
        middle_feature,
        middle_feature_len);
    // ...
}
int FR_AloEnroll(
        void *first_feature,
        uint32_t first_feature_len,
        int *enrolled_face_id_p,
        void *middle_feature,
        uint32_t middle_feature_len)
{
    // ...
    tee_print(
        0,
        "%s %10.10s: [db_v3] face template add:param is invalid.template:0x%x,templen:%u,faceid:0x%x,image:0x%x,imagelen:%u \n",
        "[error]",
        "FR_AloEnroll",
        first_feature,      /* heap-allocated buffer  */
        first_feature_len,
        enrolled_face_id_p, /* stack variable pointer */
        middle_feature,     /* heap-allocated buffer  */
        middle_feature_len);
    // ...
}

The heap allocated buffers first_feature and middle_feature are allocated in GetPlainDataWhenEnroll.

void *GetPlainDataWhenEnroll(
        template_t *tmpl,
        unsigned int tmpl_len,
        int *tmpl_offset_p,
        const void *iv_buf,
        uint32_t *feature_len)
{
    // ...
    encrypted_len = *(uint32_t *)((char *)tmpl + *tmpl_offset_p);
    // ...
    decrypted_buf = TEE_Malloc(encrypted_len, 0);
    // ...
    return decrypted_buf;
}

The pointers will end up in NWd-accessible logs, which can then be read using utilities such as logcat.

Faulty check in GetPlainDataWhenEnroll

In the GetPlainDataWhenEnroll function, if the check of the template offset fails, an error will be logged, but the execution of the function will continue anyway and an out of bounds value might be logged by the next call to tee_print.

void *GetPlainDataWhenEnroll(
        template_t *tmpl,
        unsigned int tmpl_len,
        int *tmpl_offset_p,
        const void *iv_buf,
        uint32_t *feature_len)
{
    // ...
    if (*tmpl_offset_p + 4 > tmpl_len)
        tee_print(0, "%s %10.10s: Template length is invalid : %u \n", "[error]", "GetPlainDataWhenEnroll", tmpl_len);
    encrypted_len = *(int *)((char *)&tmpl + *tmpl_offset_p);
    tee_print(
        0,
        "%s %10.10s: Data length is %u \n",
        "[info]",
        "GetPlainDataWhenEnroll",
        encrypted_len);
    // ...
}

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
Unverified Param Types in FI_onExec Critical CVE-2022-48479 May 2023
OOB Write in HiAiManager::loadModelFromBuffers Critical CVE-2022-48478 May 2023
Integer Overflow in FR_TA_CoAuthSignImg High CVE-2022-48480 May 2023
Param OOB Access in FI_onExec Low N/A Fixed
Null Pointer Dereference in MsgController::_sendMsg Low N/A Fixed
Physical Address Leak in the Trustlet Function FR_TA_CoAuthSignImg Low N/A Fixed
Param Pointer Leak in the Trustlet Function FR_GetHwAuthToken Low N/A Fixed
Param Pointer Leaks in the Trustlet Function FR_ActiveUserSet Low N/A Fixed
ION Virtual Address Leak in the Trustlet Function FR_HashCheck Low N/A Fixed
Param Pointer Leak in the Trustlet Function FR_GetResultAuthToken Low N/A Fixed
Heap Pointer Leak in the Trustlet Function FR_LoadDataBase Low N/A Fixed
Heap Pointer Leak in the Trustlet Function FR_FaceFeatureAdd Low N/A Fixed
Param Pointer Leaks in the Trustlet Function FR_SetFidoParam Low N/A Fixed
Stack Pointer Leak in the Trustlet Function FidoWrapUvt Low N/A Fixed
Heap Pointer Leaks in the Trustlet Function FR_UnwrapFeatureData Low N/A Fixed
Heap Pointer Leak in the Library Function AlgoManager::createAlgo Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers Low N/A Fixed
ION Physical Memory Leak in the Library Function HiAiManager::runModelInMainThread Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function MemoryManager::alloc Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function MemoryManager::free Low N/A Fixed
Pointer Leak in the Library Function MsgController::agentLock Low N/A Fixed
Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation Low N/A Fixed
Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation Low N/A Fixed
Heap Pointer Leak in the Library Function CImageBufferAllocator::beginAllocatation Low N/A Fixed
Pointer Leak in the Library Function CImageBuffer::delStride Low N/A Fixed
Heap Pointer Leak in the Library Function CImageBuffer::fillImage Low N/A Fixed
Pointer Leak in the Library Function CImageBuffer::attachBuffer Low N/A Fixed
Pointer Leak in the Library Function ImageSourceBase::clear Low N/A Fixed
Heap Pointer Leaks in the Library Function PipelineBuilder::createPipeline Low N/A Fixed
ION Virtual Addresses Leaks in the Library Function STFaceidAlgo::loadCpuModel Low N/A Fixed
ION Virtual Address and Heap Pointer Leak in the Library Function hw_face_quality_estimation Low N/A Fixed
Heap Pointer Leaks in the Library Function buffered_free Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function st_tee_initialize Low N/A Fixed
Heap Pointer Leak in the Library Function st_tee_detect Low N/A Fixed
Heap Pointer Leak in the Library Function st_tee_extract Low N/A Fixed
Stack Pointer and ION Virtual Address Leaks in the Library Function st_tee_create_handle Low N/A Fixed
Heap Pointer Leaks in the Library Function gray16to8_hist Low N/A Fixed
Virtual Address Leak in the Library Function HIAI_TensorBuffer_createFromTensorDesc Low N/A Fixed
ION Virtual Memory Address Leak in the Library Function HIAI_ModelManager_loadFromModelBuffers Low N/A Fixed
Stack and Heap Pointer Leak in FR_AloEnroll Low N/A Fixed
Faulty check in GetPlainDataWhenEnroll Low N/A Fixed

Timeline

  • Dec. 21, 2021 - A first vulnerability report is sent to Huawei PSIRT.
  • Jan. 12, 2022 - Huawei PSIRT acknowledges the first vulnerability report.
  • Jan. 21, 2022 - A second vulnerability report is sent to Huawei PSIRT.
  • Feb. 24, 2022 - Huawei PSIRT acknowledges the second vulnerability report.
  • May 01, 2023 - Huawei PSIRT states that these issues were fixed in the May 2023 updates.
  • From Nov. 30, 2022 to Jul, 19 2023 - We exchange regularly about the release of our advisories.
  • Jul. 03, 2023 - Huawei PSIRT informs us that some of the vulnerabilities are not patched and will be fixed in the August 2023 update.