3

Clang Static Analyzer (5) Clangsa

 1 year ago
source link: https://ost.51cto.com/posts/19892
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

#盲盒+码# Clang Static Analyzer (5) Clangsa 原创 精华

【本文正在参加「盲盒」+码有奖征文活动】https://ost.51cto.com/posts/19288

Clang Static Analyzer (5) Clangsa

接着本系列之前的文章,补充下CodeChecker的部分用法。使用命令CodeChecker analyzers可以查看当前支持的静态分析检查器analyzers。如果安装了Cppcheck就会展示出来,clangsa和Clang-tidy是LLVM Clang编译工具链提供的静态分析检查器。

zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ CodeChecker analyzers
 cppcheck
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ export PATH=~/openharmony/prebuilts/clang/ohos/linux-x86_64/llvm/bin:$PATH
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ CodeChecker analyzers
 clangsa
 cppcheck
 clang-tidy

在使用CodeChecker静态分析时,可以使用选项[--analyzers ANALYZER [ANALYZER ...]指定只使能部分分析器,这些ANALYZER可以是clangsacppcheckclang-tidy。如下:

CodeChecker analyze --analyzers clangsa compile_commands.json -o ./reports

系列文章中的前几篇中,已经到了Cppcheck和Clang-tidy这2个静态检查分析器,本文快速介绍下clangsa静态检查分析器。

1、clangsa介绍

前面系列文章介绍了Clang Static Analyzer,知道Clang 静态分析器CSA是一个源代码分析工具,可查找 C、C++ 和 Objective-C 程序的bugs。Clang Static Analyzer可以理解提供了两部分内容,一部分是CSA工具,比如scan-build、CodeChecker,还有一部分是clangsa,虽然是Clang Static Analyzer的缩写,但是指的是Clang Static Analyzer的静态检查分析器,定义了一些的代码缺陷检查规则。详细可以参考链接https://clang.llvm.org/docs/ClangStaticAnalyzer.html,除了支持的检查器,还有用户文档和开发文档,用于支持定制自己的检查器。

本文主要学习下clangsa支持的检查器Available Checkers

clangsacppcheckclang-tidy属于分析器analyzer,每个分析器定义了自己的一套检查器“checkers”。检查器还会通过分组进行管理。clangsa分析器的检查器分为三组,分别为:

  • Default Checkers 默认检查器,可以发现安全和API使用缺陷,死代码和其他逻辑错误。。请参阅下面的默认检查器列表。
  • Experimental Checkers 实验检查器也称为alpha checkers,处于开发过程中,默认关闭。可能会崩溃,也可能产生较多的误报。
  • Debug Checkers 调测检查器被分析器开发者用作调测目的

2、Default Checkers 默认检查器

默认检查器分为如下几组。只关注C、C++相关的检查器,osx、Fuchsia、nullability、WebKit可以自行按需了解。

  • core 核心语言特性,通用的检查器
  • cplusplus C++语言检查器
  • deadcode 死代码检查器
  • nullability Objective-C空指针传递和解引用检查器
  • optin 可移植、性能或编程风格相关的检查器
  • security 安全相关检查器
  • unix POSIX/Unix检查器
  • osx macOS检查器
  • Fuchsia Fuchsia操作系统相关的检查器。Fuchsia是Google开发的一款开源操作系统。
  • WebKit WebKit相关检查器。WebKit是开源Web浏览器引擎。

2.1 core检查器

core检查器关注语言的核心特性,包含通用目的的检查器,例如除零错误,空指针解引用,使用未初始化的值等。这些检查器需要一直开启,其他检查器依赖这些核心检查器。

  • core.CallAndMessage (C, C++, ObjC)
    检查函数调用的逻辑错误,Objective-C消息表达错误,例如,未初始化参数,空函数指针等。适用于C, C++, ObjC等编程语言。
//C
void test() {
   void (*foo)(void);
   foo = 0;
   foo(); // warn: function pointer is null 函数指针为空
 }
 // C++
 class C {
 public:
   void f();
 };
 void test() {
   C *pc;
   pc->f(); // warn: object pointer is uninitialized 对象指针未初始化
 }
 // C++
 class C {
 public:
   void f();
 };
 void test() {
   C *pc = 0;
   pc->f(); // warn: object pointer is null 对象指针为空
 }
 // Objective-C
......
  • core.DivideZero (C, C++, ObjC)
    检查是否存在除零错误。

    void test(int z) {
    if (z == 0)
      int x = 1 / z; // warn 除零错误
    }
    void test() {
    int x = 1;
    int y = x % 0; // warn 除零错误
    }
    
  • core.NullDereference (C, C++, ObjC)
    检查空指针的解引用错误。SuppressAddressSpaces选项会屏蔽带地址空间的空指针解引用告警。可以使用选项-analyzer-config core.NullDereference:SuppressAddressSpaces=false来关注该SuppressAddressSpaces。默认是开启的。

  // C
  void test(int *p) {
  if (p)
    return;

  int x = p[0]; // warn
  } 

// C
void test(int *p) {
  if (!p)
    *p = 0; // warn
}

// C++
class C {
public:
  int x;
};

void test() {
  C *pc = 0;
  int k = pc->x; // warn
}

// Objective-C
......
  • core.StackAddressEscape ©
    检查堆栈越界,在一个函数内部声明的局部变量存储在栈空间上,不能把该变量的地址传递到函数外部。
char const *p;

void test() {
  char const str[] = "string";
  p = str; // warn StackAddressEscape告警
}

void* test() {
   return __builtin_alloca(12); // warn StackAddressEscape告警
}

void test() {
  static int *x;
  int y;
  x = &y; // warn StackAddressEscape告警
}
  • core.UndefinedBinaryOperatorResult ©
    检查二元表达式中的未定义的值。
void test() {
int x;
int y = x + 1; // warn: left operand is garbage
}
  • core.VLASize ©
    检查变长数组(Variable Length Arrays,VLA)的长度未定义或者零值。
void test() {
  int x;
  int vla1[x]; // warn: garbage as size
}

void test() {
  int x = 0;
  int vla2[x]; // warn: zero size
}
  • core.uninitialized.ArraySubscript ©
    检查未初始化的值用作数组下标。
void test() {
  int i, a[10];
  int x = a[i]; // warn: array subscript is undefined
}
  • core.uninitialized.Assign ©
    检查使用未初始化的值进行赋值。
void test() {
  int x;
  x |= 1; // warn: left expression is uninitialized 左表达式未初始化
}
  • core.uninitialized.Branch ©

检查未初始化的值用于条件分支。

void test() {
  int x;
  if (x) // warn
    return;
}
  • core.uninitialized.CapturedBlockVariable ©
    检查代码块捕获未初始化的值。
void test() {
  int x;
  ^{ int y = x; }(); // warn
}
  • core.uninitialized.UndefReturn ©
    检查未初始化的值传递到外层函数。
int test() {
  int x;
  return x; // warn
}

2.2 cplusplus检查器

C++语言相关的检查器。

  • cplusplus.InnerPointer (C++)

    检查在re/deallocation之后使用的C容器的内部指针。C标准库中的许多容器方法会让指向容器元素的引用无效,包含实际引用,迭代器,原生指针(raw pointers)。使用无效的引用会引起未定义行为,这通常是C++中内存错误的源头,也是该检查致力于检查出来的代码缺陷。

    该检查器只是适合std::string对象,不能识别出复杂的用法,比如传递std::string_view这样的unowned指针。

void deref_after_assignment() {
  std::string s = "llvm";
  const char *c = s.data(); // note: pointer to inner buffer of 'std::string' obtained here
  s = "clang"; // note: inner buffer of 'std::string' reallocated by call to 'operator='
  consume(c); // warn: inner pointer of container used after re/deallocation
}

const char *return_temp(int x) {
  return std::to_string(x).c_str(); // warn: inner pointer of container used after re/deallocation
  // note: pointer to inner buffer of 'std::string' obtained here
  // note: inner buffer of 'std::string' deallocated by call to destructor
}
  • cplusplus.NewDelete (C++)

    检查重复释放double-free和释放后使用UAF等内存问题。追踪被new/delete管理的内存。

void f(int *p);
void testUseMiddleArgAfterDelete(int *p) {
 delete p;
 f(p); // warn: use after free 释放后使用
}

class SomeClass {
public:
 void f();
};

void test() {
 SomeClass *c = new SomeClass;
 delete c;
 c->f(); // warn: use after free 释放后使用
}

void test() {
 int *p = (int *)__builtin_alloca(sizeof(int));
 delete p; // warn: deleting memory allocated by alloca
           // 释放使用alloca申请的内存
}

void test() {
 int *p = new int;
 delete p;
 delete p; // warn: attempt to free released 重复释放内存
}

void test() {
 int i;
 delete &i; // warn: delete address of local 释放局部变量的栈内存
}

void test() {
 int *p = new int[1];
 delete[] (++p);
   // warn: argument to 'delete[]' is offset by 4 bytes
   // from the start of memory allocated by 'new[]'
   // 传递给'delete[]'的参数相对'new[]'申请的内存开始地址进行了4字节偏移
   // 释放的不是申请的内存。
}
  • cplusplus.NewDeleteLeaks (C++)
    检查内存泄露memory leaks. 追踪new/delete管理的内存。
void test() {
  int *p = new int;
} // warn 申请未释放
  • cplusplus.PlacementNewChecker (C++)
    Check if default placement new is provided with pointers to sufficient storage capacity. 检查缺省的定位new(placement new)指针操作,有足够的存储能力。
#include <new> 

void f() {
  short s;
  long *lp = ::new (&s) long; // warn
}
  • cplusplus.SelfAssignment (C++)
    检查C++的自赋值的copy 和 move赋值操作。

  • cplusplus.StringChecker (C++)
    检查 std::string 操作。检查从std::string对象构造的cstring是否为空NULL。如果检查器不能推断出该指针为空,会假设其非空,用于满足构造。

该检查器可以检查SEI CERT C++编程规则STR51-CPP。不要从空指针创建std::string对象。

#include <string>

void f(const char *p) {
  if (!p) {
    std::string msg(p); // warn: The parameter must not be null
  }
}

2.3 deadcode检查器

  • deadcode.DeadStores ©
    检查存储在变量里的值后续未被使用。

    void test() {
    int x;
    x = 1; // warn
    }
    

    WarnForDeadNestedAssignments选项会使能检查器来检测嵌套的无用的赋值代码,可以使用选项-analyzer-config deadcode.DeadStores:WarnForDeadNestedAssignments=false来关闭该选项。WarnForDeadNestedAssignments`选项默认开启。

会对这样的代码进行告警,例如: if ((y = make_int())) { }

2.4 security检查器

  • security.FloatLoopCounter ©
    浮点数作为循环变量 (CERT: FLP30-C, FLP30-CPP).
void test() {
 for (float x = 0.1f; x <= 1.0f; x += 0.1f) {} // warn
}
  • security.insecureAPI.UncheckedReturn ©
    Warn on uses of functions whose return values must be always checked.
void test() {
  setuid(1); // warn
}
  • security.insecureAPI.bcmp ©
    Warn on uses of the ‘bcmp’ function.

    void test() {
    bcmp(ptr0, ptr1, n); // warn
    }
    
  • security.insecureAPI.bcopy ©
    Warn on uses of the ‘bcopy’ function.

    void test() {
    bcopy(src, dst, n); // warn
    }
    
  • security.insecureAPI.bzero ( C )
    Warn on uses of the ‘bzero’ function.

    void test() {
    bzero(ptr, n); // warn
    }
    
  • security.insecureAPI.getpw ©
    Warn on uses of the ‘getpw’ function.

    void test() {
    char buff[1024];
    getpw(2, buff); // warn
    }
    
  • security.insecureAPI.gets ©
    Warn on uses of the ‘gets’ function.

    void test() {
    char buff[1024];
    gets(buff); // warn
    }
    
  • security.insecureAPI.mkstemp ©
    Warn when ‘mkstemp’ is passed fewer than 6 X’s in the format string.

    void test() {
    mkstemp("XX"); // warn
    }
    
  • security.insecureAPI.mktemp ©
    Warn on uses of the mktemp function.

    void test() {
    char *x = mktemp("/tmp/zxcv"); // warn: insecure, use mkstemp
    }
    
  • security.insecureAPI.rand ©
    Warn on uses of inferior random number generating functions (only if arc4random function is available): drand48, erand48, jrand48, lcong48, lrand48, mrand48, nrand48, random, rand_r.

    void test() {
    random(); // warn
    }
    
  • security.insecureAPI.strcpy ©
    Warn on uses of the strcpy and strcat functions.

    void test() {
    char x[4];
    char *y = "abcd";
    
    strcpy(x, y); // warn
    }
    
  • security.insecureAPI.vfork ©
    Warn on uses of the ‘vfork’ function.

    void test() {
    vfork(); // warn
    }
    
  • security.insecureAPI.DeprecatedOrUnsafeBufferHandling ©
    Warn on occurrences of unsafe or deprecated buffer handling functions, which now have a secure variant: sprintf, vsprintf, scanf, wscanf, fscanf, fwscanf, vscanf, vwscanf, vfscanf, vfwscanf, sscanf, swscanf, vsscanf, vswscanf, swprintf, snprintf, vswprintf, vsnprintf, memcpy, memmove, strncpy, strncat, memset

    void test() {
    char buf [5];
    strncpy(buf, "a", 1); // warn
    }
    

2.5 unix检查器

  • unix.API ©
    检查UNIX/POSIX函数调用:open, pthread_once, calloc, malloc, realloc, alloca。
// Currently the check is performed for apple targets only.
void test(const char *path) {
  int fd = open(path, O_CREAT);
    // warn: call to 'open' requires a third argument when the
    // 'O_CREAT' flag is set
}

void f();

void test() {
  pthread_once_t pred = {0x30B1BCBA, {0}};
  pthread_once(&pred, f);
    // warn: call to 'pthread_once' uses the local variable
}

void test() {
  void *p = malloc(0); // warn: allocation size of 0 bytes
}

void test() {
  void *p = calloc(0, 42); // warn: allocation size of 0 bytes
}

void test() {
  void *p = malloc(1);
  p = realloc(p, 0); // warn: allocation size of 0 bytes
}

void test() {
  void *p = alloca(0); // warn: allocation size of 0 bytes
}

void test() {
  void *p = valloc(0); // warn: allocation size of 0 bytes
}
  • unix.Malloc ©
    检查内存泄露,重复释放、释放后使用等内存代码缺陷。追踪被malloc()/free()管理的内存。
void test() {
  int *p = malloc(1);
  free(p);
  free(p); // warn: attempt to free released memory
}

void test() {
  int *p = malloc(sizeof(int));
  free(p);
  *p = 1; // warn: use after free
}

void test() {
  int *p = malloc(1);
  if (p)
    return; // warn: memory is never released
}

void test() {
  int a[] = { 1 };
  free(a); // warn: argument is not allocated by malloc
}

void test() {
  int *p = malloc(sizeof(char));
  p = p - 1;
  free(p); // warn: argument to free() is offset by -4 bytes
}

- unix.MallocSizeof (C)
Check for dubious malloc arguments involving sizeof.

void test() {
  long *p = malloc(sizeof(short));
    // warn: result is converted to 'long *', which is
    // incompatible with operand type 'short'
  free(p);
}
- unix.MismatchedDeallocator (C, C++)
Check for mismatched deallocators.

// C, C++
void test() {
  int *p = (int *)malloc(sizeof(int));
  delete p; // warn
}

// C, C++
void __attribute((ownership_returns(malloc))) *user_malloc(size_t);

void test() {
  int *p = (int *)user_malloc(sizeof(int));
  delete p; // warn
}

// C, C++
void test() {
  int *p = new int;
  free(p); // warn
}

// C, C++
void test() {
  int *p = new int[1];
  realloc(p, sizeof(long)); // warn
}

// C, C++
template <typename T>
struct SimpleSmartPointer {
  T *ptr;

  explicit SimpleSmartPointer(T *p = 0) : ptr(p) {}
  ~SimpleSmartPointer() {
    delete ptr; // warn
  }
};

void test() {
  SimpleSmartPointer<int> a((int *)malloc(4));
}

// C++
void test() {
  int *p = (int *)operator new(0);
  delete[] p; // warn
}

// Objective-C, C++
void test(NSUInteger dataLength) {
  int *p = new int;
  NSData *d = [NSData dataWithBytesNoCopy:p
               length:sizeof(int) freeWhenDone:1];
    // warn +dataWithBytesNoCopy:length:freeWhenDone: cannot take
    // ownership of memory allocated by 'new'
}
  • unix.Vfork ©
    检查vfork是否合适使用。

    int test(int x) {
    pid_t pid = vfork(); // warn
    if (pid != 0)
      return 0;
    
    switch (x) {
    case 0:
      pid = 1;
      execl("", "", 0);
      _exit(1);
      break;
    case 1:
      x = 0; // warn: this assignment is prohibited
      break;
    case 2:
      foo(); // warn: this function call is prohibited
      break;
    default:
      return 0; // warn: return is prohibited
    }
    
    while(1);
    }
    
  • unix.cstring.BadSizeArg ©
    检查传递给C字符串函数的size参数的错误模式。使用-Wno-strncat-size编译器选项屏蔽其他strncat-相关的编译告警。

    void test() {
    char dest[3];
    strncat(dest, """""""""""""""""""""""""*", sizeof(dest));
      // warn: potential buffer overflow
    }
    
  • unix.cstring.NullArg ©
    检查空指针作为参数传递给C字符串函数:strlen, strnlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcasecmp, strncasecmp, wcslen, wcsnlen。

    int test() {
    return strlen(0); // warn
    }
    

3、Experimental Checkers 实验检查器

3.1 alpha.clone检查器

  • alpha.clone.CloneChecker (C, C++, ObjC)
    检查类似的重复代码块。
void log();

int max(int a, int b) { // warn
  log();
  if (a > b)
    return a;
  return b;
}

int maxClone(int x, int y) { // similar code here
  log();
  if (x > y)
    return x;
  return y;
}

3.2 alpha.core检查器

简单看一个,其他更多检查器,可以自行访问官网文档https://clang.llvm.org/docs/analyzer/checkers.html#alpha-core。

  • alpha.core.C11Lock
    类似alpha.unix.PthreadLock,检查互斥锁的mtx_t mutexeslocking/unlocking的上锁和解锁。
mtx_t mtx1;

void bad1(void)
{
  mtx_lock(&mtx1);
  mtx_lock(&mtx1); // warn: This lock has already been acquired
}

4、参考站点

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK