- CVE-2022-48479 Unverified Param Types in FI_onExec
- CVE-2022-48478 OOB Write in HiAiManager::loadModelFromBuffers
- CVE-2022-48480 Integer Overflow in FR_TA_CoAuthSignImg
- HWPSIRT-2022-47870 Param OOB Access in FI_onExec
- HWPSIRT-2022-11475 Null Pointer Dereference in MsgController::_sendMsg
- HWPSIRT-2022-62681 Physical Address Leak in the Trustlet Function FR_TA_CoAuthSignImg
- HWPSIRT-2022-97884 Param Pointer Leak in the Trustlet Function FR_GetHwAuthToken
- HWPSIRT-2022-28444 Param Pointer Leaks in the Trustlet Function FR_ActiveUserSet
- HWPSIRT-2022-35074 ION Virtual Address Leak in the Trustlet Function FR_HashCheck
- HWPSIRT-2022-75731 Param Pointer Leak in the Trustlet Function FR_GetResultAuthToken
- HWPSIRT-2022-84671 Heap Pointer Leak in the Trustlet Function FR_LoadDataBase
- HWPSIRT-2022-37694 Heap Pointer Leak in the Trustlet Function FR_FaceFeatureAdd
- HWPSIRT-2022-25260 Param Pointer Leaks in the Trustlet Function FR_SetFidoParam
- HWPSIRT-2022-80068 Stack Pointer Leak in the Trustlet Function FidoWrapUvt
- HWPSIRT-2022-65649 Heap Pointer Leaks in the Trustlet Function FR_UnwrapFeatureData
- HWPSIRT-2022-88030 Heap Pointer Leak in the Library Function AlgoManager::createAlgo
- HWPSIRT-2022-84485 ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers
- HWPSIRT-2022-14567 ION Physical Memory Leak in the Library Function HiAiManager::runModelInMainThread
- HWPSIRT-2022-59214 ION Virtual Memory Address Leak in the Library Function HiAiManager::loadModelFromBuffers
- HWPSIRT-2022-77892 ION Virtual Memory Address Leak in the Library Function MemoryManager::alloc
- HWPSIRT-2022-10579 ION Virtual Memory Address Leak in the Library Function MemoryManager::free
- HWPSIRT-2022-75307 Pointer Leak in the Library Function MsgController::agentLock
- HWPSIRT-2022-51942 Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation
- HWPSIRT-2022-95703 Heap Pointer Leak in the Library Function CImageBufferAllocator::endAllocatation
- HWPSIRT-2022-32077 Heap Pointer Leak in the Library Function CImageBufferAllocator::beginAllocatation
- HWPSIRT-2022-41496 Pointer Leak in the Library Function CImageBuffer::delStride
- HWPSIRT-2022-32043 Heap Pointer Leak in the Library Function CImageBuffer::fillImage
- HWPSIRT-2022-66227 Pointer Leak in the Library Function CImageBuffer::attachBuffer
- HWPSIRT-2022-37311 Pointer Leak in the Library Function ImageSourceBase::clear
- HWPSIRT-2022-75322 Heap Pointer Leaks in the Library Function PipelineBuilder::createPipeline
- HWPSIRT-2022-43439 ION Virtual Addresses Leaks in the Library Function STFaceidAlgo::loadCpuModel
- HWPSIRT-2022-24846 ION Virtual Address and Heap Pointer Leak in the Library Function hw_face_quality_estimation
- HWPSIRT-2022-13888 Heap Pointer Leaks in the Library Function buffered_free
- HWPSIRT-2022-75166 ION Virtual Memory Address Leak in the Library Functionst_tee_initialize
- HWPSIRT-2022-07617 Heap Pointer Leak in the Library Function st_tee_detect
- HWPSIRT-2022-78044 Heap Pointer Leak in the Library Function st_tee_extract
- HWPSIRT-2022-97971 Stack Pointer and ION Virtual Address Leaks in the Library Function st_tee_create_handle
- HWPSIRT-2022-88935 Heap Pointer Leaks in the Library Function gray16to8_hist
- HWPSIRT-2022-21060 Virtual Address Leak in the Library Function HIAI_TensorBuffer_createFromTensorDesc
- HWPSIRT-2022-65800 ION Virtual Memory Address Leak in the Library Function HIAI_ModelManager_loadFromModelBuffers
- HWPSIRT-2021-29098 Stack and Heap Pointer Leak in FR_AloEnroll
- HWPSIRT-2021-28201 Faulty check in GetPlainDataWhenEnroll
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
andconfigSize
from theparams[3]
input buffer; - it calls
FaceIonGet
, that copies the physical address and size of the param ION buffers intog_ion_buffers
(andg_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 theparams[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 thesubbuffers
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);
- the number of subbuffers comes from
- 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, ¶ms[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.
Copyright © Impalabs 2021-2023