3 StaticAnalysisPREfast
3 StaticAnalysisPREfast
1
Static analysis aka source code analysis
Automated analysis at compile time to find potential bugs
Broad range of techniques, from light- to heavyweight:
1. simple syntactic checks, incl. grep or CTRL-F
grep " gets(" *.cpp
2. type checking
eg. adding an int and a bool
2
Static analysis in the SDLC
3
Why static analysis? (1)
Traditional methods of finding errors:
• testing
• code inspection
4
Evolution of quality assurance at Microsoft
5
False positives & false negatives
6
BOOL AddTail(LPVOID p) { ... if(queue.GetSize() >= this->_limit); { while(queue.GetSize() > this->_limit-1) { ::WaitForSingleObject(handles[SemaphoreIndex], 1); q
7
More interesting static analyses
8
OOL AddTail(LPVOID p) { ... if(queue.GetSize() >= this->_limit); { while(queue.GetSize() > this->_limit-1) { ::WaitForSingleObject(handles[SemaphoreIndex], 1); qu
9
Spot the security flaw!
static OSStatus SSLVerifySignedServerKeyExchange (SSLContext
*ctx, bool isRsa, SSLBuffer signedParams, uint8_t *signature,
UInt16 signatureLen)
{ OSStatus err;
..
if((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
goto fail;
if((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
goto fail;
goto fail;
if((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
goto fail;
...
fail:
SSLFreeBuffer(&signedHashes);
SSLFreeBuffer(&hashCtx);
}
Infamous goto bug in iOS implementation of TLS
• Dead code analysis would easily reveal this flaw!
• Or simply code style that insists you always use { } for branches
10
OOL AddTail(LPVOID p) { ... if(queue.GetSize() >= this->_limit); { while(queue.GetSize() > this->_limit-1) { ::WaitForSingleObject(handles[SemaphoreIndex], 1); qu
// main loop
while (true) {
get_readings(buf,buf2);
perform_engine_control(buf,buf2);
}
}
11
OOL AddTail(LPVOID p) { ... if(queue.GetSize() >= this->_limit); { while(queue.GetSize() > this->_limit-1) { ::WaitForSingleObject(handles[SemaphoreIndex], 1); qu
possible integer
Spot the defects! overflow
(hard to check for
code analyser, but
void start_engine_control() { for a constant is
char* buf2 = malloc (2*SOME_CONSTANT); may be doable)
char* buf = malloc (SOME_CONSTANT);
start_engine();
memset(buf2, 0, SOME_CONSTANT);
// initialise first half of buf2 to 0
// main loop
No check if mallocs succeeded!!
while (true) { (easier to check syntactically)
get_readings(buf,buf2);
perform_engine_control(buf,buf2);
}
}
12
OOL AddTail(LPVOID p) { ... if(queue.GetSize() >= this->_limit); { while(queue.GetSize() > this->_limit-1) { ::WaitForSingleObject(handles[SemaphoreIndex], 1); qu
Typically, the place where malloc fails is the place to think about
what to do.
The alternative is not check the result of malloc here, and simply let
perform_engine_control segfault or let this function check for
null arguments, but there we have even less clue on what to do.
13
Spot the defect :-)
14
Limits of static analyses
Does
if (i < 5 ) { c = 5; }
if ((i < 0) || (i*i > 20 )){ c = 6; }
initialise c?
15
Example source code analysis tools
Such tools can be useful, but… a fool with a tool is still a fool
16
PREfast & SAL
17
PREfast & SAL
• Developed by Microsoft as part of major push to improve
quality assurance
• PREfast is a lightweight static analysis tool for C(++)
– only finds bugs within a single procedure
• SAL (Standard Annotation Language) is a language for
annotating C(++) code and libraries
– SAL annotations improve the results of PREfast
• more checks
• more precise checks
18
PREfast checks
19
PREfast example
20
PREfast annotations for buffers
21
SAL annotations for buffer parameters
22
SAL annotations for buffer sizes
specified with suffix of _In_ _Out_ _Inout_ _Ret_
count_(size) bytecount_(size)
the readable size in elements
23
SAL annotations for nullness of parameters
opt_
parameter may be null, and procedure will check for this
24
PREfast example
25
PREfast example
26
Example annotation & analysis
void work() {
int elements[200];
wrap(elements, 200);
}
int *wrap(int *buf, int len) {
int *buf2 = buf;
int len2 = len;
zero(buf2, len2);
return buf;
}
void zero( int *buf,
int len){
int i;
for(i = 0; i <= len; i++) buf[i] = 0;
}
27
Example annotation & analysis
29
Tainting annotations in pre/postconditions
30
Warning: changing SAL syntax
31
Benefits of annotations
• Annotations express design intent
for human reader & for tools
32
Drawback of annotations
• The effort of having to write them...
– who's going to annotate the millions of lines of (existing)
code?
• Microsoft’s approach
– requiring annotation on checking in new code
• rejecting any code that has char* without _count()
– incremental approach, in two ways:
1. beginning with few core annotations
2. checking them at every compile, not adding them in the
end
– build tools to infer annotations, eg SALinfer
• unfortunately, not available outside Microsoft
33
Static analysis in the workplace
• Static analysis is not for free:
– Even free open source tools cost time & effort to learn
to use
34
Criteria for success
• Acceptable level of false positives
– acceptable level of false negatives also interesting, but
less important
• Not too many warnings
– this turns off potential users
• Good error reporting
– context & trace of error
• Bugs should be easy to fix
• You should be able to teach the tool
– to suppress false positives
– add design intent via assertions
35
Limitations of static analysis
Big challenges for static analysis are
1. The heap (aka dynamic memory) poses a major challenge
for static analysis
• The heap is a very dynamic structure evolving at runtime;
what is a good abstraction at compile-time?
2. Concurrency
36