ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

cppcheck 的 mallocOnClassError/mallocOnClassWarning 检查:用 malloc() 分配 C++ 类实例的未定义行为诊断

cppcheck 的 mallocOnClassError/mallocOnClassWarning 检查:用 malloc() 分配 C++ 类实例的未定义行为诊断 开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载导读本文基于 cppcheck 仓库中的官方检查器文档 man/checkers/mallocOnClassError.md完整讲解mallocOnClassError与mallocOnClassWarning两条检查规则的触发条件、危害原理与修复方法并结合 lib/checkclass.cpp 的源码实现与 test/testclass.cpp 的测试用例说明 cppcheck 是如何在符号数据库中识别用 C 内存分配函数为 C 类分配内存这一危险模式的。读完本文你将掌握这两种报错级别的判定边界、底层检测逻辑以及如何在真实代码中用new安全替换malloc()。检查器概览项目内容消息文本Memory for class instance allocated with malloc(), but class contains a virtual function.类别Undefined Behaviour未定义行为严重级别mallocOnClassErrorError错误mallocOnClassWarningWarning警告语言C关联 CWEError 对应 CWE-665不正确的初始化Warning 对应 CWE-762不匹配的内存管理函数实现位置lib/checkclass.cpp声明见 lib/checkclass.h在 cppcheck 中这两条检查由CheckClass检查器家族负责与memset()/memcpy()/memmove()作用于类对象的相关检查如memsetClass系列共享同一套类内成员递归分析基础逻辑实际检测入口为CheckClassImpl::checkMemset()。问题本质malloc() 不会调用构造函数malloc()、calloc()、realloc()只负责分配原始内存字节绝不调用 C 构造函数。这意味着即便内存已按sizeof(C)分配到位该内存中也并不存在一个C类型的对象——对象的生存期必须由构造函数的调用才开始。malloc()调用本身不会崩溃或行为异常真正的风险在于后续通过该指针所做的一切操作调用成员函数尤其是虚函数——虚函数通过 vtable 指针间接跳转而 vtable 指针从未被初始化读取未设置的 vtable 指针是未定义行为让析构函数在该内存上运行——析构函数的清理逻辑建立在对象已正确构造的前提之上按标准规则只有对象生存期真正开始后使用该类型的对象的规则才适用此前的一切访问都属于未定义行为Undefined Behaviour。正是因此该检查器在 cppcheck 中被归入 Undefined Behaviour 类别。Error 与 Warning 的判定边界cppcheck 将同一类代码问题拆分为两个级别依据是类是否必然需要构造函数运行mallocOnClassError错误类的正确性明确依赖构造函数运行此类分配几乎必然出错。文档明确列出的两类触发条件为类包含虚函数——vtable 指针永远无法被设置类包含非平凡non-trivial成员类型该成员自身需要构造例如std::string。mallocOnClassWarning警告类存在构造函数但类的正确性未必绝对依赖它代码碰巧仍可能正常工作例如构造函数仅把成员设置为与零初始化内存相同的值。此场景下无法断定必然出错因此降级为警告。这一划分在源码中有直接对应mallocOnClassError()报告时使用Severity::error而mallocOnClassWarning()使用Severity::warning见 lib/checkclass.cpp。源码实现检测链路逐层解析检测入口与函数识别检测逻辑位于CheckClassImpl::checkMemset()lib/checkclass.cpp。该函数遍历符号数据库中所有函数作用域mSymbolDatabase-functionScopes内的 token匹配两种模式memset|memcpy|memmove (调用——需要解析出第三个参数中sizeof(...)引用的类型%var% %name% (形式的赋值——左值是指针变量右侧调用需被识别为内存分配函数。对第二种模式cppcheck 通过Library::AllocFunc信息确认函数身份先查getAllocFuncInfo()再查getReallocFuncInfo()lib/checkclass.cpp。分配函数清单来自库配置文件 cfg/std.cfg其中alloc initfalse buffer-sizemallocmalloc,std::malloc/alloc alloc inittrue buffer-sizecalloccalloc,std::calloc/alloc alloc initfalse buffer-sizemalloc:2aligned_alloc,std::aligned_alloc/alloc因此malloc、std::malloc、calloc、std::calloc、realloc等均会被识别测试用例中也验证了realloc会触发相同警告见下文测试部分。若识别出的分配函数bufferSize为none则跳过。类型判定与递归成员分析分配目标的类型通过以下途径确定对memset系列解析第三参数中的sizeof表达式支持sizeof ( %type% )、sizeof ( %type% :: %type% )、sizeof ( struct %type% )等写法对p malloc(...)赋值直接取指针变量tok-variable()-typeScope()作为类作用域。确定类型后调用checkMemsetType()lib/checkclass.cpp这是整个判定的核心其逻辑为递归检查父类沿type-definedType-derivedFrom遍历所有基类基类中的虚函数、STL 成员同样会触发报告虚函数检查遍历type-functionList一旦发现带virtual说明符的函数在分配场景allocation true下报告mallocOnClassError消息中注明原因 virtual functionlib/checkclass.cpp非平凡成员检查遍历type-varlist对非静态、非 const、非指针的成员若其是 STL 类型var.isStlType()且既不是std::array也没有在库配置中被声明为 POD 类型则报告mallocOnClassError消息中注明成员类型名例如std::stringlib/checkclass.cpp嵌套已知类型成员为已知类类型时递归调用checkMemsetType()继续深入lib/checkclass.cpp去重保护用parsedTypes集合记录已检查过的类型避免对继承链上的同一类型重复分析。Warning 的触发条件完成上述分析后若printWarnings即 warning 严重级别在设置中被启用且指针所指向的类作用域numConstructors 0则报告mallocOnClassWarninglib/checkclass.cpp。也就是说类有构造函数、但无虚函数也无非平凡成员→ 仅警告类有虚函数或非平凡成员→ 直接错误错误级别优先且无论 warning 是否开启都会报告。numConstructors来自符号数据库对类作用域的统计mallocOnClassError与mallocOnClassWarning的报告消息格式见 lib/checkclass.cpp两条消息都给出了修复建议Consider using new instead.测试用例验证判定行为与边界测试集中在 test/testclass.cpp 的mallocOnClass()用例中可逐条对照判定边界触发 Warning有构造函数无虚函数/非平凡成员class C { C() {} }; void foo(C* p) { p malloc(sizeof(C)); } // [test.cpp:3:5] - [test.cpp:1:1]: // (warning) Memory for class instance allocated with malloc(), but class provides constructors. [mallocOnClassWarning]构造函数带参数同样触发使用realloc也触发消息中的函数名随之变为 realloc()。触发 Error虚函数struct C { virtual void bar(); }; void foo(C* p) { p malloc(sizeof(C)); } // [test.cpp:3:9] - [test.cpp:1:1]: // (error) Memory for class instance allocated with malloc(), but class contains a virtual function. [mallocOnClassError]触发 Error非平凡成员struct C { std::string s; }; void foo(C* p) { p malloc(sizeof(C)); } // [test.cpp:3:9] - [test.cpp:1:1]: // (error) Memory for class instance allocated with malloc(), but class contains a std::string. [mallocOnClassError]不触发合法用法空类/纯 C 风格结构体class C { };配malloc(sizeof(C))不报告构造函数的缺失对平凡类型无影响使用new分配p new C();不报告目标指针类型未知如p声明为D*而分配sizeof(C)类型无法匹配不报告。注意测试输出中的[test.cpp:3:5] - [test.cpp:1:1]格式前者是分配发生的位置后者是类定义的位置说明报告同时携带使用点与类定义点两个定位信息。修复方法用 new 替代 malloc()文档给出的修复方案十分直接——将 C 风格内存分配替换为new表达式。以下两对示例完整对应 Error 与 Warning 两种场景。修复 Error类含虚函数修复前#include cstdlib struct C { virtual void bar(); }; void foo(C* p) { p malloc(sizeof(C)); // - mallocOnClassError: no constructor/vtable set up }修复后struct C { virtual void bar(); }; void foo(C* p) { p new C(); }new C()会调用构造函数、初始化 vtable 指针使对象的生存期真正开始之后对p的虚函数调用才是合法行为。修复 Warning类含构造函数修复前#include cstdlib class C { public: C() {} }; void foo(C* p) { p malloc(sizeof(C)); // - mallocOnClassWarning: C()s body never runs }修复后class C { public: C() {} }; void foo(C* p) { p new C(); }即便该构造函数当前只是把成员设为与零初始化内存相同的值、代码碰巧可用也应改用new消除隐患——一旦未来构造函数逻辑变化malloc 分配的实例就会暴露未初始化成员问题。注意如果对象由new创建对应的释放也应当使用delete避免与free()混用导致 CWE-762 类问题。实际使用建议将分配/释放职责在代码中配对用new分配的对象用delete释放用malloc分配的内存用free释放两者不可混用对于带虚函数、继承体系或持有 STL 容器的类一律使用new/make_unique等 C 惯用法构造如需在原始内存上手动构造对象如内存池、placement new 场景应显式使用new (ptr) C()形式的 placement new并手动调用析构函数而不是依赖malloc在项目中使用 cppcheck 的完整检查时可在 CLI 中直接指定 C 语言并开启检查例如cppcheck --languagec --enablewarning,error file.cpp随后依据输出中的错误 IDmallocOnClassError/mallocOnClassWarning定位并修复问题。延伸阅读本规则官方说明man/checkers/mallocOnClassError.md检测源码实现lib/checkclass.cpp接口声明lib/checkclass.h测试用例test/testclass.cpp分配函数库配置cfg/std.cfg赞分享开发工具静态分析代码质量质量保障【免费下载链接】cppcheckstatic analysis of C/C code项目地址https://gitcode.com/gh_mirrors/cpp/cppcheck点击查看免费下载相关推荐cppcheck invalidFree 检测详解偏移指针释放Mismatching Free的未定义行为诊断cppcheck invalidFree 检测详解偏移指针释放Mismatching Free的未定义行为诊断 导读 invalidFree 是 cppc开发工具静态分析代码质量质量保障mistral.rs 流式聊天响应完全指南从 Rust 端处理 Response 变体、工具进度事件与取消mistral.rs 流式聊天响应完全指南从 Rust 端处理 Response 变体、工具进度事件与取消 stream_chat_request 是 mis开发工具静态分析代码质量质量保障cppcheck returnReference 检查器详解C 返回局部变量引用的未定义行为检测cppcheck returnReference 检查器详解C 返回局部变量引用的未定义行为检测 导读 returnReference 是 cppchec开发工具静态分析代码质量质量保障上一篇如何将asyncpg与监控系统集成Grafana与Datadog完整指南下一篇TextSnatcher如何在Linux上快速从图片中提取文字的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表