2023.09.07
CVE-2021-39629 | A-197353344 | EoP | 高 | 9、10、11、12 |
---|---|---|---|---|
patch

分析
漏洞点位于phTmlNfc_Init
函数,该函数会malloc一个chunk给上下文变量gpphTmlNfc_Context
,然后初始化其中的一些成员变量。当TML层初始化失败时,只调用**phTmlNfc_CleanUp**
函数来清除初始化过程中打开的handles,而已经初始化的一些接口并没有被清除掉,导致UAF。
patch是把phTmlNfc_CleanUp
换成phTmlNfc_Shutdown_CleanUp
。看下面的代码,区别是后者会先调用phTmlNfc_Shutdown()
,然后再调用phTmlNfc_CleanUp
。所以前者就是缺少了shutdown的过程。
那么shutdown和cleanup有什么区别呢?按照对这两个函数的描述来看,phTmlNfc_Shutdown()
用于清除已经初始化的一些接口(猜测主要是在关闭gpphTmlNfc_Context->readerThread
和 gpphTmlNfc_Context->writerThread
这两个线程的时候?),而且该函数里还会销毁互斥锁。而phTmlNfc_CleanUp()
函数主要用于清除初始化过程中打开的handles。
此外,shutdown会对
gpphTmlNfc_Context
的成员变量进行sem_post
操作,而cleanup会对gpphTmlNfc_Context
的成员变量进行sem_destroy
操作。
sem_post和sem_destroy两者的区别?
sem_post和sem_destroy都是POSIX信号量操作函数。
sem_post函数用于释放信号量,即增加指定信号量的计数值,使其变为可用状态。这通常用于释放由信号量控制的某个资源,使其他等待该资源的线程可以继续执行。如果没有线程在等待,则信号量的值仅增加1。
sem_destroy函数用于销毁指定的信号量。它会彻底释放信号量所占用的资源,并将信号量的状态设置为未初始化的状态。当一个信号量被销毁后,它不能再被使用。在调用sem_destroy之后,如果再次对信号量进行操作,将会产生未定义的行为。通常在不再需要使用信号量时,会调用sem_destroy函数来释放相关的资源。
综上所述,sem_post用于释放信号量,即增加信号量的计数值,使其变为可用状态。而sem_destroy用于销毁信号量,即彻底释放信号量的资源,并将其恢复到未初始化的状态。
phTmlNfc_Init
/*******************************************************************************
**
** Function phTmlNfc_Init
**
** Description Provides initialization of TML layer and hardware interface
** Configures given hardware interface and sends handle to the
** caller
** 该函数是TML(Transport Layer Manager)层的初始化函数。
** TML层用于提供与硬件接口的交互,包括配置硬件接口和发送句柄给调用者。
**
** Parameters pConfig - TML configuration details as provided by the upper
** layer 参数pConfig是TML配置的详细信息,由上层提供。
**
** Returns NFC status:
** NFCSTATUS_SUCCESS - initialization successful
** NFCSTATUS_INVALID_PARAMETER - at least one parameter is
** invalid
** NFCSTATUS_FAILED - initialization failed (for example,
** unable to open hardware interface)
** NFCSTATUS_INVALID_DEVICE - device has not been opened or has
** been disconnected
** 返回NFC状态,包括成功初始化、无效参数、初始化失败和设备未打开或已断开连接等
**
*******************************************************************************/
NFCSTATUS phTmlNfc_Init(pphTmlNfc_Config_t pConfig) {
NFCSTATUS wInitStatus = NFCSTATUS_SUCCESS;
/* Check if TML layer is already Initialized */
// 检查TML层是否已经初始化,如果已经初始化则返回错误码NFCSTATUS_ALREADY_INITIALISED
if (NULL != gpphTmlNfc_Context) {
/* TML initialization is already completed */
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_ALREADY_INITIALISED);
}
/* Validate Input parameters */
// 验证输入参数,如果参数为空或者dwGetMsgThreadId参数为默认值(PH_TMLNFC_RESET_VALUE),
// 则返回错误码NFCSTATUS_INVALID_PARAMETER。
else if ((NULL == pConfig) ||
(PH_TMLNFC_RESET_VALUE == pConfig->dwGetMsgThreadId)) {
/*Parameters passed to TML init are wrong */
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_INVALID_PARAMETER);
} else {
/* Allocate memory for TML context */
// 为TML上下文分配内存
gpphTmlNfc_Context =
(phTmlNfc_Context_t*)malloc(sizeof(phTmlNfc_Context_t));
// 如果内存分配失败,则返回错误码NFCSTATUS_FAILED
if (NULL == gpphTmlNfc_Context) {
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_FAILED);
} else {
/* Initialise all the internal TML variables */
// 初始化TML的所有内部变量,将上下文内存区域清零,并设置线程标志位为1,表示线程已完成。
memset(gpphTmlNfc_Context, PH_TMLNFC_RESET_VALUE,
sizeof(phTmlNfc_Context_t));
/* Make sure that the thread runs once it is created */
gpphTmlNfc_Context->bThreadDone = 1;
/* Open the device file to which data is read/written */
// 打开设备文件并进行配置,返回状态码给wInitStatus。
wInitStatus = phTmlNfc_i2c_open_and_configure(
pConfig, &(gpphTmlNfc_Context->pDevHandle));
// 如果打开设备失败,则返回错误码NFCSTATUS_INVALID_DEVICE。
if (NFCSTATUS_SUCCESS != wInitStatus) {
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_INVALID_DEVICE);
gpphTmlNfc_Context->pDevHandle = NULL;
// 如果打开成功,则初始化读写信息的其他变量,包括是否启用读写线程和线程忙标志位等。
} else {
gpphTmlNfc_Context->tReadInfo.bEnable = 0;
gpphTmlNfc_Context->tWriteInfo.bEnable = 0;
gpphTmlNfc_Context->tReadInfo.bThreadBusy = false;
gpphTmlNfc_Context->tWriteInfo.bThreadBusy = false;
// 初始化互斥锁和信号量。如果初始化失败,则返回错误码NFCSTATUS_FAILED。
if (pthread_mutex_init(&gpphTmlNfc_Context->readInfoUpdateMutex,
NULL) != 0) {
wInitStatus = NFCSTATUS_FAILED;
} else if (0 != sem_init(&gpphTmlNfc_Context->rxSemaphore, 0, 0)) {
wInitStatus = NFCSTATUS_FAILED;
} else if (0 != sem_init(&gpphTmlNfc_Context->txSemaphore, 0, 0)) {
wInitStatus = NFCSTATUS_FAILED;
} else if (0 != sem_init(&gpphTmlNfc_Context->postMsgSemaphore, 0, 0)) {
wInitStatus = NFCSTATUS_FAILED;
} else {
// 发送信号量postMsgSemaphore,表示TML线程已开始运行。
sem_post(&gpphTmlNfc_Context->postMsgSemaphore);
/* Start TML thread (to handle write and read operations) */
// 启动TML线程来处理写和读操作。如果线程启动失败,则返回错误码NFCSTATUS_FAILED
if (NFCSTATUS_SUCCESS != phTmlNfc_StartThread()) {
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_FAILED);
} else {
/* Create Timer used for Retransmission of NCI packets */
// 创建用于重传NCI数据包的定时器。
gpphTmlNfc_Context->dwTimerId = phOsalNfc_Timer_Create();
// 如果定时器创建成功,则存储线程标识符dwCallbackThreadId和配置信息eConfig。
if (PH_OSALNFC_TIMER_ID_INVALID != gpphTmlNfc_Context->dwTimerId) {
/* Store the Thread Identifier to which Message is to be posted */
gpphTmlNfc_Context->dwCallbackThreadId =
pConfig->dwGetMsgThreadId;
/* Enable retransmission of Nci packet & set retry count to
* default */
gpphTmlNfc_Context->eConfig = phTmlNfc_e_DisableRetrans;
/* Retry Count = Standby Recovery time of NFCC / Retransmission
* time + 1 */
// 设置重试计数bRetryCount,计算公式为(2000 / PHTMLNFC_MAXTIME_RETRANSMIT)+ 1
gpphTmlNfc_Context->bRetryCount =
(2000 / PHTMLNFC_MAXTIME_RETRANSMIT) + 1;
gpphTmlNfc_Context->bWriteCbInvoked = false;
} else {
wInitStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_FAILED);
}
}
}
}
}
}
/* Clean up all the TML resources if any error */
// 如果初始化过程中发生错误,则清理TML的所有资源。
if (NFCSTATUS_SUCCESS != wInitStatus) {
/* Clear all handles and memory locations initialized during init */
phTmlNfc_CleanUp(); // 【漏洞点】
}
return wInitStatus; // 返回初始化状态码wInitStatus。
}
phTmlNfc_CleanUp - Free
/*******************************************************************************
**
** Function phTmlNfc_CleanUp
**
** Description Clears all handles opened during TML initialization
** 清除所有TML层初始化时打开的handles
**
** Parameters None
**
** Returns None
**
*******************************************************************************/
void phTmlNfc_CleanUp(void) {
// 检查gpphTmlNfc_Context是否为NULL。
// 如果为NULL,表示TML上下文尚未初始化,直接返回
if (NULL == gpphTmlNfc_Context) {
return;
}
// 检查gpphTmlNfc_Context->pDevHandle是否为NULL。
// 如果不为NULL,表示有设备handle存在。
if (NULL != gpphTmlNfc_Context->pDevHandle) {
// 调用phTmlNfc_i2c_reset函数重置I2C通信,并忽略其返回值
(void)phTmlNfc_i2c_reset(gpphTmlNfc_Context->pDevHandle, 0);
// 将gpphTmlNfc_Context->bThreadDone的值设为0,用于终止线程
gpphTmlNfc_Context->bThreadDone = 0;
}
// 销毁下面三个信号量
sem_destroy(&gpphTmlNfc_Context->rxSemaphore);
sem_destroy(&gpphTmlNfc_Context->txSemaphore);
sem_destroy(&gpphTmlNfc_Context->postMsgSemaphore);
// 调用phTmlNfc_i2c_close函数关闭I2C通信
phTmlNfc_i2c_close(gpphTmlNfc_Context->pDevHandle);
// 将gpphTmlNfc_Context->pDevHandle的值设为NULL
gpphTmlNfc_Context->pDevHandle = NULL;
/* Clear memory allocated for storing Context variables */
// 释放存储上下文变量的内存空间
free((void*)gpphTmlNfc_Context);
/* Set the pointer to NULL to indicate De-Initialization */
// 将gpphTmlNfc_Context的值设NULL,表示已经完成了销毁
gpphTmlNfc_Context = NULL;
return;
}
phTmlNfc_Shutdown_CleanUp - Free
/*******************************************************************************
**
** Function phTmlNfc_Shutdown_CleanUp
**
** Description wrapper function for shutdown and cleanup of resources
**
** Parameters None
**
** Returns NFCSTATUS
**
*******************************************************************************/
NFCSTATUS phTmlNfc_Shutdown_CleanUp() {
NFCSTATUS wShutdownStatus = phTmlNfc_Shutdown();
phTmlNfc_CleanUp();
return wShutdownStatus;
}
phTmlNfc_Shutdown
/*******************************************************************************
**
** Function phTmlNfc_Shutdown
**
** Description Uninitializes TML layer and hardware interface
** 清除TML层和hardware的接口
**
** Parameters None
**
** Returns NFC status:
** NFCSTATUS_SUCCESS - TML configuration released successfully
** NFCSTATUS_INVALID_PARAMETER - at least one parameter is
** invalid
** NFCSTATUS_FAILED - un-initialization failed (example: unable
** to close interface)
**
*******************************************************************************/
NFCSTATUS phTmlNfc_Shutdown(void) {
NFCSTATUS wShutdownStatus = NFCSTATUS_SUCCESS;
/* Check whether TML is Initialized */
// 检查gpphTmlNfc_Context是否为NULL。
// gpphTmlNfc_Context是一个指向TML上下文的指针,用于判断TML是否已初始化
if (NULL != gpphTmlNfc_Context) {
/* Reset thread variable to terminate the thread */
// 将gpphTmlNfc_Context->bThreadDone的值设为0,用于终止线程
gpphTmlNfc_Context->bThreadDone = 0;
usleep(1000); // 暂停1毫秒
/* Clear All the resources allocated during initialization */
// 清除所有初始化时分配的资源
// 调用sem_post函数,释放gpphTmlNfc_Context->rxSemaphore信号量
sem_post(&gpphTmlNfc_Context->rxSemaphore);
usleep(1000);
// 调用sem_post函数,释放gpphTmlNfc_Context->txSemaphore信号量
sem_post(&gpphTmlNfc_Context->txSemaphore);
usleep(1000);
// 调用sem_post函数,释放gpphTmlNfc_Context->postMsgSemaphore信号量
sem_post(&gpphTmlNfc_Context->postMsgSemaphore);
usleep(1000);
// 这里为啥重复调用?
sem_post(&gpphTmlNfc_Context->postMsgSemaphore);
usleep(1000);
// 销毁gpphTmlNfc_Context->readInfoUpdateMutex互斥锁
pthread_mutex_destroy(&gpphTmlNfc_Context->readInfoUpdateMutex);
// pthread_join函数用于等待指定的线程终止,并获取其返回值
// 下面这两个if是用来确保readerThread线程和writerThread线程已经成功终止
// 检查gpphTmlNfc_Context->readerThread线程的终止状态,
// 如果pthread_join函数返回值不为0(即线程终止失败)
// 则打印错误日志
if (0 != pthread_join(gpphTmlNfc_Context->readerThread, (void**)NULL)) {
NXPLOG_TML_E("Fail to kill reader thread!");
}
// 检查gpphTmlNfc_Context->writerThread线程的终止状态
if (0 != pthread_join(gpphTmlNfc_Context->writerThread, (void**)NULL)) {
NXPLOG_TML_E("Fail to kill writer thread!");
}
NXPLOG_TML_D("bThreadDone == 0");
} else {
// 如果ghTmlNfc_Context为NULL,则将wStatus的值设为PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_NOT_INITIALISED)。
// PHNFCSTVAL是一个用于创建NFC错误的宏
wShutdownStatus = PHNFCSTVAL(CID_NFC_TML, NFCSTATUS_NOT_INITIALISED);
}
// 返回wShutdownStatus作为函数的结果
return wShutdownStatus;
}
phTmlNfc_TmlThread - Use
/*******************************************************************************
**
** Function phTmlNfc_TmlThread
**
** Description Read the data from the lower layer driver
**
** Parameters pParam - parameters for Writer thread function
**
** Returns None
**
*******************************************************************************/
static void* phTmlNfc_TmlThread(void* pParam) {
NFCSTATUS wStatus = NFCSTATUS_SUCCESS;
int32_t dwNoBytesWrRd = PH_TMLNFC_RESET_VALUE;
uint8_t temp[260];
uint8_t readRetryDelay = 0;
/* Transaction info buffer to be passed to Callback Thread */
static phTmlNfc_TransactInfo_t tTransactionInfo;
/* Structure containing Tml callback function and parameters to be invoked
by the callback thread */
static phLibNfc_DeferredCall_t tDeferredInfo;
/* Initialize Message structure to post message onto Callback Thread */
static phLibNfc_Message_t tMsg;
UNUSED(pParam);
NXPLOG_TML_D("PN54X - Tml Reader Thread Started................\n");
/* Writer thread loop shall be running till shutdown is invoked */
while (gpphTmlNfc_Context->bThreadDone) {
/* If Tml write is requested */
/* Set the variable to success initially */
wStatus = NFCSTATUS_SUCCESS;
sem_wait(&gpphTmlNfc_Context->rxSemaphore);
/* If Tml read is requested */
if (1 == gpphTmlNfc_Context->tReadInfo.bEnable) {
NXPLOG_TML_D("PN54X - Read requested.....\n");
/* Set the variable to success initially */
wStatus = NFCSTATUS_SUCCESS;
/* Variable to fetch the actual number of bytes read */
dwNoBytesWrRd = PH_TMLNFC_RESET_VALUE;
/* Read the data from the file onto the buffer */
if (NULL != gpphTmlNfc_Context->pDevHandle) {
NXPLOG_TML_D("PN54X - Invoking I2C Read.....\n");
dwNoBytesWrRd =
phTmlNfc_i2c_read(gpphTmlNfc_Context->pDevHandle, temp, 260);
if (-1 == dwNoBytesWrRd) {
NXPLOG_TML_E("PN54X - Error in I2C Read.....\n");
if (readRetryDelay < MAX_READ_RETRY_DELAY_IN_MILLISEC) {
/*sleep for 30/60/90/120/150 msec between each read trial incase of
* read error*/
readRetryDelay += 30;
}
usleep(readRetryDelay * 1000);
sem_post(&gpphTmlNfc_Context->rxSemaphore);
} else if (dwNoBytesWrRd > 260) {
NXPLOG_TML_E("Numer of bytes read exceeds the limit 260.....\n");
readRetryDelay = 0;
sem_post(&gpphTmlNfc_Context->rxSemaphore);
} else {
pthread_mutex_lock(&gpphTmlNfc_Context->readInfoUpdateMutex);
memcpy(gpphTmlNfc_Context->tReadInfo.pBuffer, temp, dwNoBytesWrRd);
readRetryDelay = 0;
NXPLOG_TML_D("PN54X - I2C Read successful.....\n");
/* This has to be reset only after a successful read */
gpphTmlNfc_Context->tReadInfo.bEnable = 0;
if ((phTmlNfc_e_EnableRetrans == gpphTmlNfc_Context->eConfig) &&
(0x00 != (gpphTmlNfc_Context->tReadInfo.pBuffer[0] & 0xE0))) {
NXPLOG_TML_D("PN54X - Retransmission timer stopped.....\n");
/* Stop Timer to prevent Retransmission */
uint32_t timerStatus =
phOsalNfc_Timer_Stop(gpphTmlNfc_Context->dwTimerId);
if (NFCSTATUS_SUCCESS != timerStatus) {
NXPLOG_TML_E("PN54X - timer stopped returned failure.....\n");
} else {
gpphTmlNfc_Context->bWriteCbInvoked = false;
}
}
if (gpphTmlNfc_Context->tWriteInfo.bThreadBusy) {
NXPLOG_TML_D("Delay Read if write thread is busy");
usleep(2000); /*2ms delay to give prio to write complete */
}
/* Update the actual number of bytes read including header */
gpphTmlNfc_Context->tReadInfo.wLength = (uint16_t)(dwNoBytesWrRd);
phNxpNciHal_print_packet("RECV",
gpphTmlNfc_Context->tReadInfo.pBuffer,
gpphTmlNfc_Context->tReadInfo.wLength);
dwNoBytesWrRd = PH_TMLNFC_RESET_VALUE;
/* Fill the Transaction info structure to be passed to Callback
* Function */
tTransactionInfo.wStatus = wStatus;
tTransactionInfo.pBuff = gpphTmlNfc_Context->tReadInfo.pBuffer;
/* Actual number of bytes read is filled in the structure */
tTransactionInfo.wLength = gpphTmlNfc_Context->tReadInfo.wLength;
/* Read operation completed successfully. Post a Message onto Callback
* Thread*/
/* Prepare the message to be posted on User thread */
tDeferredInfo.pCallback = &phTmlNfc_ReadDeferredCb;
tDeferredInfo.pParameter = &tTransactionInfo;
tMsg.eMsgType = PH_LIBNFC_DEFERREDCALL_MSG;
tMsg.pMsgData = &tDeferredInfo;
tMsg.Size = sizeof(tDeferredInfo);
pthread_mutex_unlock(&gpphTmlNfc_Context->readInfoUpdateMutex);
NXPLOG_TML_D("PN54X - Posting read message.....\n");
phTmlNfc_DeferredCall(gpphTmlNfc_Context->dwCallbackThreadId, &tMsg);
}
} else {
NXPLOG_TML_D("PN54X -gpphTmlNfc_Context->pDevHandle is NULL");
}
} else {
NXPLOG_TML_D("PN54X - read request NOT enabled");
usleep(10 * 1000);
}
} /* End of While loop */
return NULL;
}