CodeQL 文档

析构函数中未释放资源

ID: cpp/resource-not-released-in-destructor
Kind: problem
Security severity: 
Severity: warning
Precision: high
Tags:
   - efficiency
   - readability
   - external/cwe/cwe-404
   - external/jsf
Query suites:
   - cpp-security-and-quality.qls

单击查看 CodeQL 仓库中的查询

此规则查找由类分配但未在该类的析构函数中释放的资源。分配资源包括

  • 使用 malloc 分配内存

  • 使用 new 创建对象

  • 打开文件

  • 打开网络套接字资源管理可能是一项复杂的任务,因此一个标准最佳实践是“资源获取即初始化”(RAII)模式。在 RAII 类中,构造函数分配所有必需的资源,析构函数释放所有资源。这保证了只需删除类的实例就足以释放资源,并得益于 C++ 的自动对象生命周期管理。只要 RAII 类的生命周期得到适当管理,设计良好的 RAII 类就不可能成为资源泄漏的来源。

  • 如果使用 new 分配,则应由创建它的客户端使用 delete 释放

  • 如果其生命周期是词汇的(它仅在一个函数中使用),那么将其声明为该函数的局部变量(而不是指针)就足够了,C++ 运行时将确保在退出时释放它。有两个可能的消息

  • “资源 *x* 由类 *C* 获取但未在析构函数中释放。它从 *f* 中释放,因此可能需要在析构函数中调用此函数”。这表明资源(*x*)正在被释放,只是不在类的析构函数中。通常,它是在名为 closefree 或类似名称的函数中释放的。这并不总是表示资源泄漏,但它表明该类使用起来不必要地复杂,因为它不符合 RAII 模式。

  • “资源 *x* 由类 *C* 获取,但未在该类的任何地方释放”。这表明该类正在分配资源,但没有负责释放它们。这非常容易出错:即使类需要显式关闭运算符,它也应该管理它分配的任何资源,而不是强迫客户端管理它们。在最坏的情况下,如果客户端代码未释放资源,这可能导致资源泄漏。

建议

如果资源根本没有被释放,请确保该类释放了资源,通常是通过将释放操作添加到该类的析构函数中。此更改需要仔细验证:客户端代码可能依赖于资源比分配它的类生存时间更长,并且如果需要,必须审查和更新。

在另一种情况下,例如具有显式 close 函数的类,目标是将该类迁移到简单的 RAII 模式。这可以通过几个步骤来实现

  1. 首先,确保 close 函数(或其等效项)可以安全地调用两次,仅在资源尚未释放的情况下才释放资源。

  2. 接下来,从析构函数中调用 close 函数。这不需要更改客户端代码,因为它可以安全地调用两次。

  3. 迁移客户端代码以删除对 close 函数的直接使用,并借此机会检查对象本身是否被正确删除。

  4. 最后,如果可能,也将初始化代码迁移到类的构造函数中,使其完全遵循 RAII 模式。

示例

// This class opens a file but never closes it. Even its clients
// cannot close the file
class ResourceLeak {
private:
    int sockfd;
    FILE* file;
public:
    C() {
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
    }

    void f() {
        file = fopen("foo.txt", "r");
        ...
    }
};

// This class relies on its client to release any stream it
// allocates. Note that this means the client must have
// intimate knowledge of the implementation of the class to
// decide whether it is safe to release the stream. 
class StreamPool {
private:
  Stream *instance;
public:
  Stream *createStream(char *name) {
    if (!instance) 
      instance = new Stream(name);
    return instance;
  }
}

// This class handles its resources, but does not do that in
// the constructor/destructor. It can be rewritten easily to
// be safer to use.
class StreamHandler {
private:
  char *_name;
  Stream *stream;
public:
  C(char *name) {
    _name = strdup(name):
  }
  void open() {
    stream = new Stream();
  }
  void close() {
    delete stream;
  }
  ~StreamHandler() {
    free(_name);
    // stream should be deleted here, not in close()
  }
}

参考

  • AV 规则 79,《联合攻击战斗机空中载具 C++ 编码标准》。洛克希德马丁公司,2005 年。

  • S. Meyers。《Effective C++ 第 3 版》。第 61-66 页。Addison-Wesley Professional,2005 年。

  • 资源获取即初始化

  • 常见弱点枚举:CWE-404.

  • ©GitHub, Inc.
  • 条款
  • 隐私