From 8212146fad7a737726c0ce74d49dc8e2fac65150 Mon Sep 17 00:00:00 2001 From: owlmk Date: Thu, 22 Sep 2022 10:16:50 +0800 Subject: [PATCH 1/2] =?UTF-8?q?:art:=E8=BD=AC=E7=A7=BB=E8=A7=84=E5=88=99?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../\350\247\204\345\210\231/cpp_doc.md" | 509 ++++++++++++++++++ .../enhanced_safety_java.md" | 0 2 files changed, 509 insertions(+) create mode 100644 "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" rename "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md" => "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" (100%) diff --git "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" new file mode 100644 index 000000000..4d3452e62 --- /dev/null +++ "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" @@ -0,0 +1,509 @@ +# 规则案例Cpp篇 +[TOC] + +## 线程锁检查 + +包含规则: +- missing_lock +- dead_lock + +在使用多线程对文件全局变量或类成员在进行读写时,工具会对未正确的进行上锁操作和上锁异常而引发死锁的情况进行检查。 + +支持的多线程标准库库包括(若有其他库需求可提issue): +- pthread +- thread +#### missing_lock +missing_lock 如果发现多线程中某个全局变量在未持有锁便更新时,则会上报错误。 + +##### 代码示例 +以下提供一个或多个 missing_loc 案例 +在下面代码中,函数 increase1, increase2 皆以将 counter 加到 1000 为目的。如果使用 increase1 函数则有可能多个线程皆在 1000 时进入循环导致最后 counter结果大于 1000 + +``` +int counter = 0; +std::mutex mtx; // 保护counter +void increase1() { + while (1) { + if (counter <= 1000) + counter++; // defect: missing_lock + else + break; + } +} +void increase2() { + while (1) { + mtx.lock(); // example_lock + if (counter <= 1000) + counter++; + else + break; + mtx.unlock(); // example_release + } +} +``` + +#### dead_lock +dead_lock 如果发现文件内存在 mtx1 -> mtx2 的上锁顺序时,另存在mtx2 -> mtx1 的上锁顺序,视为死锁或存在死锁的可能,则会上报错误。 +死锁发生时程序将会卡死无法正常执行。 + +##### 规则参数 +- better-lock: True or False 默认为 False + +##### 代码示例 +以下提供一个或多个 dead_lock 案例 + +在下面代码中,函数 increase 以将 counter 加到 1000 为目的。但在线程 1 中第一次释放 mtx 后,线程 2 的 mtx 上锁,此时线程1等待线程2释放mtx,线程2等待线程1释放mtx2,形成死锁,程序卡死。 +``` +int counter = 0; +std::mutex mtx; +std::mutex mtx2; +void increase() { + while (1) { + mtx.lock(); + mtx2.lock(); + mtx.unlock(); + mtx.lock(); // defect: dead_lock + if (counter <= 1000) + counter++; + else + break; + mtx2.unlock(); + mtx.unlock() + } +} +``` + +在下面代码中 线程函数increase1存在mtx -> mtx2 的顺序,increase2顺序为 mtx2 -> mtx;视为出现死锁。 +``` +void increase1() { + while (1) { + mtx.lock(); + mtx2.lock(); + if (counter <= 1000) + counter++; + else + break; + mtx2.unlock(); + mtx.unlock() + } +} +void increase2() { + while (1) { + mtx2.lock(); + mtx.lock(); // defect: dead_lock; + if (counter <= 1000) + counter++; + else + break; + mtx2.unlock(); + mtx.unlock() + } +} +``` + +以下案例在`better-lock`参数为`True`时将会生效 +使用`better-lock`规则会检查在上锁期间若调用其他函数时将视为可能会出现预期之外的异常,且上锁期间应只做对全局变量操作以提升性能 +``` +void increase1() { + while (1) { + mtx.lock(); + if (counter <= 1000) + counter++; + else + break; + read_counter(counter); // defect: dead_lock + mtx.unlock() + } +} +void read_counter(counter){ + std::cout << counter << std::endl; + do_something_more(); +} +void increase1() { + while (1) { + std::lock_guard lk(mtx); // good: 使用lock_guard会自动上锁解锁将不会检查dead_lock + if (counter <= 1000) + counter++; + else + break; + read_counter(counter); + } +} +``` + +## 资源泄漏检查 +包含规则 +- resource_leak + +#### resource_leak +resource_leak 在程序申请了资源但并未按时释放时上报错误 +目前场景包括:句柄打开时未关闭,指针分配内存后没有及时释放 + +##### 代码示例 +以下将提供一个或多个resource_leak案例 + +``` +int leak_example1(int c) { + void *p = malloc(10); + if(c) + return -1; // defect: if c "p" is leaked + free(p); + return 0; +} + +int leak_example2() { + void *p = malloc(10); + void *q = malloc(20); + if(!p || !q) + return -1; // defect: "p" or "q" may be leaked if the other is NULL + /*...*/ + free(q); + free(p); + return 0; +} + +void leak_example3(int c) { + FILE *p = fopen("starwar.anakin", "rb"); + if(c) + return; // defect: leaking file pointer "p" + fclose(p); +} +``` + +##### TODO +指针为返回值目前不会进行上报与检查,需要后期增加对返回值是否释放的检查 + +## 无效值检查 +包含规则 +- unused_value + +#### unused_value +unused_value 检查那些赋予给变量的值是否正确被使用,存在连续两次赋予变量值的情况,视为第一次赋予的值未被正确使用,报出错误。 +两次连续赋值可能存在条件控制语句出现错误、变量名拼写错误等情况。 +##### 代码示例 +以下提供一个或多个unused_value案例 + +以下函数会因为key的不同去不一样的神明,但实际上 Zeus Hades却永远不会使用到。 +``` +const char* key_value(const int key) { + const char * value = 0; + if (key != 0) { + value = "Zeus"; + } else if (key != 1) { + value = "Hades"; + } + if (key != 2) { // Should be 'else if' here. + value = "Poseidon"; // defect: unused_value Zeus Hades never used + } + else { + value = "Unknow + } + return result; +} +``` +以下继续提供几个unused_value代码 +``` +const char* key_value1(const int key) { + const char * value = 0; + value = "Zeus"; // defect: Zeus never used + if (key == 1) { + value = "Hades; + } else if (key == 2) { + value = "Poseidon"; + } else { // May else need not be here + value = "Unknow"; + } + return value +} + +const char* key_value2(const int key) { + const char * value = 0; + value = "Zeus"; // better Zeus used + if (key == 1) { + value = "Hades; + } else if (key == 2) { + value = "Poseidon"; + } + return value +} + +const char* key_value3(const int key) { + const char * value = 0; + if (key == 1) { + value = "Hades; + } else { + value = "Poseidon"; + } + value = "Zeus"; // defect: Hades Poseidon never used + return value +} +``` + +## 数组溢出检查 +包含规则 +- array_overflow +- buff_overflow + +#### array_overflow +array_overflow 检查数组越界的情况。不正确的缓存区访问可能损坏内存,导致程序崩溃或读取到权限外的内存。 + +##### 代码示例 +以下提供一个或多个array_overflow案例 + +``` +void foo() { + int array[10]; + int i = get(); + // i = 9; + if (i > 8 && i <= length(array)) { // Shoud be i < length(array) + array[i] = 1; // defect: array[10] overflow + } + array[i] = 1; // defect: array[10] overflow +} + + +void test(int i) { + int n= 10; + char *p = malloc(sizeof(int) * 10); + int y = n; + p[y] = 'a'; // defect: writing to buffer[y] overflow +} +``` + +##### TODO +- 提供规则参数支持函数参数作为数组或索引的情况检查 + +#### buff_overflow +buff_overflow 检查`strcpy`,`strcat`字符串复制/拼接过程中长度不当导致的溢出, 同样`gets` `scanf`函数也视为不安全 + +##### 代码示例 +以下提供一个或多个buff_overflow案例 +``` +void overflow1() { + + char a[4]={0}; + strcpy(a, "Poseidon"); // defect: len("Poseidon") > 4 strncpy is better + return; +} + +void overflow2() { + char s1[10] = "1"; + char s2[] = "1234567890"; + strcat(s1, s2); // defect: len(s1 + s2) > 10 + // strncat(s1, s2, 6) // strncat is better + return 0; +} + +``` + +## 指针检查 +包含规则 +- func_ret_null +- func_ret_null_full +- use_after_free +- forward_null +- reverse_null +- glob_null_pointer + +#### func_ret_null +func_ret_null 函数返回值可能为nullpointer,但是调用该函数时指针未经判空便进行使用
+在选用func_ret_null_full 时, 检查器会在项目内全局搜索空指针函数的调用情况,否则只会在相关文件内进行检查。 + +#### 代码示例 +以下提供一个或多个func_ret_null代码案例 + +在下面代码中`test`函数中调用`get_name`可能返回空指针,在后续使用`name`指针前应该判断是否为空指针 +``` +// name.hpp + +char* get_name(int id) { + char* name = 0; + if (id == 1) { + name = "Zeus"; + } else if (id == 2) { + name = "Hades" + } else { + return nullpointer; + } + return name; +} + +void test(int i) { + char* name = get_name(i); + dosomething(name); // defect: name may nullpointer should check it + if (name != nullpointer) { + dosomething(name); // good + } +} + +``` + +在选用full_ret_null_full时,将会全局分析函数`get_name`调用情况 +``` +// third.cpp +# include "name.h" + +void name_test(int i) { + char* name = get_name(i); + dosomething(name); // defect +} +``` + +#### use_after_free + +use_after_free 检查当指针已经被释放但在后续仍然使用该指针的情况。 + +##### 代码示例 +以下提供一个或多个use_after_free代码案例 + +通常情况下已经释放的指针只允许置空或重新指向新的值,不允许存在读取或作为函数参数使用。 +``` + +void UAR() { + int* p = new int[]; + p = get_array(); + dosomething(p); + delete p; + p = NULL; // allow + p = get_array(); // allow: get array again + delete p; + dosomething(p); // defect: use as param + std::cout << "p[0] = " << p[0] << std::endl; // defect: read p +} +``` + +#### forward_null +forward_null 检查可能导致程序终止或运行时异常的错误。它查找指针或引用被检查是否为 null 或被赋值为 null,且之后被解引用的很多情况。 + + +##### 规则参数 +- trust_param True or False 默认为 True + +##### 代码示例 +以下提供一个或多个forward_null代码案例 + +指针曾经有过检查null的操作则会视为有可能为空指针,之后在未被确认为非空指针情况下直接使用。将会视为`forward_null`错误 +``` +void forward_null_1() { + int * p; + p = get_int_pointer(); + dosomething(p); + if (p == NULL) { + std::cout << "Null Pointer Find" << srd::endl; + // return; // prefer: if return here + } else { + dosomething(p); // good: p is not NULL + } + dosomething(p); // defect forward_null: p may NULL +} + + +void forward_null_2(int *p) { + dosomething(p); + if (p == NULL) { + return; + } else { + dosomething(p); // good: p is not NULL + } + dosomething(p); // good + ... + if (p != NULL) { // means p may nullpointer here + dosomething(p); + } + dosomething(p); // defect forward_null:p may NULL +} +``` + +以下案例在设置`trust_param`为`False`时将会生效,其将会默认认为函数参数存在空指针可能,必须确认无空指针可能时方可使用 +``` +void forward_null_2(int *p) { + dosomething(p); // defect: param p may NULL + if (p != NULL) { // means p may nullpointer here + dosomething(p); + } + dosomething(p); // defect forward_null:p may NULL +} +``` + +#### reverse_null +reverse_null 检查已经使用过指针,但在后续又对指针进行了判空操作;会被认为之前使用指针也有可能是空指针。 + +##### 代码示例 +以下将提供一个或多个reverse_null案例 + +``` +void reverse_null(int *p) { + dosomething(p); // use p + if (p != NULL) { // defect reverse_null: It means p may NULL + dosomething(p); + } + ... +``` + +#### glob_null_pointer +glob_null_pointer 检查文件内全局指针是否为空,指针赋值将会被认为不为空指针,但检测到空指针判断则视为指针此时可能为空,如果在可能为空时使用则会报错 + +##### 代码示例 +以下将提供一个或多个glob_null_pointer案例 + +``` +int *p; + + +void thread1() { + p = get_int_pointer(); // p is not NULL + dosomething(p); // good + if (p != NULL) { + something(p); // good + } + something(p); // defect: p may NULL, because check p before +} + + +void thread2() { + *p = 6; // defect: p may NULL + if (p != NULL) { + something(p); // good + } + something(p); // defect: p may NULL +} +``` + +## 函数重写 +包含规则 +- function_override + +仅类虚拟函数允许重写。 + +#### function_override +function_override 检查非虚拟函数重写的情况。 + +##### 代码示例 +以下提供一个或多个function_override代码案例 + +``` + + +class father{ + public: + father(){}; + ~father(){}; + + private: + virtual void test(){}; + void test2(){ std::cout<<"hello";}; +}; + +class man{}; + + +class son: public father, public man{ + public: + son(){}; + ~son(){}; + private: + void test(){ std::cout<<"hello";}; // allow: virtual function override + void test2(){ std::cout<<"hello";}; // defect: bad override +}; +``` + diff --git "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md" "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" similarity index 100% rename from "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md" rename to "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" From 46dd0dbf975a4daae2e29af0e704cb2fe169b56c Mon Sep 17 00:00:00 2001 From: owlmk Date: Mon, 26 Sep 2022 14:37:56 +0800 Subject: [PATCH 2/2] =?UTF-8?q?:art:=E8=A7=84=E5=88=99=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TCA-Armory-Q1.md" | 44 ++++++++++++++++++- .../cpp_doc.md" | 13 ++++++ .../enhanced_safety_java.md" | 0 3 files changed, 55 insertions(+), 2 deletions(-) rename "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" => "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\345\267\245\345\205\267/TCA-Armory-Q1.md" (89%) create mode 100644 "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/cpp_doc.md" rename "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" => "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md" (100%) diff --git "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\345\267\245\345\205\267/TCA-Armory-Q1.md" similarity index 89% rename from "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" rename to "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\345\267\245\345\205\267/TCA-Armory-Q1.md" index 4d3452e62..ae37badbc 100644 --- "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/cpp_doc.md" +++ "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\345\267\245\345\205\267/TCA-Armory-Q1.md" @@ -1,5 +1,45 @@ -# 规则案例Cpp篇 -[TOC] + +- [TCA-Armory-Q1工具介绍](#tca-armory-q1工具介绍) +- [规则详情](#规则详情) + - [线程锁检查](#线程锁检查) + - [missing_lock](#missing_lock) + - [代码示例](#代码示例) + - [dead_lock](#dead_lock) + - [规则参数](#规则参数) + - [代码示例](#代码示例-1) + - [资源泄漏检查](#资源泄漏检查) + - [resource_leak](#resource_leak) + - [代码示例](#代码示例-2) + - [TODO](#todo) + - [无效值检查](#无效值检查) + - [unused_value](#unused_value) + - [代码示例](#代码示例-3) + - [数组溢出检查](#数组溢出检查) + - [array_overflow](#array_overflow) + - [代码示例](#代码示例-4) + - [TODO](#todo-1) + - [buff_overflow](#buff_overflow) + - [代码示例](#代码示例-5) + - [指针检查](#指针检查) + - [func_ret_null](#func_ret_null) + - [代码示例](#代码示例-6) + - [use_after_free](#use_after_free) + - [代码示例](#代码示例-7) + - [forward_null](#forward_null) + - [规则参数](#规则参数-1) + - [代码示例](#代码示例-8) + - [reverse_null](#reverse_null) + - [代码示例](#代码示例-9) + - [glob_null_pointer](#glob_null_pointer) + - [代码示例](#代码示例-10) + - [函数重写](#函数重写) + - [function_override](#function_override) + - [代码示例](#代码示例-11) +# TCA-Armory-Q1工具介绍 +TCA-Armory-Q1, 又名 tca_ql_cpp 主要用于分析Cpp质量问题。 + + +# 规则详情 ## 线程锁检查 diff --git "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/cpp_doc.md" "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/cpp_doc.md" new file mode 100644 index 000000000..98a4c09d7 --- /dev/null +++ "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/cpp_doc.md" @@ -0,0 +1,13 @@ +# Cpp代码质量缺陷规则包 +采用自研工具检查Cpp代码缺陷,需要使用license;属于 TCA 增强分析模块的能力之一,请参考[增强分析模块部署](https://tencent.github.io/CodeAnalysis/zh/quickStarted/enhanceDeploy.html)文档进行部署。 + + +## 规则 + +- [array_overflow](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#array_overflow) +- [buff_overflow](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#buff_overflow) +- [dead_lock](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#dead_lock) +- [func_ret_null](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#func_ret_null) +- [missing_lock](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#missing_lock) +- [resource_leak](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#resource_leak) +- [unused_value](https://tencent.github.io/CodeAnalysis/zh/guide/代码检查/工具/TCA-Armory-Q1.html#unused_value) \ No newline at end of file diff --git "a/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" "b/doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md" similarity index 100% rename from "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231/enhanced_safety_java.md" rename to "doc/zh/guide/\344\273\243\347\240\201\346\243\200\346\237\245/\350\247\204\345\210\231\345\214\205/enhanced_safety_java.md"