Klockwork检测到RH.LEAK资源泄漏问题排查求助
Klockwork检测到frametxrxmngr.cpp中SOCKET资源泄漏问题
问题概述
生成Klockwork报告时,frametxrxmngr.cpp文件触发RH.LEAK错误:第385行通过socket()调用获取的SOCKET类型资源socFd,被判定可能在第500行发生泄漏。即便移除closesocket(socFd)语句,该错误仍未消失。
相关函数代码
FrameTxRxMngr::sendStrmReqInfoToTarget( void ) { uint32_t retVal = IPNEXT_SUCCESS; uint32_t timeout = TIMEOUT_IN_MS; stFrameHeader frameHdr{}; struct sockaddr_in destAddr{}; SOCKET socFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (socFd == INVALID_SOCKET) { printf("sendStrmReqInfoToTarget() Socket creation failed"); retVal = IPNEXT_ERR_SCKT_CRT_FAIL; } else { struct sockaddr_in localAddr; localAddr.sin_family = AF_INET; // 将IP地址复制到localAddr.sin_addr memcpy(&(localAddr.sin_addr.s_addr), &m_pIpCfginfo->x86SrcIPAddr, sizeof(m_pIpCfginfo->x86SrcIPAddr)); localAddr.sin_port = htons(PORT_NUM); // 尝试将套接字绑定到指定IP地址 int32_t bindResult = bind(socFd, (SOCKADDR *)&localAddr, sizeof(localAddr)); if (bindResult == SOCKET_ERROR) { printf("ERROR: bind failed with %d for sendStrmReqInfoToTarget\n", WSAGetLastError()); retVal = IPNEXT_ERR_UDP_BIND_FAIL; } else { // 设置地址族为IPv4 destAddr.sin_family = AF_INET; // 将IP地址复制到destAddr.sin_addr memcpy(&(destAddr.sin_addr.s_addr), &m_pIpCfginfo->c10DestIPAddr, sizeof(m_pIpCfginfo->c10DestIPAddr)); destAddr.sin_port = htons(PORT_NUM); } if (retVal == IPNEXT_SUCCESS) { // 设置套接字连接的超时值,如果失败则处理 int32_t sockoptResult = setsockopt(socFd, SOL_SOCKET, SO_RCVTIMEO, (const CHAR *)&timeout, sizeof(int32_t)); if (sockoptResult == SOCKET_ERROR) { printf("sendStrmReqInfoToTarget setsockopt for SO_RCVTIMEO Failed"); retVal = IPNEXT_ERR_UDP_RECV_TIMEOUT; } } } // 向目标发送待处理的流数量 if (retVal == IPNEXT_SUCCESS) { int32_t bytesSent = sendto(socFd, (CHAR *)m_pTxRxConfig->pActStrmToProcess, sizeof(stActiveStreamFiles), 0, (sockaddr*)&destAddr, sizeof(destAddr)); if (bytesSent == SOCKET_ERROR) { printf("sendStrmReqInfoToTarget sendto failed with ErrCode: %d\n", WSAGetLastError()); retVal = IPNEXT_ERR_UDP_SENDTO; } } if (retVal == IPNEXT_SUCCESS) { // 使用套接字连接接收数据并存储 int32_t bytesRecv = recvfrom(socFd, (CHAR *)&frameHdr, sizeof(stFrameHeader), 0, NULL, NULL); if (bytesRecv == SOCKET_ERROR) { // 检查错误是否由超时导致 if (WSAGetLastError() == WSAETIMEDOUT) { printf("sendStrmReqInfoToTarget recvfrom Timeout!! Didn't recv all the data"); retVal = IPNEXT_ERR_UDP_RECV_TIMEOUT; } else { printf("sendStrmReqInfoToTarget recvfrom failed ErrCode: %d", WSAGetLastError()); retVal = IPNEXT_ERR_UDP_RECVFROM; } } } if (retVal == IPNEXT_SUCCESS) { // 如果收到否定应答 if (!frameHdr.isFrmAckSuccess) { retVal = IPNEXT_ERR_NEG_ACK; } } closesocket(socFd); return retVal; }
Klockwork错误详情
# 错误: frametxrxmngr.cpp:500 RH.LEAK (2:错误) 分析 第385行获取到'socFd'资源可能在此处丢失。 * frametxrxmngr.cpp:385: 资源获取: 在调用'socket'时获取了'socFd' * frametxrxmngr.cpp:500: 资源丢失: 'socFd' 当前状态: '分析'
问题分析与修复方案
核心原因
Klockwork静态分析误判的根源在于:
- 当
socket()返回INVALID_SOCKET时,代码仍会执行closesocket(socFd),对无效句柄调用释放函数会触发未定义行为,导致分析工具误识别为资源泄漏。 - 代码分支逻辑未明确区分“资源已创建”和“资源未创建”的路径,静态分析无法清晰跟踪资源生命周期。
修复方案
方案1:仅对有效套接字调用closesocket
在释放资源前增加有效性判断,避免对无效句柄执行释放操作,同时让分析工具明确资源释放路径:
// 替换原有的closesocket(socFd); if (socFd != INVALID_SOCKET) { closesocket(socFd); }
方案2:用RAII机制管理套接字资源
封装RAII类自动管理套接字生命周期,从根本上杜绝泄漏风险:
class SocketGuard { private: SOCKET m_sock; public: explicit SocketGuard(SOCKET sock) : m_sock(sock) {} ~SocketGuard() { if (m_sock != INVALID_SOCKET) { closesocket(m_sock); } } // 禁止拷贝与移动 SocketGuard(const SocketGuard&) = delete; SocketGuard& operator=(const SocketGuard&) = delete; SocketGuard(SocketGuard&&) = delete; SocketGuard& operator=(SocketGuard&&) = delete; SOCKET get() const { return m_sock; } };
在函数中使用:
SOCKET socFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SocketGuard sockGuard(socFd); // 后续无需手动调用closesocket,析构函数会自动处理
方案3:提前返回避免无效分支
当socket()创建失败时直接返回,减少不必要的分支执行,让分析工具更清晰地跟踪资源流向:
if (socFd == INVALID_SOCKET) { printf("sendStrmReqInfoToTarget() Socket creation failed"); return IPNEXT_ERR_SCKT_CRT_FAIL; // 提前返回,跳过后续代码 }
上述任一方案均可消除Klockwork的RH.LEAK误判,同时提升代码的健壮性。
内容的提问来源于stack exchange,提问作者Harsh JAIN
相关产品推荐
相关产品推荐

