You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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静态分析误判的根源在于:

  1. 当socket()返回INVALID_SOCKET时,代码仍会执行closesocket(socFd),对无效句柄调用释放函数会触发未定义行为,导致分析工具误识别为资源泄漏。
  2. 代码分支逻辑未明确区分“资源已创建”和“资源未创建”的路径,静态分析无法清晰跟踪资源生命周期。

修复方案

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 06:35:54