c++代码逻辑规范
2018-01-11 00:00
274 查看
代码逻辑
5.1 没有对new作异常处理
5.1.1 概览
new操作符失败的时候有些编译器将会抛出异常如果没有对异常进行捕捉,或者没有使用new(std::nothrow)将会导致程序结果错误甚至崩溃。
5.1.2 代码示例
void BadNew()
{
char * p = new char[2147483645]; // 程序崩溃, 抛出std::bad_alloc
}
5.2 分配和释放函数不匹配
5.2.1 概览
资源一般有配对的分配函数和释放函数,但是如果分配函数和释放函数不配对,将会产生这个错误。当一个资源被不同的不兼容的函数分配和释放资源时,C和C++系统不会强制报错,但是不正确的API用法会导致内存泄漏或者内存崩溃。
5.2.2 代码示例
void mixedNewAndFree()
{
int *x = new int;
free(x); // 报错,应该使用’delete x’
}
// MS Windows API
HINTERNET hConn = InternetOpenUrl(hSession, url, headers, \
length, flags, context);
CloseHandle(hConn); // 报错,应该使用 ‘InternetCloseHandle(hConn)’
5.3 对象指针和对象数组指针混淆
5.3.1 概览
对象指针被误解为数组指针,这可能导致程序访问无效内存,从内存中读取不需要的信息或者往非意向的地址写数据,从而导致崩溃。ARRAY_VS_SINGLETON也表示:基类的数组指针被看成了继承类的数组指针。
5.3.2 代码示例
class Base
{
public:
int x;
};
class Derived : public Base
{
public:
int y;
};
void f(Base *b)
{
b[1].x = 4;// b[1] 表示 b+1,表示address(b) + sizeof(Base)
}
Derived arr[3];// 实际上此数组对象大小为sizeof(Derived),大小不匹配,错误
f(arr); // 缺陷
void foo(char **result)
{
result = (char)malloc(80);
if (…)
{
strcpy(*result, “some result string”);
}
else
{
…
result[79] = 0; // 报错,应该是”(*result)[79] = 0”
}
}
void bar()
{
char *s;
foo(&s); // 这里错误
}
5.4 断言中的参数表达式有副作用
5.4.1 概览
有副作用的表达式作为断言的条件表达式。这种情况是很危险的,因为断言经常在发布版本中不编译,这意味着程序在debug模式和发布模式下行为是不同的。
5.4.2 代码示例
if (!flags & FLAG) // Defect: always yields 0
if (a == b ? 1 : 2)
5.24 赋值粘贴错误
5.24.1 概览
复制粘贴代码时修改变量不全。
5.24.2 代码示例
class CopyPasteError {
int square(int x) {
return x*x;
}
int example(int a, int b, int x, int y) {
int result = 0;
if (a > 0) {
result = square(a) + square(x);
}
if (b > 0) {
// “square(a)” should read “square(b)”
result = square(a) + square(y);
}
return result;
}
};
5.25 有赋值构造函数缺少赋值操作符
5.25.1 概览
一个类有复制构造函数但是却没有赋值操作符。此时只会进行浅拷贝,将会出现多个指针共用一个内存的现象,会导致内存泄漏或崩溃。
5.25.2 代码示例
class MyString {
char *p;
public:
MyString(const char *s) : p(strdup(s)) {}
~MyString() {free(p);}
// copy constructor
MyString(const MyString &init) : p(strdup(init.p)) {}
// no assignment operator
const char *str() const {return p;}
operator const char *() const {return str();}
};
5.26 构造函数和析构函数有内存泄漏
5.26.1 概览
一个构造函数分配内存并且存储指针到成员变量,但是析构函数没有释放。
5.26.2 代码示例
struct A {
int *p;
A() { p = new int; }
~A() { /oops, leak/ }
};
5.27 不安全的IO流操作
5.27.1 概览
用了不安全的IO函数scanf, fscanf等等,可能导致内存溢出。
5.28 不安全的字符串函数
5.28.1 概览
使用了不安全的字符串函数可能导致内存溢出比如sprintf, sscanf, strcat, strcpy 等等。
5.29 加密算法太弱
5.29.1 概览
DC.WEAK_CRYPTO 表示:函数产生了不安全的伪随机序列数字。initstate, lcong48, rand, random, seed48, setstate, and [dejlmn]rand48。这些函数不应该使用在加密方面,因为太容易被破解。
5.30 无用代码
5.30.1 概览
表示函数中的有一些代码由于逻辑分支的原因永远没有到达。
5.30.2 代码示例
int deadcode_example1(int *p) {
if( p == NULL ) {
return -1;
}
}
void deadcode_example2(void *p) {
int c = ( p == NULL );
}
5.31 删除数组错误
5.31.1 概览
使用delete而不是delete[]来说释放一个对象数组的内存。 这样将会导致内存泄漏或者其他问题。
5.31.2 代码示例
void wrong_delete() {
char *buf = new char [10];
delete buf; // Defect: should be delete[] buf
}
struct auto_ptr {
auto_ptr():ptr(0){}
~auto_ptr(){delete ptr;}
int *ptr;
};
void test() {
auto_ptr *arr = new auto_ptr[2];
arr[0].ptr = new int(0);
arr[1].ptr = new int(1);
delete arr; // Memory leak, destructors are not called (or worse!)
}
5.32 删除void*指针错误
5.32.1 概览
指向void的指针被delete。当delete一个void指针时,该指针本身的内存被释放掉了,但是任何与动态类型相关联的析构函数都没有被执行。当析构函数执行一些比较重要的事情比如释放内存,此种情况是一个严重缺陷。
在很多实现中,delete一个void指针被看作是调用free(),因为内存被释放掉了,但是析构函数没有执行。然而,即使这是故意的行为,也是十分危险的,原因如下:
• 语言标准没有定义这个行为。具体实现从技术角度上说可以执行任何事情,一些编译器做了一些优化,粗暴地改变了依赖于未定义行为的代码。
• 如果分配位置被更改,例如,分配对象数组内存,那么释放内存将会默默地做错误的事情。
5.32.2 代码示例
void buggy(void *p)
{
delete p;
}
5.33 除数为0
5.33.1 概览
一个算术除法或者取模算法时当除数为0的时候。这种运算是未定义的,并且通常会导致程序终结。如果证明一个除数会在特定的情况下为0,那么检测器将会报告错误。
5.33.2 代码示例
int foo() {
int x = 0;
if (cond()) {
x = 1;
}
return 1 / x;
}
int foo(int y) {
if (y < 0) {
return 0;
}
return y;
}
void bar(int y) {
int z = 1 / foo(y);
}
5.34 枚举当做布尔
5.34.1 概览
枚举类型表达式不小心用于布尔内容中了。
5.34.2 代码示例
enum color {red, green, blue};
…
color c;
…
if (c) /* Why are green and blue distinguished from red? */
5.35 优先级顺序错误
5.35.1 概览
运算符优先级或者函数参数的顺序没有明确,导致执行动作随着不同的编译器而不同。
5.35.2 代码示例
int g(int x) {
return x + x++; // Defect, undefined evaluation order
}
int foo() {
int x = 0;
x = x++; // Defect
return x; // returns either 0 or 1
}
5.36 NULL指针使用
5.36.1 概览
表示一个指针为NULL或者未定义,但是后续在使用这个指针。
5.36.2 代码示例
int forward_null_example1(int *p) {
int x;
if ( p == NULL ) {
x = 0;
} else {
x = *p;
}
x += fn();
*p = x; // Defect: p is potentially NULL
return 0;
}
5.37 代码中有显示密码
5.37.1 概览
密码或者加密符号直接存储在源代码中。使用这种方式不安全。
5.37.2 代码示例
void test() {
char password[] = “ABCD1234!”;
HANDLE pHandle;
LogonUserA(“User”, “Domain”, password, 3, 0, &pHandle);
// HARDCODED_CREDENTIALS defect
}
5.38 相同的逻辑分支
5.38.1 概览
条件语句或者表达式执行相同的代码,而不管该条件是什么。这种重复代码隐含着条件是没有必要的(或者多个条件应该合并的)或者他们不应该是相同的(可能是拷贝错误)
5.38.2 代码示例
if (x==2) {
y=32;
z=y*2;
} else {
y=32;
z=y*2;
}
if (hasName(a)) {
name = getName(a);
return name;
}
name = getName(a);
return name;
5.39 不兼容的转换
5.39.1 概览
一个指针被强制转换为一个不兼容的指针。强制转换指针将会导致潜在的内存访问越界或者字节序列不对。
5.39.2 代码示例
void init(uint32_t *x) {
*x = 42;
}
void foo() {
uint16_t y;
init((int32_t*) &y); // out-of-bounds memory access
}
5.40 循环无法停止
5.40.1 概览
循环不会终止,循环条件可能永远为true。
5.40.2 代码示例
void foo(int x)
{
int i=0;
while (true) {
if (i >= 10) {
if (x == 55) { // x is never updated
break;
}
}
i++;
}
}
char c = foo();
while (c != EOF) {
if (c == 0x1c) {
found = 1;
} else {
if (found)
return -1;
else
continue;
}
…
}
5.41 整型溢出
5.41.1 概览
整型数据在进行算术运算时导致溢出或者截断的情况。
5.41.2 代码示例
public:
int a;
int *b;
};
void test(int x, int fd) {
int y;
read(fd, &y, 4); // y is from a tainted (outside) source
int size = y;
Cell *mycell;
if (size != 0) {
// Overflow results from operation size * sizeof(Cell)
// Overflowed value is used in memory allocation
mycell = new Cell[size]; // overflow and overflow_sink events
}
}
5.42 无效的迭代器
5.42.1 概览
使用了一个无效的迭代器,进行STL中的下标运算。
5.42.2 代码示例
void wrong_erase(list &l, int v) {
list::iterator i = l.begin();
for(; i != l.end(); ++i) { /* Defect: “i” is incremented
after invalidation
by a call to “erase” */
if(*i == v)
l.erase(i);
}
}
int deref_end(list &l) {
list::iterator i = l.end();
int x = *i; // Defect: dereferencing past-the-end
}
5.43 锁使用错误
5.43.1 概览
一个锁申请了但是没有释放,或者申请了两词,但是没有释放。这种情况一般是由于错误的返回分支导致的,这种情况将会导致死锁。
5.43.2 代码示例
int missing_unlock( struct info *data, int num ) {
spin_lock(data->lock); // +lock
}
fn() {
for (i = 0; i < 10; i++) {
lock(A); // +lock, +double_lock
}
fn(L l) {
lockassert(l);
// …
}
caller() {
lock(A);
unlock(A); // +unlock
fn(A); // +lockassert
}
5.44 不匹配的迭代器
5.44.1 概览
一个STL迭代器传给另外一个不同的迭代器或者一个迭代器与另外一个不同类型的迭代器进行比较。
5.44.2 代码示例
void test(vector &v1, vector &v2) {
vector::iterator i = v1.begin();
// Defect: Uses “i” from “v1” in a method on “v2”
v2.erase(i);
}
void test(list &l1, list &l2) {
list::iterator i = l1.begin();
l2.splice(l2.begin(), l1);
// Defect: i belonged to l1 but was transferred to l2 with “splice”
l1.erase(i);
}
void test(list &l1, list &l2) {
// Error: comparing “i” from “l1” with “l2.end()”
for(list::iterator i = l1.begin(); i != l2.end(); ++i){}
}
5.45 错误的隐式转换
5.45.1 概览
MISRA_CAST 表示一些隐式类型转换,比如把一个float类型隐式转换为int类型。
5.45.2 代码示例
const int32_t xs32a = 0, xs32b = 1;
static void non_compliant1()
{
(void)(float64_t)(xs32a / xs32b); // Defect: Casting complex expression with
// integer type to a non-integer
// type float64_t
}
float32_t f32a;
float64_t f64a;
int16_t compliant()
{
12c4c
f32a = 2.5F;
f64a = f64b + f32a;
}
static void non_compliant1()
{
f32a = f64a; // Defect, casting complex expression to narrower type
}
extern float32_t f32a, f32b;
static void non_compliant1()
{
(void)(float64_t)(f32a + f32b); //Defect: Type 32-bit float_t cast
// to 64-bit float64_t
}
5.46 缺少break
5.46.1 概览
在switch表达式没有break分支,可能是编写者的遗漏。
5.46.2 代码示例
void doSomething(int what)
{
switch (what) {
case 1:
foo();
break;
}
5.47 缺少逗号
5.47.1 概览
两个字符串之间没有使用逗号,这样会导致这两个字符串变成了一个字符串。
5.47.2 代码示例
char* arr[] = {
“a string literal” //Defect here.
“another string literal”
};
char* arr[] = {
“a string literal” //Defect here.
};
5.48 缺少赋值或者拷贝构造函数
5.48.1 概览
MISSING_COPY_OR_ASSIGN 表示许多情况比如: 一个类中有动态申请的资源,但是该类没有拷贝构造函数,此时的拷贝构造函数会使用默认的浅拷贝构造函数,将会导致一系列的问题。
5.48.2 代码示例
class MyString {
char *p;
public:
// evidence of resource ownership
MyString(const char *s) : p(strdup(s)) {}
// further evidence of resource ownership
~MyString() {free(p);}
// no copy constructor at all
// no assignment operator at all
const char *str() const {return p;}
operator const char *() const {return str();}
};
class MyString {
char *p;
public:
MyString(const char *s) : p(strdup(s)) {}
// inadequate copy constructor
MyString(const MyString &init) : p(init.p) {}
~MyString() {free(p);}
// inadequate assignment operator
MyString &operator=(const MyString &rhs)
{
if (this != &rhs) {
p = rhs.p;
}
return *this;
}
};
5.49 缺少部分锁
5.49.1 概览
MISSING_LOCK 表示有些情况比如:一个对象多个地方都是由锁进行锁住的,但是有个别地方没有加锁,这将导致数据的并发安全性。
5.49.2 代码示例
struct bingo {
int bango;
lock bongo;
};
void example(struct bingo *b) {
lock(&b->bongo); // example_lock
b->bango++; // example_access
unlock(&b->bongo);
}
void lockDefect(struct bingo *b) {
b->bango = 99; // missing_lock
}
5.50 缺少移动赋值
5.50.1 概览
一个类使用了赋值操作符时使用的是拷贝但是没有使用移动赋值,这样会浪费很多资源。
5.50.2 代码示例
struct S { // missing_move_assignment event
S() {
p = new int(0);
}
S(const S &other) {
p = new int;
*p = *other.p;
}
~S() {
delete p;
}
S& operator=(const S &other) {
*p = *other.p;
return *this;
}
int *p;
};
}}
struct dtor_no_free { // No defect when report_no_dtor_free is false.
// Defect when report_no_dtor_free is true.
dtor_no_free() {}
dtor_no_free(const dtor_no_free &other): p(other.p) {}
dtor_no_free& operator=(const dtor_no_free &other) {
p = other.p;
return *this;
}
~dtor_no_free() {}
std::string p;
};
int main() {
dtor_no_free d;
d = dtor_no_free();
return 0;
}
5.51 未还原半途而废的函数动作
5.51.1 概览
一个全局变量在进行更改时,如果中间遇到错误之后没有还原。
5.51.2 代码示例
extern int refresh_mode;
void move(item_t *item)
{
int save_mode = refresh_mode;
refresh_mode = 0; /* reduce flicker */
if (!lock_for_move(item))
return; /* error: leaving ‘refresh_mode’ as 0 */
handle_move(item);
unlock_for_move(item);
refresh_mode = save_mode;
}
5.52 缺少函数返回值
5.52.1 概览
一个非void的函数没有返回值或者返回多个值。
5.52.2 代码示例
int fn(int x)
{
switch (x) {
case 5: return 4;
default: return 5;
}
// no return; but not a defect, since unreachable
}
int fn(int x) {
if (x == 5)
return 4;
else if (x == 3)
return 2;
} // missing_return
int fn(int x) {
if (x == 5)
return 4; // extra_return
else if (x == 3)
return 2; // extra_return
return 0; // extra_return
}
5.53 混淆的枚举值
5.53.1 概览
两个枚举定义的各种枚举值混用。
5.53.2 代码示例
enum e {E1, E2};
enum f {F0, F1, F2, F3};
…
void foo(e ee) {
switch (ee) {
case E1:
…
case F2: /* Defect */
…
}
}
void bar(int x) {
switch (x) {
case E1: /* Defect in conjunction with (see the second line that follows) … */
…
case F2: /* … this */
…
}
}
5.54 负整数的误用
5.54.1 概览
许多负整数的误用. 负整数以及函数返回值必须在使用之前进行判断 (例如, 数组下标,循环下标等)。
5.54.2 代码示例
void basic_negative() {
int buff[1024];
int x = some_function(); // some_function () might return -1.
buff[x] = 0; // Defect: buffer underrun at buff[-1]
}
void subtle_negative() {
unsigned x;
x = signed_count_func(); // Returns signed -1 on error.
// -1 cast to an unsigned 是一个 very large integer.
loop_with_param(x); // Uses x as an upper bound.
// Defect: loop might never end or last too long.
}
void another_subtle_negative(){
unsigned int c;
for (i = 0; (c=read(fd, buf, sizeof(buf)))>0; i+=c)
// read() returns -1 on error, c is now a very large integer
if (write(1, buf, c) != c) // Defect: Too many bytes written to stdout.
die(“Write call failed”);
}
5.55 缩进不匹配错误
5.55.1 概览
代码的缩进不匹配。经常这种情况是因为忘记加括号了。
5.55.2 代码示例
if (condition)
MULTI_STMT_MACRO(p->x, p->y); // bar(p->y)一定会执行
if (condition1)
if (condition2)
do_something();
else // “dangling”
do_something_else();
5.56 无意义的函数
5.56.1 概览
一些语句不完成任何事情,或者不是完成本意想做的事情。通常这种情况是由于笔误或者语法的误解,比如运算符优先级等。
5.56.2 代码示例
void array_null()
{
unsigned int a[3];
unsigned int b[1];
unsigned int c[2];
if (*a == 0)
a[0];
if (b == 0) // The entire array b is compared to 0.
b[1];
if (c[1] == 0)
c[1];
}
void bad_memset()
{
int *p;
memset(p, ‘0’, l); // Fill value is ‘0’, and 0 is more likely.
memset(p, l, 0); // Length is 0, and so likely that l and 0 reversed.
memset(p, 0xabcd, l); // Fill is truncated, and so memory
// will not contain the 0xabcd pattern.
}
• void extra_comma() {
• int a, b;
• for (a = 0, b = 0; a < 10, b < 10; a++, b++);
• // Extra comma, and so a < 10 is not used.
}
• void no_effect_test() {
• int a, b;
• a == b; // Test has no effect, and is
• // likely intended to be the assignment a = b
}
• int a, b;
• void bool_switch() {
• switch (a == b) { // Boolean switch
• case 1:
• }
}
• void incomplete_delete() {
• int *p, *q;
• delete p, q; // The pointer q is not deleted.
}
• void no_effect_deref() {
• int *p;
• *p++; // *p is useless
}
• void self_assign(struct foo *ptr) {
• a = a; // assignment to self, global
• ptr->x = ptr->x; // assignment to self, field
}
struct C { static void foo(); int x; };
C * c; c->foo(); // Becomes “c, C::foo();”. c is unnecessary,
// but not flagged.
C stackc; stackc.x = 4;
5.57 函数返回值为NULL
5.57.1 概览
函数的返回值为NULL,但是在使用返回值的时候没有加以判断。
5.57.2 代码示例
void bad_malloc() {
// malloc returns NULL on error
struct some_struct x = (struct some_struct) malloc(sizeof(*x));
// ERROR: memset dereferences possibly null pointer x
memset(x, 0, sizeof(*x));
}
5.58 文件权限不对
5.58.1 概览
OPEN_ARGS表示一些情况比如一个文件没有使用指定的访问权限被创建。
5.58.2 代码示例
void open_args_example() {
int fd = open(“file.log”, O_CREAT); // lacks a correct mode argument.
}
5.59 顺序相反错误
5.59.1 概览
程序需要一对锁但是在不同的地方这一对锁使用的顺序不同,这样将会导致死锁。
5.59.2 代码示例
// Thread one enters this 函数
void deadlock_partA(struct directory *a, struct file *b) {
}
// Thread two enters this 函数
void deadlock_partB( struct directory *a, struct file *b ) {
}
5.60 扩充数据精度丢失
5.60.1 概览
如果一个表达式准备进行扩充时但是没有显示强制转换,那么将会丢失一些数据信息导致错误。
5.60.2 代码示例
void foo() {
unsigned int x = 2147483648;
unsigned int y = 2;
unsigned long long z;
if ((x * y) == z) {
// Do something.
}
}
5.61 内存越界
5.61.1 概览
内存访问越界. 不合适的内存访问将会导致内存崩溃和进程崩溃以及安全漏洞问题。包括堆和栈的下标索引。
5.61.2 代码示例
void bad_heap() {
int buffer = (int ) malloc(10 * sizeof(int)); // 40 bytes
int i = 0;
for(; i <= 10; i++) { // Defect: writes buffer[10] and overruns memory
buffer[i] = i;
}
}
void test(int i) {
int n;
char *p = malloc(n);
int y = n; // Valid indices are buffer[0] to buffer[y - 1]
p[y] = ‘a’; // Defect: writing to buffer[y] overruns local buffer
}
struct s {
int a;
int b;
} s1;
void test() {
int n, i;
struct s p = malloc(n sizeof(struct s));
if (i <= n) // “i” can be equal to n
p[i] = s1; // Defect: overrun of buffer p
}
void access_dbuffer(int *x, int n) {
x[n-1] = 1;
}
void caller(int n) {
int array[10];
if (n < 100) {
access_dbuffer(array, n); // defect
}
}
void foo() {
int array[10];
int i = get();
if (i > 8) {
array[i] = 1;
}
}}
5.62 函数传值太大
5.62.1 概览
一些函数的入参太大(超过128个字节)。为了避免这种情况,不能之传值,只能传引用或者指针。
5.62.2 代码示例
struct big {
int a[20];
int b[20];
int c[20];
};
void test(big b) { // Warning: passing by value, 240 bytes
}
struct exn {
const char str[128];
int code;
};
void foo() {
try {
//…
} catch(exn e) { // Warning, catch by value, 132 bytes
//…
}
}
5.63 返回局部变量地址
5.63.1 概览
局部变量的地址被函数返回,但是该地址在函数返回之时已经失效,将会导致不可预测的错误。
5.63.2 代码示例
some_struct * basic_return_local(struct some_struct *b) {
struct some_struct a(*b); // a is copy-constructed onto the stack
return &a; // Returns a pointer to local struct a
}
5.64 NULL判断过晚
5.64.1 概览
一个对象或变量已经被使用之后,再去判断该变量是否为NULL或者是否有效,此时已经晚了。
5.64.2 代码示例
void basic_reverse_null(struct buf_t *request_buf) {
*request_buf = some_函数(); // Assignment dereference
if (request_buff == NULL) // NULL check AFTER dereference
return;
}
5.65 负数判断过晚
5.65.1 概览
一个对象或变量已经被使用之后,再去判断该变量是否为负数,此时已经晚了。
5.65.2 代码示例
void simple_reverse_neg(int some_signed_integer) {
some_struct *x = kmalloc(some_signed_integer, GFP_KERNEL); // Dangerous integer use
if (some_signed_integer < 0) // Check after use
return error;
}
5.66 有风险的加密
5.66.1 概览
加密算法十分的脆弱或者有风险。一般是使用了旧的有风险的加密算法。DES算法,RSG算法,ECB块模式,RC4都不建议使用。
5.66.2 代码示例
CryptDeriveKey(hCryptProv, CALG_DES, hHash, 0, &hKey));
5.67 使用废弃接口
5.67.1 概览
使用了C++明确要求废弃的函数接口。
5.67.2 代码示例
void secure_coding_example() {
char *d, *s, *p;
int x;
…
gets(p);
strcpy(d, s);
strncpy(d, s, x);
}
5.68 临时文件不安全
5.68.1 概览
一个临时文件以一个不安全的方式创建。
5.68.2 代码示例
void secure_temp_example() {
char *tmp, *tmp2, *tmp3;
char buffer[1024];
tmp = mktemp(buffer);
}
5.69 自我赋值无意义
5.69.1 概览
自己赋值给自己,这种情况毫无意义或者会产生错误。
5.69.2 代码示例
class SimpleString {
char *p;
public:
SimpleString(const char *s = “”) : p(strdup(s)) {}
SimpleString(const SimpleString &init) : p(strdup(init.p)) {}
~SimpleString() {free(p);}
SimpleString &operator=(const SimpleString &rhs)
{
free(p); // bad if &rhs == this
p = strdup(rhs.p); // use-after-free when &rhs == this
return *this;
}
const char *str() {return p;}
operator const char *() {return str();}
};
5.70 符号扩展溢出
5.70.1 概览
SIGN_EXTENSION表示在符号扩展的过程中导致数据溢出异常,导致非正确的数据结果。
5.70.2 代码示例
unsigned long readLittleEndian(unsigned char *p)
{
return p[0] |
(p[1] << 8) |
(p[2] << 16) |
(p[3] << 24);
}
int main()
{
unsigned char bytes[4] = { 0x03, 0x02, 0x01, 0x80 };
unsigned long result = readLittleEndian(bytes);
printf(“0x%lX\n”, result);
}
5.71 Sizeof不匹配
5.71.1 概览
sizeof关键字作用的对象和预期的对象大小不一致,导致对象大小计算错误。
5.71.2 代码示例
struct buffer {
char b[100];
};
void f() {
struct buffer buf;
memset(&buf, 0, sizeof(&buf)); /* Defect: should have been “sizeof(buf)” */
}
struct buffer {
char b[100];
};
void f() {
struct buffer p = (struct buffer )malloc(sizeof(struct buffer *));
/* Defect: should be “sizeof(struct buffer)” */
}
void f(void , void *, size_t);
void g() {
short s;
short *ps;
}
struct buffer {
char b[100];
};
void f(struct buffer *p) {
p += sizeof(struct buffer); /* Defect: “sizeof(struct buffer)” should be “1” */
}
struct buffer {
char b[100];
};
void f(struct buffer *p, struct buffer *q) {
if (q – p > 3 * sizeof(p)) / Defect: “* sizeof(p)” is extraneous /
printf(“q too far ahead of p\n”);
}
struct buffer {
char b[100];
};
struct buffer array[30];
void f(struct buffer *cur) {
size_t pos = (cur – array) / sizeof(struct buffer); /* Defect: “/ sizeof(struct buffer)” is extraneous */
}
5.72 在锁中睡眠
5.72.1 概览
在有锁的情况下使用sleep.这将会导致其他线程去争夺这个锁的资源,大大浪费了时间资源。
5.73 栈使用过大
5.73.1 概览
较大的栈空间使用(超过250K字节),或者单一对象超过10K字节。
5.73.2 代码示例
void stack_use_callee1(void) {
}
void stack_use_callee2(void) {
char buf[16384]; // Exceeds max single base use of 1024 bytes
}
void stack_use_callee3(void) {
char buf[20000]; // Exceeds max single base use of 1024 bytes
}
void stack_use_example(void) {
char buf[16384]; // Exceeds max single base usage of 1024 bytes
}
5.74 分号使用错误
5.74.1 概览
有些语句后面不该加分号但是加了,或者有些语句后面应该加分号,但是没加。
5.74.2 代码示例
if (condition);
do_something_conditionally();
while (condition);
{
if (other_condition)
return;
/* advance the loop */
}
/* count the elements in list ‘head’ */
for (count = 0, p = head; p != 0; ++count, p = p->next)
;
{
int local_variable
/* … */
}
DPRINT(“condition is true\n”);
5.75 格式化状态错误
5.75.1 概览
许多情况比如 一个输出流对象的格式化状态被修改了但是没有被还原. 在函数返回后,对该流的格式化输出可能会有非计划中的影响.
5.75.2 代码示例
void oops1(int i)
{
cout << hex << i;
}
You can fix this defect as follows:
void corrected1(int i)
{
cout << hex << i << dec;
}
void oops2(ostream &os, float f)
{
os << setprecision(2) << f;
}
5.76 非零字符串错误
5.76.1 概览
字符串是非零结尾的,但是却将该字符串发送给一个以零结尾的字符串接收函数中。
5.76.2 代码示例
char *string_null_example() {
char name[1024];
char *extension;
}
5.77 字符串溢出
5.77.1 概览
字符串处理函数在字符串结尾处写入数据。由于源字符串大于目的字符串导致的。
5.77.2 代码示例
void string_overflow_example() {
char destination_buffer[256];
char source_buffer[1024];
…
strcpy(destination_buffer, source_buffer);
}
5.78 字符串大小不对
5.78.1 概览
字符串处理函数在字符串结尾处写入数据。推荐使用字符串安全函数strncpy()等等。与字符串溢出的区别在于,它是显示的长度超出目的接收缓冲区。
5.78.2 代码示例
char *string_size_example() {
static char addr[100];
struct hostent *he;
he = gethostbyaddr(address, len, type);
strcpy(addr, he->h_name);
return addr;
}
5.79 参数顺序混乱
5.79.1 概览
函数的两个参数或者多个参数的填入顺序搞错,导致函数入参不正确,导致函数行为异常。
5.79.2 代码示例
void copy(int srcId, int dstId) { /* … */ }
void test() {
int srcId = 1;
int dstId = 2;
copy(dstId, srcId); /* Defect: arguments are swapped. */
}
5.80 数据污染
5.80.1 概览
一个数据本来是可信的,但是被一个不可信的数据污染掉了,此数据变成不可信数据。
5.80.2 代码示例
void tainted_scalar_example() {
int nresp = packet_get_int();
if (nresp > 0) {
}
5.81 字符串污染
5.81.1 概览
一个缓存本来是可信的,但是被一个不可信的数据污染掉了,此缓存变成不可信缓存。
5.81.2 代码示例
void tainted_string_example() {
char *request = packet_get_string();
if (!legal_request(request)) {
sprintf(error_msg, “Illegal request: %s”, request); /* sprintf()
transitively taints error_msg */
…
syslog(LOG_WARNING, error_msg);
}
}
5.82 文件访问不安全
一个文件一般是先判断是否可以访问,然后再去访问它,但是在这两步骤之间,可能攻击者已经将文件内容替换掉了,不安全。应该直接访问该文件,并修改权限。
5.82.1 代码示例
void toctou_example() {
stat(logfile, &st);
if (st.st_uid != getuid())
return -1;
open(logfile, O_RDWR);
}
5.83 未捕捉异常
5.83.1 概览
函数抛出了异常但是没有任何函数捕捉它,这样会导致整个进程崩溃。
5.83.2 代码示例
// Prototypical defect.
int main(){
throw 7;
return 0;
}
// A simple defect resulting from a 函数 call.
void fun() {
throw 7;
}
int main(){
fun();
return 0;
}
void fun() {
throw 7;
}
void cannot_throw() throw() {
fun();
}
class A {};
class B {};
class C {};
int main(){
try {
throw A();
} catch (B b){
} catch (C b){
}
return 0;
}
class A {};
int main() {
try {
throw A(); //Will not be caught.
} catch (…){
cerr << “Error” << endl;
throw;
}
}
5.84 敏感数据未加密
5.84.1 概览
像密码一样的敏感数据没有被加密掉,导致数据不安全。
5.84.2 代码示例
void test(int socket, char* password) {
}
5.85 变量未初始化
5.85.1 概览
代码中使用任何的未初始化的变量或资源。
5.85.2 代码示例
int uninit_example1(int c) {
int x;
if(c)
return c;
else
return x; // defect: “x” is not initialized
}
int result;
int uninit_example2(int c) {
int *x;
if(c)
x = &c;
use (x); // defect: uninitialized variable “x” and “*x” used in call
}
void use (int *x) {
result = *x+2;
}
int result;
int uninit_example3() {
int x[4];
result = x[1]; // defect: use of uninitialized value x[1]
}
int result;
struct A {
int a;
int *b;
};
int uninit_example4() {
struct A *st_x;
st_x = malloc (sizeof(struct A)); // Dynamically allocate struct
partially_init(st_x);
use (st_x); // defect: use of uninitialized variable st_x->b
}
void partially_init(struct A *st_x) {
st_x->a = 0;
}
void use (struct A *st_x) {
result = *st_x->b;
}
5.86 没有构造函数
5.86.1 概览
类的非静态的成员变量没有被初始化。
5.86.2 代码示例
class Uninit_Ctor_代码示例1 {
Uninit_Ctor_代码示例1(int a) : m_a(a) {
// Defect: m_p not initialized in constructor
}
};
class Uninit_Ctor_代码示例2 {
Uninit_Ctor_代码示例2(int a) : m_a(a) {
init();
// Defect: m_c not initialized in constructor
}
void init() {
m_b = 0;
}
int m_a, m_b, m_c;
};
class HasCtor {
int m;
public:
HasCtor() : m(0) {}
};
class HasOnlyGenCtor : public HasCtor {
int *p;
};
5.87 整数除法精度丢失
5.87.1 概览
使用整型进行除法的时候精度丢失,如果本意是赋值给浮点型的话,那么应该先转为浮点型再进行除法运算。
5.87.2 代码示例
// Sets PI_APPROX to 3.0!
double PI_APPROX = 22 / 7; // Defect here.
// Rounds toward zero (and adds 1 to negative results)!
int roundedAverage(int a, int b) {
return (int)(0.5 + ((a + b) / 2)); // Defect here.
}
5.88 代码不可到达
5.88.1 概览
控制流不能到达代码的特定地区,或者到达了特定地区但是不是程序员想要的。
5.88.2 代码示例
bool forgiving = false; // Start less flexible.
do {
if (!tryIt(forgiving) && !forgiving) {
forgiving = true;
continue; // [intend to] Try again, more flexibly.
}
} while (false); // [The loop will never proceed past this point.]
int unreachable_example (int *p) {
if( p == NULL ) /{/
handle_error();
return -1;
/}/
use_p( *p ); //An UNREACHABLE defect here.
return 0;
}
int unreachable_example2 (int array[10]) {
int i;
int value = -1;
for( i = 0; i < 10; i++ ) { //An UNREACHABLE defect here:
// Increment is unreachable. Array
// not properly searched because the break
// statement is executed on the first iteration.
if( array[i] > 100 ) /{/
value = array[i];
break;
/}/
}
return value;
}
5.89 定义变量但未使用
5.89.1 概览
一个变量被定义了,但是从来没有使用过,这样会浪费代码资源并且产生误解。
5.89.2 代码示例
const char* get_capital_city(const char *country)
{
const char *result = 0;
if (strcmp(country, “Argentina”) == 0) {
result = “Buenos Aires”; // Assigned value will never be used.
} else if (strcmp(country, “Italy”) == 0) {
result = “Rome”; // Assigned value will never be used.
} if (strcmp(country, “China”) == 0) { // Should be ‘else if’ here.
result = “Beijing”;
} else {
result = “Unknown”; // This will overwrite values
// “Buenos Aires” and “Rome”.
}
return result;
}
5.90 无效的函数调用
5.90.1 概览
一个函数没有起到作用,或者是用户没有对唯一起作用的返回值做判断,或者是用户没有对参数的返回值作任何判断和处理。
5.90.2 代码示例
struct pair_t
{
pair_t(int x, int y) : x_(x), y_(y) {}
int x_;
int y_;
};
pair_t swap(pair_t xy)
{
return pair_t(xy.y_, xy.x_);
}
void incorrect()
{
…
swap(xy); /* Defect: swap does not modify its argument */
…
}
5.91 系统调用用户指针
5.91.1 概览
系统直接调用用户级别的指针,这样会导致系统崩溃。
5.91.2 代码示例
void user_pointer_example() {
error = copyin((void *)p->p_sysent->sv_psstrings, &pstr, sizeof(pstr));
if (error)
return (error);
for (i = 0; i < pstr.ps_nargvstr; i++) {
sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
sbuf_printf(sb, “%c”, ‘\0’);
}
}
5.92 释放资源后使用
5.92.1 概览
内存或者资源在被释放掉后进行使用。这种情况几乎每次都是内存崩溃。
5.92.2 代码示例
void fun(int * p) {
free (p);
int k = *p; // Defect
}
int f(void *p) {
if(some_error()) {
free(p);
return -1;
}
return 0;
}
void g() {
void *p = malloc(42);
if(f(p) < 0) {
free(p); // Double free
}
use(p);
}
void use_after_free(struct S *p) {
free(p);
free(p->field); // Dereference
}
int f(int i) {
int *p = malloc(8);
free (p);
int res = p[i];
}
extern int ext(int *p);
void fun() {
int * p = malloc(100);
free(p); // Pointer freed
ext(p); // Pointer used as arg
}
5.93 可变参数使用错误
5.93.1 概览
va_start or va_copy后面必须有va_end且必须在va_arg之前调用。
5.93.2 代码示例
void missing_vaend(char *s, …)
{
va_list va;
va_start(va, s); // va_init - va_start is called on va
vfprintf(log, s, ap);
} // missing_va_end - reached end of 函数 without calling va_end
void missing_vastart(int n, …)
{
va_list va;
while (n– > 0) {
int c = va_arg(va, c); // va_arg - va has not been initialized
}
}
5.94 虚函数没有析构函数
5.94.1 概览
一个类的析构函数没有或者不正确。因为该析构函数不是虚函数,所以继承类的析构函数就不会被调用,造成继承类的资源泄漏。
5.94.2 代码示例
struct A {
};
struct B: public A {
B(): p(new int) {}
~B() { delete p; }
int *p;
};
void leak() {
A *a = new B;
// This will not invoke ~B()
delete a;
}
class X {
~X() {}
};
class Y: public X {
X x;
~Y() {}
};
void test() {
Y *y = new Y;
X *x = y;
delete x; // Does not call Y::~Y(). A defect is not reported.
}
class X {
~X() {}
};
class Z {
~Z() { do_stuff(); }
};
class Y: public X {
Z z;
~Y() {} // Looks empty but calls Z::~Z(), which is not empty.
};
void test() {
Y *y = new Y;
X *x = y;
delete x; // Does not, but should call Y::~Y().
}
5.95 与常量字符串比较
5.95.1 概览
将一个不可信的数据与常量字符串进行比较。这种情况容易被攻击。
5.95.2 代码示例
void test() {
struct sockaddr_in serviceClient;
struct hostent *hostInfo
= gethostbyaddr((char*)&serviceClient.sin_addr,
sizeof(serviceClient.sin_addr),
AF_INET);
}
5.96 密码哈希序列较弱
5.96.1 概览
使用比较弱的加密hash序列。
5.96.2 代码示例
void test() {
HCRYPTPROV hCryptProv;
HCRYPTHASH hHash;
UCHAR calcHash[64];
DWORD hashSize = 64;
char password[128];
}
5.97 无效的字符串指针
5.97.1 概览
表示一个string类在返回该函数之后,该指针变成了无效的情况。
5.97.2 代码示例
BSTR has_a_bug()
{
return CComBSTR(L”temporary object”); // bug
}
void has_another_bug()
{
char const *p;
{
std::string s(“hi”);
p = s.c_str();
} // s is destroyed
use(p); // use after free
}
string global_string;
char const *test() {
char const *s = global_string.c_str(); // internal representation escapes
global_string += “foobaz”; // invalidation
return s; // use of invalid pointer
}
void buggy() {
std::vector v;
v.push_back(10);
int &x = v.back();
v.push_back(20); // might reallocate memory
use(x); // using possibly invalid memory
}
5.1 没有对new作异常处理
5.1.1 概览
new操作符失败的时候有些编译器将会抛出异常如果没有对异常进行捕捉,或者没有使用new(std::nothrow)将会导致程序结果错误甚至崩溃。
5.1.2 代码示例
void BadNew()
{
char * p = new char[2147483645]; // 程序崩溃, 抛出std::bad_alloc
}
5.2 分配和释放函数不匹配
5.2.1 概览
资源一般有配对的分配函数和释放函数,但是如果分配函数和释放函数不配对,将会产生这个错误。当一个资源被不同的不兼容的函数分配和释放资源时,C和C++系统不会强制报错,但是不正确的API用法会导致内存泄漏或者内存崩溃。
5.2.2 代码示例
void mixedNewAndFree()
{
int *x = new int;
free(x); // 报错,应该使用’delete x’
}
// MS Windows API
HINTERNET hConn = InternetOpenUrl(hSession, url, headers, \
length, flags, context);
CloseHandle(hConn); // 报错,应该使用 ‘InternetCloseHandle(hConn)’
5.3 对象指针和对象数组指针混淆
5.3.1 概览
对象指针被误解为数组指针,这可能导致程序访问无效内存,从内存中读取不需要的信息或者往非意向的地址写数据,从而导致崩溃。ARRAY_VS_SINGLETON也表示:基类的数组指针被看成了继承类的数组指针。
5.3.2 代码示例
class Base
{
public:
int x;
};
class Derived : public Base
{
public:
int y;
};
void f(Base *b)
{
b[1].x = 4;// b[1] 表示 b+1,表示address(b) + sizeof(Base)
}
Derived arr[3];// 实际上此数组对象大小为sizeof(Derived),大小不匹配,错误
f(arr); // 缺陷
void foo(char **result)
{
result = (char)malloc(80);
if (…)
{
strcpy(*result, “some result string”);
}
else
{
…
result[79] = 0; // 报错,应该是”(*result)[79] = 0”
}
}
void bar()
{
char *s;
foo(&s); // 这里错误
}
5.4 断言中的参数表达式有副作用
5.4.1 概览
有副作用的表达式作为断言的条件表达式。这种情况是很危险的,因为断言经常在发布版本中不编译,这意味着程序在debug模式和发布模式下行为是不同的。
5.4.2 代码示例
include
define FLAG 2
extern int flags;if (!flags & FLAG) // Defect: always yields 0
if (a == b ? 1 : 2)
5.24 赋值粘贴错误
5.24.1 概览
复制粘贴代码时修改变量不全。
5.24.2 代码示例
class CopyPasteError {
int square(int x) {
return x*x;
}
int example(int a, int b, int x, int y) {
int result = 0;
if (a > 0) {
result = square(a) + square(x);
}
if (b > 0) {
// “square(a)” should read “square(b)”
result = square(a) + square(y);
}
return result;
}
};
5.25 有赋值构造函数缺少赋值操作符
5.25.1 概览
一个类有复制构造函数但是却没有赋值操作符。此时只会进行浅拷贝,将会出现多个指针共用一个内存的现象,会导致内存泄漏或崩溃。
5.25.2 代码示例
class MyString {
char *p;
public:
MyString(const char *s) : p(strdup(s)) {}
~MyString() {free(p);}
// copy constructor
MyString(const MyString &init) : p(strdup(init.p)) {}
// no assignment operator
const char *str() const {return p;}
operator const char *() const {return str();}
};
5.26 构造函数和析构函数有内存泄漏
5.26.1 概览
一个构造函数分配内存并且存储指针到成员变量,但是析构函数没有释放。
5.26.2 代码示例
struct A {
int *p;
A() { p = new int; }
~A() { /oops, leak/ }
};
5.27 不安全的IO流操作
5.27.1 概览
用了不安全的IO函数scanf, fscanf等等,可能导致内存溢出。
5.28 不安全的字符串函数
5.28.1 概览
使用了不安全的字符串函数可能导致内存溢出比如sprintf, sscanf, strcat, strcpy 等等。
5.29 加密算法太弱
5.29.1 概览
DC.WEAK_CRYPTO 表示:函数产生了不安全的伪随机序列数字。initstate, lcong48, rand, random, seed48, setstate, and [dejlmn]rand48。这些函数不应该使用在加密方面,因为太容易被破解。
5.30 无用代码
5.30.1 概览
表示函数中的有一些代码由于逻辑分支的原因永远没有到达。
5.30.2 代码示例
int deadcode_example1(int *p) {
if( p == NULL ) {
return -1;
}
use_p( *p ); if ( p == NULL ) { // p cannot be null. handle_error(); // Defect: dead code return -1; } return 0;
}
void deadcode_example2(void *p) {
int c = ( p == NULL );
if ( p != NULL && c ) { // Always false do_some_other_work(); // Defect: dead code }
}
5.31 删除数组错误
5.31.1 概览
使用delete而不是delete[]来说释放一个对象数组的内存。 这样将会导致内存泄漏或者其他问题。
5.31.2 代码示例
void wrong_delete() {
char *buf = new char [10];
delete buf; // Defect: should be delete[] buf
}
struct auto_ptr {
auto_ptr():ptr(0){}
~auto_ptr(){delete ptr;}
int *ptr;
};
void test() {
auto_ptr *arr = new auto_ptr[2];
arr[0].ptr = new int(0);
arr[1].ptr = new int(1);
delete arr; // Memory leak, destructors are not called (or worse!)
}
5.32 删除void*指针错误
5.32.1 概览
指向void的指针被delete。当delete一个void指针时,该指针本身的内存被释放掉了,但是任何与动态类型相关联的析构函数都没有被执行。当析构函数执行一些比较重要的事情比如释放内存,此种情况是一个严重缺陷。
在很多实现中,delete一个void指针被看作是调用free(),因为内存被释放掉了,但是析构函数没有执行。然而,即使这是故意的行为,也是十分危险的,原因如下:
• 语言标准没有定义这个行为。具体实现从技术角度上说可以执行任何事情,一些编译器做了一些优化,粗暴地改变了依赖于未定义行为的代码。
• 如果分配位置被更改,例如,分配对象数组内存,那么释放内存将会默默地做错误的事情。
5.32.2 代码示例
void buggy(void *p)
{
delete p;
}
5.33 除数为0
5.33.1 概览
一个算术除法或者取模算法时当除数为0的时候。这种运算是未定义的,并且通常会导致程序终结。如果证明一个除数会在特定的情况下为0,那么检测器将会报告错误。
5.33.2 代码示例
int foo() {
int x = 0;
if (cond()) {
x = 1;
}
return 1 / x;
}
int foo(int y) {
if (y < 0) {
return 0;
}
return y;
}
void bar(int y) {
int z = 1 / foo(y);
}
5.34 枚举当做布尔
5.34.1 概览
枚举类型表达式不小心用于布尔内容中了。
5.34.2 代码示例
enum color {red, green, blue};
…
color c;
…
if (c) /* Why are green and blue distinguished from red? */
5.35 优先级顺序错误
5.35.1 概览
运算符优先级或者函数参数的顺序没有明确,导致执行动作随着不同的编译器而不同。
5.35.2 代码示例
int g(int x) {
return x + x++; // Defect, undefined evaluation order
}
int foo() {
int x = 0;
x = x++; // Defect
return x; // returns either 0 or 1
}
5.36 NULL指针使用
5.36.1 概览
表示一个指针为NULL或者未定义,但是后续在使用这个指针。
5.36.2 代码示例
int forward_null_example1(int *p) {
int x;
if ( p == NULL ) {
x = 0;
} else {
x = *p;
}
x += fn();
*p = x; // Defect: p is potentially NULL
return 0;
}
5.37 代码中有显示密码
5.37.1 概览
密码或者加密符号直接存储在源代码中。使用这种方式不安全。
5.37.2 代码示例
void test() {
char password[] = “ABCD1234!”;
HANDLE pHandle;
LogonUserA(“User”, “Domain”, password, 3, 0, &pHandle);
// HARDCODED_CREDENTIALS defect
}
5.38 相同的逻辑分支
5.38.1 概览
条件语句或者表达式执行相同的代码,而不管该条件是什么。这种重复代码隐含着条件是没有必要的(或者多个条件应该合并的)或者他们不应该是相同的(可能是拷贝错误)
5.38.2 代码示例
if (x==2) {
y=32;
z=y*2;
} else {
y=32;
z=y*2;
}
if (hasName(a)) {
name = getName(a);
return name;
}
name = getName(a);
return name;
5.39 不兼容的转换
5.39.1 概览
一个指针被强制转换为一个不兼容的指针。强制转换指针将会导致潜在的内存访问越界或者字节序列不对。
5.39.2 代码示例
void init(uint32_t *x) {
*x = 42;
}
void foo() {
uint16_t y;
init((int32_t*) &y); // out-of-bounds memory access
}
5.40 循环无法停止
5.40.1 概览
循环不会终止,循环条件可能永远为true。
5.40.2 代码示例
void foo(int x)
{
int i=0;
while (true) {
if (i >= 10) {
if (x == 55) { // x is never updated
break;
}
}
i++;
}
}
char c = foo();
while (c != EOF) {
if (c == 0x1c) {
found = 1;
} else {
if (found)
return -1;
else
continue;
}
…
}
5.41 整型溢出
5.41.1 概览
整型数据在进行算术运算时导致溢出或者截断的情况。
5.41.2 代码示例
define INT_MAX 2147483647
class Cell {public:
int a;
int *b;
};
void test(int x, int fd) {
int y;
read(fd, &y, 4); // y is from a tainted (outside) source
int size = y;
Cell *mycell;
if (size != 0) {
// Overflow results from operation size * sizeof(Cell)
// Overflowed value is used in memory allocation
mycell = new Cell[size]; // overflow and overflow_sink events
}
}
5.42 无效的迭代器
5.42.1 概览
使用了一个无效的迭代器,进行STL中的下标运算。
5.42.2 代码示例
void wrong_erase(list &l, int v) {
list::iterator i = l.begin();
for(; i != l.end(); ++i) { /* Defect: “i” is incremented
after invalidation
by a call to “erase” */
if(*i == v)
l.erase(i);
}
}
int deref_end(list &l) {
list::iterator i = l.end();
int x = *i; // Defect: dereferencing past-the-end
}
5.43 锁使用错误
5.43.1 概览
一个锁申请了但是没有释放,或者申请了两词,但是没有释放。这种情况一般是由于错误的返回分支导致的,这种情况将会导致死锁。
5.43.2 代码示例
int missing_unlock( struct info *data, int num ) {
spin_lock(data->lock); // +lock
if (num > data->count) { /* +missing_unlock DEFECT: data is left locked, any thread attempting to lock it will wait indefinitely */ return -1; } spin_unlock(data->lock); return 0;
}
fn() {
for (i = 0; i < 10; i++) {
lock(A); // +lock, +double_lock
if (cond) continue; // -unlock unlock(A); }
}
fn(L l) {
lockassert(l);
// …
}
caller() {
lock(A);
unlock(A); // +unlock
fn(A); // +lockassert
}
5.44 不匹配的迭代器
5.44.1 概览
一个STL迭代器传给另外一个不同的迭代器或者一个迭代器与另外一个不同类型的迭代器进行比较。
5.44.2 代码示例
void test(vector &v1, vector &v2) {
vector::iterator i = v1.begin();
// Defect: Uses “i” from “v1” in a method on “v2”
v2.erase(i);
}
void test(list &l1, list &l2) {
list::iterator i = l1.begin();
l2.splice(l2.begin(), l1);
// Defect: i belonged to l1 but was transferred to l2 with “splice”
l1.erase(i);
}
void test(list &l1, list &l2) {
// Error: comparing “i” from “l1” with “l2.end()”
for(list::iterator i = l1.begin(); i != l2.end(); ++i){}
}
5.45 错误的隐式转换
5.45.1 概览
MISRA_CAST 表示一些隐式类型转换,比如把一个float类型隐式转换为int类型。
5.45.2 代码示例
const int32_t xs32a = 0, xs32b = 1;
static void non_compliant1()
{
(void)(float64_t)(xs32a / xs32b); // Defect: Casting complex expression with
// integer type to a non-integer
// type float64_t
}
float32_t f32a;
float64_t f64a;
int16_t compliant()
{
12c4c
f32a = 2.5F;
f64a = f64b + f32a;
}
static void non_compliant1()
{
f32a = f64a; // Defect, casting complex expression to narrower type
}
extern float32_t f32a, f32b;
static void non_compliant1()
{
(void)(float64_t)(f32a + f32b); //Defect: Type 32-bit float_t cast
// to 64-bit float64_t
}
5.46 缺少break
5.46.1 概览
在switch表达式没有break分支,可能是编写者的遗漏。
5.46.2 代码示例
void doSomething(int what)
{
switch (what) {
case 1:
foo();
break;
case 2: // Defect: Missing break statement in this case bar(); case 3: gorf(); break; }
}
5.47 缺少逗号
5.47.1 概览
两个字符串之间没有使用逗号,这样会导致这两个字符串变成了一个字符串。
5.47.2 代码示例
char* arr[] = {
“a string literal” //Defect here.
“another string literal”
};
char* arr[] = {
“a string literal” //Defect here.
"another string literal"
};
5.48 缺少赋值或者拷贝构造函数
5.48.1 概览
MISSING_COPY_OR_ASSIGN 表示许多情况比如: 一个类中有动态申请的资源,但是该类没有拷贝构造函数,此时的拷贝构造函数会使用默认的浅拷贝构造函数,将会导致一系列的问题。
5.48.2 代码示例
class MyString {
char *p;
public:
// evidence of resource ownership
MyString(const char *s) : p(strdup(s)) {}
// further evidence of resource ownership
~MyString() {free(p);}
// no copy constructor at all
// no assignment operator at all
const char *str() const {return p;}
operator const char *() const {return str();}
};
class MyString {
char *p;
public:
MyString(const char *s) : p(strdup(s)) {}
// inadequate copy constructor
MyString(const MyString &init) : p(init.p) {}
~MyString() {free(p);}
// inadequate assignment operator
MyString &operator=(const MyString &rhs)
{
if (this != &rhs) {
p = rhs.p;
}
return *this;
}
};
5.49 缺少部分锁
5.49.1 概览
MISSING_LOCK 表示有些情况比如:一个对象多个地方都是由锁进行锁住的,但是有个别地方没有加锁,这将导致数据的并发安全性。
5.49.2 代码示例
struct bingo {
int bango;
lock bongo;
};
void example(struct bingo *b) {
lock(&b->bongo); // example_lock
b->bango++; // example_access
unlock(&b->bongo);
lock(&b->bongo); // example_lock b->bango++; // example_access unlock(&b->bongo); lock(&b->bongo); // example_lock b->bango++; // example_access unlock(&b->bongo);
}
void lockDefect(struct bingo *b) {
b->bango = 99; // missing_lock
}
5.50 缺少移动赋值
5.50.1 概览
一个类使用了赋值操作符时使用的是拷贝但是没有使用移动赋值,这样会浪费很多资源。
5.50.2 代码示例
struct S { // missing_move_assignment event
S() {
p = new int(0);
}
S(const S &other) {
p = new int;
*p = *other.p;
}
~S() {
delete p;
}
S& operator=(const S &other) {
*p = *other.p;
return *this;
}
int *p;
};
s = S(); // example of copy assignment operator being applied to rvalue return 0;
}}
struct dtor_no_free { // No defect when report_no_dtor_free is false.
// Defect when report_no_dtor_free is true.
dtor_no_free() {}
dtor_no_free(const dtor_no_free &other): p(other.p) {}
dtor_no_free& operator=(const dtor_no_free &other) {
p = other.p;
return *this;
}
~dtor_no_free() {}
std::string p;
};
int main() {
dtor_no_free d;
d = dtor_no_free();
return 0;
}
5.51 未还原半途而废的函数动作
5.51.1 概览
一个全局变量在进行更改时,如果中间遇到错误之后没有还原。
5.51.2 代码示例
extern int refresh_mode;
void move(item_t *item)
{
int save_mode = refresh_mode;
refresh_mode = 0; /* reduce flicker */
if (!lock_for_move(item))
return; /* error: leaving ‘refresh_mode’ as 0 */
handle_move(item);
unlock_for_move(item);
refresh_mode = save_mode;
}
5.52 缺少函数返回值
5.52.1 概览
一个非void的函数没有返回值或者返回多个值。
5.52.2 代码示例
int fn(int x)
{
switch (x) {
case 5: return 4;
default: return 5;
}
// no return; but not a defect, since unreachable
}
int fn(int x) {
if (x == 5)
return 4;
else if (x == 3)
return 2;
} // missing_return
int fn(int x) {
if (x == 5)
return 4; // extra_return
else if (x == 3)
return 2; // extra_return
return 0; // extra_return
}
5.53 混淆的枚举值
5.53.1 概览
两个枚举定义的各种枚举值混用。
5.53.2 代码示例
enum e {E1, E2};
enum f {F0, F1, F2, F3};
…
void foo(e ee) {
switch (ee) {
case E1:
…
case F2: /* Defect */
…
}
}
void bar(int x) {
switch (x) {
case E1: /* Defect in conjunction with (see the second line that follows) … */
…
case F2: /* … this */
…
}
}
5.54 负整数的误用
5.54.1 概览
许多负整数的误用. 负整数以及函数返回值必须在使用之前进行判断 (例如, 数组下标,循环下标等)。
5.54.2 代码示例
void basic_negative() {
int buff[1024];
int x = some_function(); // some_function () might return -1.
buff[x] = 0; // Defect: buffer underrun at buff[-1]
}
void subtle_negative() {
unsigned x;
x = signed_count_func(); // Returns signed -1 on error.
// -1 cast to an unsigned 是一个 very large integer.
loop_with_param(x); // Uses x as an upper bound.
// Defect: loop might never end or last too long.
}
void another_subtle_negative(){
unsigned int c;
for (i = 0; (c=read(fd, buf, sizeof(buf)))>0; i+=c)
// read() returns -1 on error, c is now a very large integer
if (write(1, buf, c) != c) // Defect: Too many bytes written to stdout.
die(“Write call failed”);
}
5.55 缩进不匹配错误
5.55.1 概览
代码的缩进不匹配。经常这种情况是因为忘记加括号了。
5.55.2 代码示例
define MULTI_STMT_MACRO(x, y) foo(x); bar(y) /* user ‘;’ */
/* … */if (condition)
MULTI_STMT_MACRO(p->x, p->y); // bar(p->y)一定会执行
if (condition1)
if (condition2)
do_something();
else // “dangling”
do_something_else();
5.56 无意义的函数
5.56.1 概览
一些语句不完成任何事情,或者不是完成本意想做的事情。通常这种情况是由于笔误或者语法的误解,比如运算符优先级等。
5.56.2 代码示例
void array_null()
{
unsigned int a[3];
unsigned int b[1];
unsigned int c[2];
if (*a == 0)
a[0];
if (b == 0) // The entire array b is compared to 0.
b[1];
if (c[1] == 0)
c[1];
}
void bad_memset()
{
int *p;
memset(p, ‘0’, l); // Fill value is ‘0’, and 0 is more likely.
memset(p, l, 0); // Length is 0, and so likely that l and 0 reversed.
memset(p, 0xabcd, l); // Fill is truncated, and so memory
// will not contain the 0xabcd pattern.
}
• void extra_comma() {
• int a, b;
• for (a = 0, b = 0; a < 10, b < 10; a++, b++);
• // Extra comma, and so a < 10 is not used.
}
• void no_effect_test() {
• int a, b;
• a == b; // Test has no effect, and is
• // likely intended to be the assignment a = b
}
• int a, b;
• void bool_switch() {
• switch (a == b) { // Boolean switch
• case 1:
• }
}
• void incomplete_delete() {
• int *p, *q;
• delete p, q; // The pointer q is not deleted.
}
• void no_effect_deref() {
• int *p;
• *p++; // *p is useless
}
• void self_assign(struct foo *ptr) {
• a = a; // assignment to self, global
• ptr->x = ptr->x; // assignment to self, field
}
struct C { static void foo(); int x; };
C * c; c->foo(); // Becomes “c, C::foo();”. c is unnecessary,
// but not flagged.
C stackc; stackc.x = 4;
5.57 函数返回值为NULL
5.57.1 概览
函数的返回值为NULL,但是在使用返回值的时候没有加以判断。
5.57.2 代码示例
void bad_malloc() {
// malloc returns NULL on error
struct some_struct x = (struct some_struct) malloc(sizeof(*x));
// ERROR: memset dereferences possibly null pointer x
memset(x, 0, sizeof(*x));
}
5.58 文件权限不对
5.58.1 概览
OPEN_ARGS表示一些情况比如一个文件没有使用指定的访问权限被创建。
5.58.2 代码示例
void open_args_example() {
int fd = open(“file.log”, O_CREAT); // lacks a correct mode argument.
}
5.59 顺序相反错误
5.59.1 概览
程序需要一对锁但是在不同的地方这一对锁使用的顺序不同,这样将会导致死锁。
5.59.2 代码示例
// Thread one enters this 函数
void deadlock_partA(struct directory *a, struct file *b) {
spin_lock(a->lock); // Thread one acquires this lock spin_lock(b->lock); // before it can acquire this lock...
}
// Thread two enters this 函数
void deadlock_partB( struct directory *a, struct file *b ) {
spin_lock(b->lock); // Thread two acquires this lock spin_lock(a->lock); // Deadlock
}
5.60 扩充数据精度丢失
5.60.1 概览
如果一个表达式准备进行扩充时但是没有显示强制转换,那么将会丢失一些数据信息导致错误。
5.60.2 代码示例
void foo() {
unsigned int x = 2147483648;
unsigned int y = 2;
unsigned long long z;
if ((x * y) == z) {
// Do something.
}
}
5.61 内存越界
5.61.1 概览
内存访问越界. 不合适的内存访问将会导致内存崩溃和进程崩溃以及安全漏洞问题。包括堆和栈的下标索引。
5.61.2 代码示例
void bad_heap() {
int buffer = (int ) malloc(10 * sizeof(int)); // 40 bytes
int i = 0;
for(; i <= 10; i++) { // Defect: writes buffer[10] and overruns memory
buffer[i] = i;
}
}
void test(int i) {
int n;
char *p = malloc(n);
int y = n; // Valid indices are buffer[0] to buffer[y - 1]
p[y] = ‘a’; // Defect: writing to buffer[y] overruns local buffer
}
struct s {
int a;
int b;
} s1;
void test() {
int n, i;
struct s p = malloc(n sizeof(struct s));
if (i <= n) // “i” can be equal to n
p[i] = s1; // Defect: overrun of buffer p
}
void access_dbuffer(int *x, int n) {
x[n-1] = 1;
}
void caller(int n) {
int array[10];
if (n < 100) {
access_dbuffer(array, n); // defect
}
}
void foo() {
int array[10];
int i = get();
if (i > 8) {
array[i] = 1;
}
}}
5.62 函数传值太大
5.62.1 概览
一些函数的入参太大(超过128个字节)。为了避免这种情况,不能之传值,只能传引用或者指针。
5.62.2 代码示例
struct big {
int a[20];
int b[20];
int c[20];
};
void test(big b) { // Warning: passing by value, 240 bytes
}
struct exn {
const char str[128];
int code;
};
void foo() {
try {
//…
} catch(exn e) { // Warning, catch by value, 132 bytes
//…
}
}
5.63 返回局部变量地址
5.63.1 概览
局部变量的地址被函数返回,但是该地址在函数返回之时已经失效,将会导致不可预测的错误。
5.63.2 代码示例
some_struct * basic_return_local(struct some_struct *b) {
struct some_struct a(*b); // a is copy-constructed onto the stack
return &a; // Returns a pointer to local struct a
}
5.64 NULL判断过晚
5.64.1 概览
一个对象或变量已经被使用之后,再去判断该变量是否为NULL或者是否有效,此时已经晚了。
5.64.2 代码示例
void basic_reverse_null(struct buf_t *request_buf) {
*request_buf = some_函数(); // Assignment dereference
if (request_buff == NULL) // NULL check AFTER dereference
return;
}
5.65 负数判断过晚
5.65.1 概览
一个对象或变量已经被使用之后,再去判断该变量是否为负数,此时已经晚了。
5.65.2 代码示例
void simple_reverse_neg(int some_signed_integer) {
some_struct *x = kmalloc(some_signed_integer, GFP_KERNEL); // Dangerous integer use
if (some_signed_integer < 0) // Check after use
return error;
}
5.66 有风险的加密
5.66.1 概览
加密算法十分的脆弱或者有风险。一般是使用了旧的有风险的加密算法。DES算法,RSG算法,ECB块模式,RC4都不建议使用。
5.66.2 代码示例
CryptDeriveKey(hCryptProv, CALG_DES, hHash, 0, &hKey));
5.67 使用废弃接口
5.67.1 概览
使用了C++明确要求废弃的函数接口。
5.67.2 代码示例
void secure_coding_example() {
char *d, *s, *p;
int x;
…
gets(p);
strcpy(d, s);
strncpy(d, s, x);
}
5.68 临时文件不安全
5.68.1 概览
一个临时文件以一个不安全的方式创建。
5.68.2 代码示例
void secure_temp_example() {
char *tmp, *tmp2, *tmp3;
char buffer[1024];
tmp = mktemp(buffer);
}
5.69 自我赋值无意义
5.69.1 概览
自己赋值给自己,这种情况毫无意义或者会产生错误。
5.69.2 代码示例
class SimpleString {
char *p;
public:
SimpleString(const char *s = “”) : p(strdup(s)) {}
SimpleString(const SimpleString &init) : p(strdup(init.p)) {}
~SimpleString() {free(p);}
SimpleString &operator=(const SimpleString &rhs)
{
free(p); // bad if &rhs == this
p = strdup(rhs.p); // use-after-free when &rhs == this
return *this;
}
const char *str() {return p;}
operator const char *() {return str();}
};
5.70 符号扩展溢出
5.70.1 概览
SIGN_EXTENSION表示在符号扩展的过程中导致数据溢出异常,导致非正确的数据结果。
5.70.2 代码示例
unsigned long readLittleEndian(unsigned char *p)
{
return p[0] |
(p[1] << 8) |
(p[2] << 16) |
(p[3] << 24);
}
int main()
{
unsigned char bytes[4] = { 0x03, 0x02, 0x01, 0x80 };
unsigned long result = readLittleEndian(bytes);
printf(“0x%lX\n”, result);
}
5.71 Sizeof不匹配
5.71.1 概览
sizeof关键字作用的对象和预期的对象大小不一致,导致对象大小计算错误。
5.71.2 代码示例
struct buffer {
char b[100];
};
void f() {
struct buffer buf;
memset(&buf, 0, sizeof(&buf)); /* Defect: should have been “sizeof(buf)” */
}
struct buffer {
char b[100];
};
void f() {
struct buffer p = (struct buffer )malloc(sizeof(struct buffer *));
/* Defect: should be “sizeof(struct buffer)” */
}
void f(void , void *, size_t);
void g() {
short s;
short *ps;
f(&s, &ps, sizeof(short));
}
struct buffer {
char b[100];
};
void f(struct buffer *p) {
p += sizeof(struct buffer); /* Defect: “sizeof(struct buffer)” should be “1” */
}
struct buffer {
char b[100];
};
void f(struct buffer *p, struct buffer *q) {
if (q – p > 3 * sizeof(p)) / Defect: “* sizeof(p)” is extraneous /
printf(“q too far ahead of p\n”);
}
struct buffer {
char b[100];
};
struct buffer array[30];
void f(struct buffer *cur) {
size_t pos = (cur – array) / sizeof(struct buffer); /* Defect: “/ sizeof(struct buffer)” is extraneous */
}
5.72 在锁中睡眠
5.72.1 概览
在有锁的情况下使用sleep.这将会导致其他线程去争夺这个锁的资源,大大浪费了时间资源。
5.73 栈使用过大
5.73.1 概览
较大的栈空间使用(超过250K字节),或者单一对象超过10K字节。
5.73.2 代码示例
void stack_use_callee1(void) {
char buf[1024]; // 1024 bytes of stack usage char c; /* 4 bytes of stack usage, 1 byte promoted to 4 byte alignment requirement */
}
void stack_use_callee2(void) {
char buf[16384]; // Exceeds max single base use of 1024 bytes
}
void stack_use_callee3(void) {
char buf[20000]; // Exceeds max single base use of 1024 bytes
}
void stack_use_example(void) {
char buf[16384]; // Exceeds max single base usage of 1024 bytes
if (/* condition */) { stack_use_callee1(); // Temporarily consumes 1044 bytes } else if (/* condition */) { stack_use_callee2(); // Stack overflow: (16400 + 16912) > 32768 } else { stack_use_callee3(); // Stack overflow: (20016 + 16912) > 32768 } if (/* condition */) { char another_buf[512]; // 512 bytes of stack usage }
}
5.74 分号使用错误
5.74.1 概览
有些语句后面不该加分号但是加了,或者有些语句后面应该加分号,但是没加。
5.74.2 代码示例
if (condition);
do_something_conditionally();
while (condition);
{
if (other_condition)
return;
/* advance the loop */
}
/* count the elements in list ‘head’ */
for (count = 0, p = head; p != 0; ++count, p = p->next)
;
{
int local_variable
/* … */
}
ifndef _NDEBUG
define DPRINT(x…) fprintf(stderr, x)
else
define DPRINT(x…)
endif
if (condition)DPRINT(“condition is true\n”);
5.75 格式化状态错误
5.75.1 概览
许多情况比如 一个输出流对象的格式化状态被修改了但是没有被还原. 在函数返回后,对该流的格式化输出可能会有非计划中的影响.
5.75.2 代码示例
void oops1(int i)
{
cout << hex << i;
}
You can fix this defect as follows:
void corrected1(int i)
{
cout << hex << i << dec;
}
void oops2(ostream &os, float f)
{
os << setprecision(2) << f;
}
5.76 非零字符串错误
5.76.1 概览
字符串是非零结尾的,但是却将该字符串发送给一个以零结尾的字符串接收函数中。
5.76.2 代码示例
char *string_null_example() {
char name[1024];
char *extension;
string_from_net(fd, 1023, name); // read from net, no null-termination if (x[0] != SOME_CHAR) { extension = process_filename(name); // process until '\0' found }
}
5.77 字符串溢出
5.77.1 概览
字符串处理函数在字符串结尾处写入数据。由于源字符串大于目的字符串导致的。
5.77.2 代码示例
void string_overflow_example() {
char destination_buffer[256];
char source_buffer[1024];
…
strcpy(destination_buffer, source_buffer);
}
5.78 字符串大小不对
5.78.1 概览
字符串处理函数在字符串结尾处写入数据。推荐使用字符串安全函数strncpy()等等。与字符串溢出的区别在于,它是显示的长度超出目的接收缓冲区。
5.78.2 代码示例
char *string_size_example() {
static char addr[100];
struct hostent *he;
he = gethostbyaddr(address, len, type);
strcpy(addr, he->h_name);
return addr;
}
5.79 参数顺序混乱
5.79.1 概览
函数的两个参数或者多个参数的填入顺序搞错,导致函数入参不正确,导致函数行为异常。
5.79.2 代码示例
void copy(int srcId, int dstId) { /* … */ }
void test() {
int srcId = 1;
int dstId = 2;
copy(dstId, srcId); /* Defect: arguments are swapped. */
}
5.80 数据污染
5.80.1 概览
一个数据本来是可信的,但是被一个不可信的数据污染掉了,此数据变成不可信数据。
5.80.2 代码示例
void tainted_scalar_example() {
int nresp = packet_get_int();
if (nresp > 0) {
response = xmalloc(nresp * sizeof(char *)); for (i = 0; i < nresp; i++) { // tainted scalar controls loop response[i] = packet_get_string(NULL); // heap corruption } }
}
5.81 字符串污染
5.81.1 概览
一个缓存本来是可信的,但是被一个不可信的数据污染掉了,此缓存变成不可信缓存。
5.81.2 代码示例
void tainted_string_example() {
char *request = packet_get_string();
if (!legal_request(request)) {
sprintf(error_msg, “Illegal request: %s”, request); /* sprintf()
transitively taints error_msg */
…
syslog(LOG_WARNING, error_msg);
}
}
5.82 文件访问不安全
一个文件一般是先判断是否可以访问,然后再去访问它,但是在这两步骤之间,可能攻击者已经将文件内容替换掉了,不安全。应该直接访问该文件,并修改权限。
5.82.1 代码示例
void toctou_example() {
stat(logfile, &st);
if (st.st_uid != getuid())
return -1;
open(logfile, O_RDWR);
}
5.83 未捕捉异常
5.83.1 概览
函数抛出了异常但是没有任何函数捕捉它,这样会导致整个进程崩溃。
5.83.2 代码示例
// Prototypical defect.
int main(){
throw 7;
return 0;
}
// A simple defect resulting from a 函数 call.
void fun() {
throw 7;
}
int main(){
fun();
return 0;
}
void fun() {
throw 7;
}
void cannot_throw() throw() {
fun();
}
class A {};
class B {};
class C {};
int main(){
try {
throw A();
} catch (B b){
} catch (C b){
}
return 0;
}
class A {};
int main() {
try {
throw A(); //Will not be caught.
} catch (…){
cerr << “Error” << endl;
throw;
}
}
5.84 敏感数据未加密
5.84.1 概览
像密码一样的敏感数据没有被加密掉,导致数据不安全。
5.84.2 代码示例
void test(int socket, char* password) {
recv(socket, password, 100, 0); HANDLE pHandle; LogonUserA("User", "Domain", password, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &pHandle); // Defect here.
}
5.85 变量未初始化
5.85.1 概览
代码中使用任何的未初始化的变量或资源。
5.85.2 代码示例
int uninit_example1(int c) {
int x;
if(c)
return c;
else
return x; // defect: “x” is not initialized
}
int result;
int uninit_example2(int c) {
int *x;
if(c)
x = &c;
use (x); // defect: uninitialized variable “x” and “*x” used in call
}
void use (int *x) {
result = *x+2;
}
int result;
int uninit_example3() {
int x[4];
result = x[1]; // defect: use of uninitialized value x[1]
}
int result;
struct A {
int a;
int *b;
};
int uninit_example4() {
struct A *st_x;
st_x = malloc (sizeof(struct A)); // Dynamically allocate struct
partially_init(st_x);
use (st_x); // defect: use of uninitialized variable st_x->b
}
void partially_init(struct A *st_x) {
st_x->a = 0;
}
void use (struct A *st_x) {
result = *st_x->b;
}
5.86 没有构造函数
5.86.1 概览
类的非静态的成员变量没有被初始化。
5.86.2 代码示例
class Uninit_Ctor_代码示例1 {
Uninit_Ctor_代码示例1(int a) : m_a(a) {
// Defect: m_p not initialized in constructor
}
int m_a; int *m_p;
};
class Uninit_Ctor_代码示例2 {
Uninit_Ctor_代码示例2(int a) : m_a(a) {
init();
// Defect: m_c not initialized in constructor
}
void init() {
m_b = 0;
}
int m_a, m_b, m_c;
};
class HasCtor {
int m;
public:
HasCtor() : m(0) {}
};
class HasOnlyGenCtor : public HasCtor {
int *p;
};
5.87 整数除法精度丢失
5.87.1 概览
使用整型进行除法的时候精度丢失,如果本意是赋值给浮点型的话,那么应该先转为浮点型再进行除法运算。
5.87.2 代码示例
// Sets PI_APPROX to 3.0!
double PI_APPROX = 22 / 7; // Defect here.
// Rounds toward zero (and adds 1 to negative results)!
int roundedAverage(int a, int b) {
return (int)(0.5 + ((a + b) / 2)); // Defect here.
}
5.88 代码不可到达
5.88.1 概览
控制流不能到达代码的特定地区,或者到达了特定地区但是不是程序员想要的。
5.88.2 代码示例
bool forgiving = false; // Start less flexible.
do {
if (!tryIt(forgiving) && !forgiving) {
forgiving = true;
continue; // [intend to] Try again, more flexibly.
}
} while (false); // [The loop will never proceed past this point.]
int unreachable_example (int *p) {
if( p == NULL ) /{/
handle_error();
return -1;
/}/
use_p( *p ); //An UNREACHABLE defect here.
return 0;
}
int unreachable_example2 (int array[10]) {
int i;
int value = -1;
for( i = 0; i < 10; i++ ) { //An UNREACHABLE defect here:
// Increment is unreachable. Array
// not properly searched because the break
// statement is executed on the first iteration.
if( array[i] > 100 ) /{/
value = array[i];
break;
/}/
}
return value;
}
5.89 定义变量但未使用
5.89.1 概览
一个变量被定义了,但是从来没有使用过,这样会浪费代码资源并且产生误解。
5.89.2 代码示例
const char* get_capital_city(const char *country)
{
const char *result = 0;
if (strcmp(country, “Argentina”) == 0) {
result = “Buenos Aires”; // Assigned value will never be used.
} else if (strcmp(country, “Italy”) == 0) {
result = “Rome”; // Assigned value will never be used.
} if (strcmp(country, “China”) == 0) { // Should be ‘else if’ here.
result = “Beijing”;
} else {
result = “Unknown”; // This will overwrite values
// “Buenos Aires” and “Rome”.
}
return result;
}
5.90 无效的函数调用
5.90.1 概览
一个函数没有起到作用,或者是用户没有对唯一起作用的返回值做判断,或者是用户没有对参数的返回值作任何判断和处理。
5.90.2 代码示例
struct pair_t
{
pair_t(int x, int y) : x_(x), y_(y) {}
int x_;
int y_;
};
pair_t swap(pair_t xy)
{
return pair_t(xy.y_, xy.x_);
}
void incorrect()
{
…
swap(xy); /* Defect: swap does not modify its argument */
…
}
5.91 系统调用用户指针
5.91.1 概览
系统直接调用用户级别的指针,这样会导致系统崩溃。
5.91.2 代码示例
void user_pointer_example() {
error = copyin((void *)p->p_sysent->sv_psstrings, &pstr, sizeof(pstr));
if (error)
return (error);
for (i = 0; i < pstr.ps_nargvstr; i++) {
sbuf_copyin(sb, pstr.ps_argvstr[i], 0);
sbuf_printf(sb, “%c”, ‘\0’);
}
}
5.92 释放资源后使用
5.92.1 概览
内存或者资源在被释放掉后进行使用。这种情况几乎每次都是内存崩溃。
5.92.2 代码示例
void fun(int * p) {
free (p);
int k = *p; // Defect
}
int f(void *p) {
if(some_error()) {
free(p);
return -1;
}
return 0;
}
void g() {
void *p = malloc(42);
if(f(p) < 0) {
free(p); // Double free
}
use(p);
}
void use_after_free(struct S *p) {
free(p);
free(p->field); // Dereference
}
int f(int i) {
int *p = malloc(8);
free (p);
int res = p[i];
}
extern int ext(int *p);
void fun() {
int * p = malloc(100);
free(p); // Pointer freed
ext(p); // Pointer used as arg
}
5.93 可变参数使用错误
5.93.1 概览
va_start or va_copy后面必须有va_end且必须在va_arg之前调用。
5.93.2 代码示例
void missing_vaend(char *s, …)
{
va_list va;
va_start(va, s); // va_init - va_start is called on va
vfprintf(log, s, ap);
} // missing_va_end - reached end of 函数 without calling va_end
void missing_vastart(int n, …)
{
va_list va;
while (n– > 0) {
int c = va_arg(va, c); // va_arg - va has not been initialized
}
}
5.94 虚函数没有析构函数
5.94.1 概览
一个类的析构函数没有或者不正确。因为该析构函数不是虚函数,所以继承类的析构函数就不会被调用,造成继承类的资源泄漏。
5.94.2 代码示例
struct A {
};
struct B: public A {
B(): p(new int) {}
~B() { delete p; }
int *p;
};
void leak() {
A *a = new B;
// This will not invoke ~B()
delete a;
}
class X {
~X() {}
};
class Y: public X {
X x;
~Y() {}
};
void test() {
Y *y = new Y;
X *x = y;
delete x; // Does not call Y::~Y(). A defect is not reported.
}
class X {
~X() {}
};
class Z {
~Z() { do_stuff(); }
};
class Y: public X {
Z z;
~Y() {} // Looks empty but calls Z::~Z(), which is not empty.
};
void test() {
Y *y = new Y;
X *x = y;
delete x; // Does not, but should call Y::~Y().
}
5.95 与常量字符串比较
5.95.1 概览
将一个不可信的数据与常量字符串进行比较。这种情况容易被攻击。
5.95.2 代码示例
void test() {
struct sockaddr_in serviceClient;
struct hostent *hostInfo
= gethostbyaddr((char*)&serviceClient.sin_addr,
sizeof(serviceClient.sin_addr),
AF_INET);
if (strcmp(hostInfo->h_name, "www.domain.nonexistanttld") == 0) { // WEAK_GUARD DNS defect protected_operation(); }
}
5.96 密码哈希序列较弱
5.96.1 概览
使用比较弱的加密hash序列。
5.96.2 代码示例
void test() {
HCRYPTPROV hCryptProv;
HCRYPTHASH hHash;
UCHAR calcHash[64];
DWORD hashSize = 64;
char password[128];
CryptAcquireContextW(&hCryptProv, 0, 0, PROV_RSA_FULL, 0); CryptCreateHash(hCryptProv, CALG_SHA_512, 0, 0, &hHash); CryptHashData(hHash, (BYTE*)password, strlen(password), 0); CryptGetHashParam(hHash, HP_HASHVAL, (BYTE*)calcHash, &hashSize, 0); //WEAK_PASSWORD_HASH defect
}
5.97 无效的字符串指针
5.97.1 概览
表示一个string类在返回该函数之后,该指针变成了无效的情况。
5.97.2 代码示例
BSTR has_a_bug()
{
return CComBSTR(L”temporary object”); // bug
}
void has_another_bug()
{
char const *p;
{
std::string s(“hi”);
p = s.c_str();
} // s is destroyed
use(p); // use after free
}
string global_string;
char const *test() {
char const *s = global_string.c_str(); // internal representation escapes
global_string += “foobaz”; // invalidation
return s; // use of invalid pointer
}
include
void use(int);void buggy() {
std::vector v;
v.push_back(10);
int &x = v.back();
v.push_back(20); // might reallocate memory
use(x); // using possibly invalid memory
}
相关文章推荐
- C++代码注释规范(整理)
- 白盒测试 [代码规范][C++] 三
- Google放出C++代码风格规范
- [C++]项目中的代码注释规范(整理)
- C++代码匈牙利命名规范
- 代码规范 : 控制逻辑
- 我自己的c++代码规范
- 白盒测试 [代码规范] [C++] 四
- cocos2d c++ 代码规范(译文)
- Google放出C++代码风格规范
- 谷歌的C++代码规范
- C/C++代码命名和格式规范
- C++项目代码规范(偶自用)
- C++代码规范
- C/C++代码规范
- 谷歌C++代码规范--翻译学习1
- Google的c++代码规范
- C/C++代码命名和格式规范
- 白盒测试 [代码规范] [C++] 一
- 代码规范_3:c++ _异常