splint是一个动态检查C语言程序安全弱点和编写错误的程序.splint会进行
多种常规检查,包括未使用的变量,类型不一致,使用未定义变量,无法执行的
代码,忽略返回值,执行路径未返回,无限循环等错误.
GTES 10.5和11版本已经整合有splint软件包,直接可以使用.
下载地址:http://www.splint.org/downloads/splint-3.1.2.src.tgz源码包安装:# tar zxvf splint-3.1.2.src.tgz# cd splint-3.1.2# ./configure# make install
在引用没有指向任何内存地址的指针时,会导致这种错误.也就是使用了一个没有赋值的指针.
splint支持一种特别的注释.这种注释写在C程序代码中,用于对程序进行特殊说明.
如下面这段程序.使用了/*@null@*/进行了说明,表示说明*s的值可能会是NULL.
//null.cchar firstChar1 (/*@null@*/ char *s){return *s;}char firstChar2 (/*@null@*/ char *s){if (s ==NULL) return '\0';return *s;}//END
使用splint扫描这个程序时,会输出:
# splint null.cSplint 3.1.1 --- 28 Apr 2005null.c: (in function firstChar1)null.c:3:11: Dereference of possibly null pointer s: *snull.c:1:35: Storage s may become nullFinished checking --- 1 code warning found由于firstChar1和firstChar2都使用了null说明,表示指针s可能是个NULL值.所以,splint会对s值的使用情况进行检查.因为firstChar2函数中,对s的值进行了NULL的判断.所以,没有对firstChar2函数的指针s输出警告信息.
C语言中,要求先定义变量,而后才可使用.所以,当使用一个没有定义的变量时,编译就会出错.
如下例,使用/*@in@*/说明的变量,表示必须进行定义.使用/*@out@*/说明的变量,表示在 执行过此函数后,这个变量就进行了定义.
// usedef.cextern void setVal (/*@out@*/ int *x);extern int getVal (/*@in@*/ int *x);extern int mysteryVal (int *x);int dumbfunc (/*@out@*/ int *x, int i){if (i > 3) return *x;else if (i > 1)return getVal (x);else if (i == 0)return mysteryVal (x);else{setVal (x);return *x;}}// END
使用splint检查usedef.c
$ splint usedef.cSplint 3.1.1 --- 28 Apr 2005usedef.c: (in function dumbfunc)usedef.c:7:19: Value *x used before definitionAn rvalue is used that may not be initialized to a value on some executionpath. (Use -usedef to inhibit warning)usedef.c:9:18: Passed storage x not completely defined (*x is undefined):getVal (x)Storage derivable from a parameter, return value or global is not defined.Use /*@out@*/ to denote passed or returned storage which need not be defined.(Use -compdef to inhibit warning)usedef.c:11:22: Passed storage x not completely defined (*x is undefined):mysteryVal (x)Finished checking --- 3 code warnings//错误原因: 由于程序中没有对x进行定义,所以报未定义错误.但setVal()使用了/*@out@*/说明,所以在setVal(x);和return x;中,没有报未定义错误.
C语言中的数据类型较多,各个之间有些细微差别.splint也可以对变量类型进行检查.
示例1:
//bool.cint f (int i, char *s,bool b1, bool b2){if (i = 3) return b1;if (!i || s) return i;if (s) return 7;if (b1 == b2)return 3;return 2;}//END
使用splint进行检查:
$ splint bool.cSplint 3.1.1 --- 28 Apr 2005bool.c: (in function f)bool.c:4:5: Test expression for if is assignment expression: i = 3The condition test is an assignment expression. Probably, you mean to use ==instead of =. If an assignment is intended, add an extra parentheses nesting(e.g., if ((a = b)) ...) to suppress this message. (Use -predassign toinhibit warning)// 错误原因: if语句中的条件表达式是一个赋值语句.bool.c:4:5: Test expression for if not boolean, type int: i = 3Test expression type is not boolean or int. (Use -predboolint to inhibitwarning)// 错误原因: if语句中的条件表达式的返回值,不是布尔型,而是整型.bool.c:4:8: Return value type bool does not match declared type int: b1Types are incompatible. (Use -type to inhibit warning)// 错误原因: 返回值是布尔型,而不是整型.bool.c:5:6: Operand of ! is non-boolean (int): !iThe operand of a boolean operator is not a boolean. Use +ptrnegate to allow !to be used on pointers. (Use -boolops to inhibit warning)// 错误原因: "!"操作符的操作数不是布尔型,而是整型i.bool.c:5:11: Right operand of || is non-boolean (char *): !i || s// 错误原因: "||"操作符的右操作数不是布尔型,而是字符指针.bool.c:7:5: Use of == with boolean variables (risks inconsistency because ofmultiple true values): b1 == b2Two bool values are compared directly using a C primitive. This may produceunexpected results since all non-zero values are considered true, sodifferent true values may not be equal. The file bool.h (included insplint/lib) provides bool_equal for safe bool comparisons. (Use -boolcompareto inhibit warning)// 错误原因: 使用"=="对两个布尔型进行比较.应该使用"&&".Finished checking --- 6 code warnings
示例2:
//malloc1.c#include <stdlib.h>#include <stdio.h>int main(void){char *some_mem;int size1=1048576;some_mem=(char *)malloc(size1);printf("Malloed 1M Memory!\n");free(some_mem);exit(EXIT_SUCCESS);}//END
使用splint检查malloc1.c
$ splint malloc1.cSplint 3.1.1 --- 28 Apr 2005malloc1.c: (in function main)malloc1.c:9:25: Function malloc expects arg 1 to be size_t gets int: size1To allow arbitrary integral types to match any integral type, use+matchanyintegral.Finished checking --- 1 code warning
修改变量size1的定义为:
size_t size1=1048576;
再使用splint进行检查.
$ splint malloc1.cSplint 3.1.1 --- 28 Apr 2005Finished checking --- no warnings
没有检查到错误.
缓冲区溢出错误是一种非常危险的C语言错误,大部分安全漏洞都与它有关.splint可以
对缓冲区的使用进行检查.报告溢出或越界错误.
示例1:
//over.cint main(){int buf[10];buf[10] = 3;retrun 0;}//END
使用splint进行检查
$ splint over.c +bounds +showconstraintlocationSplint 3.1.1 --- 21 Apr 2006Command Line: Setting +showconstraintlocation redundant with current valueover.c: (in function main)over.c:6:3: Likely out-of-bounds store:buf[10]Unable to resolve constraint:requires 9 >= 10needed to satisfy precondition:requires maxSet(buf @ over.c:6:3) >= 10A memory write may write to an address beyond the allocated buffer. (Use-likely-boundswrite to inhibit warning)Finished checking --- 1 code warning
数组buf的大小是10字节.最大可使用的元素位置为buf[9],但程序中使用了buf[10].
所以报错.
示例2:
// bound.cvoid updateEnv(char *str){char *tmp;tmp = getenv("MYENV");if(tmp != NULL) strcpy(str,tmp);}void updateEnvSafe(char *str, size_t strSize){char *tmp;tmp = getenv("MYENV");if(tmp != NULL){strncpy(str, tmp, strSize -1);str[strSize - 1] = '/0';}}//END
$ splint bound.c +bounds +showconstraintlocationSplint 3.1.1 --- 21 Apr 2006Command Line: Setting +showconstraintlocation redundant with current valuebound.c: (in function updateEnv)bound.c:6:19: Possible out-of-bounds store:strcpy(str, tmp)Unable to resolve constraint:requires maxSet(str @ bound.c:6:26) >= maxRead(getenv("MYENV") @bound.c:5:9)needed to satisfy precondition:requires maxSet(str @ bound.c:6:26) >= maxRead(tmp @ bound.c:6:30)derived from strcpy precondition: requires maxSet(<parameter 1>) >=maxRead(<parameter 2>)A memory write may write to an address beyond the allocated buffer. (Use-boundswrite to inhibit warning)错误原因: 由于使用strcpy函数时,没有指定复制字符串的长度,所以可能导致缓冲区溢出.后面的updateEnvSafe函数,使用了strncpy进行字符串复制,避免了这种情况.
联系客服