BUG

野火烧不尽,春风吹又生

                                  ——BUG

0x00000001

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void inplace_swap(int *x, int *y)
{
*y = *x ^ *y;
*x = *x ^ *y;
*y = *x ^ *y;
}

void reverse_array(int a[], int cnt)
{
int first, last;
for (first = 0, last = cnt - 1; first <= last; first++,last--)
{
inplace_swap(&a[fisrt], &a[last]);
}
}

目的:使用上述reverse_array()函数实现将一个数组的元素头尾两端依次对调

漏洞:当数组长度为奇数时,该函数总会将数组中正中元素置零

原因:设数组长度为2*k+1,当first=last=k时,调用inplace_swap()函数意味着a[k]元素与自身异或,结果总为0

解决方案:

1
for (first = 0, last = cnt - 1; first < last; first++,last--)

0x00000002

1
2
3
4
5
6
7
8
9
float sum_elements(float a[], unsigned length)
{
int i;
float result = 0;

for (i = 0; i <= length - 1; i++)
result += a[i];
return result;
}

目的:该函数试图计算数组a中所有元素的和,其中元素数量由参数length给出

漏洞:当length值为0时,运行该函数会遇到一个内存错误

原因:因为参数length是无符号的,计算0-1将使用无符号运算,这等价于模数加法,结果为32位最大无符号数,而任何数都是小于或等于该值的,所以该判断条件总为真,代码试图访问数组a的非法元素

解决方案:

1
for (i = 0; i < length; i++)

0x00000003

1
2
3
4
5
6
size_t strlen(const char *s);

int strlonger(char *s, char *t)
{
return strlen(s) - strlen(t) > 0;
}

目的:比较s串是否长于t串

漏洞:当s串长度小于t串长度时,比较函数会不正确地返回1

原因:由于strlen()被定义为产生一个无符号结果,差和比较都采用无符号运算计算。当s比t短的时候,strlen(s)-strlen(t)的差会为负,但是变成了一个很大的无符号数,且大于零

解决方案:

1
2
3
4
int strlonger(char *s, char *t)
{
return strlen(s) > strlen(t);
}

0x00000004

1
2
3
4
5
int tadd_ok(int x, int y)
{
int sum == x + y;
return (sum -x == y) && (sum -y == x);
}

目的:测试两数相加是否溢出,无溢出返回1

漏洞:上述测试函数总返回1

原因:补码加法是一个阿贝尔群,因此无论加法是否溢出,表达式(x+y)-x求值总为y,(x+y)-y求值总为x

解决方案:

1
2
3
4
5
6
7
int tadd_ok(int x, int y)
{
int sum == x + y;
int neg_over = x < 0 && y < 0 && sum >= 0;
int pos_over = x > 0 && y > 0 && sum < 0;
return !neg_over && !pos_over;
}

0x00000005

1
2
3
4
int tsub_ok(int x, int y)
{
return tadd_ok(x, -y);
}

目的:测试x-y是否溢出,无溢出返回1

漏洞:该函数会给出正确的值,除了当y=TMin时

原因:当y=TMin,有-y也等于TMin,因此函数tadd_ok()会认为只要x是负数时,就会溢出;而x为非负数时,不会溢出。实际上,情况恰恰相反才对:当x为负数时,tsub_ok(x,TMin)应该为1;而当x为非负数时,它应该为0

这个例子说明,在函数的任何测试过程中,TMin都应该作为一种测试情况