SlideShare a Scribd company logo
STATIC CODE ANALYSIS:
WHAT? HOW? WHY?
Maxim Stefanov
PVS-Studio, C++/Java developer, Tula
1
About The Speaker
• Maxim Stefanov (stefanov@viva64.com)
• C++/Java developer in PVS-Studio
• Duties:
• Develops C++ core of the analyzer
• Develops Java analyzer
2
• Theory
 The importance of code quality (bugs, vulnerabilities, ...)
 Defect prevention methods
• From code review to static code analysis
 Code review VS Static code analysis
• Static code analysis techniques
• Some examples of defects in real projects
• More about static analysis
• Summary
3
We’ll Talk About...
• Perception
• Enhancement
• Support
• Absence of bugs
• ... et cetera
4
High-Quality Code
•It prevents technical debt if the project is new
•It helps not to lose users if the project is mature
5
Why Is Code Quality Important?
Cost to Fix a Defect
•Helps to find high-level bugs without shooting
yourself in the foot
•Allows to share experience with padawans
•Together you’ll learn some new things about
the project and its secrets
7
Code review
•Code review is very expensive:
– Expectation: «We’ll review this edit for
10-15 min»
– Reality – sometimes code review takes
hours
•You get tired quickly
8
But...
Pros Cons
Finds defects before code review You can’t find high-level
errors
An analyzer can’t get tired, it’s ready to work at any time False positives
You can find errors without even knowing about such a
pattern
You can find errors which are difficult to notice
9
Static Code Analysis Comes to Rescue
•Pattern-based analysis
•Type inference
•Method annotations
•Data-flow analysis
•Symbolic execution
10
Static Code Analysis Techniques
@Override
public boolean equals(Object obj) {
....
return index.equals(other.index)
&& type.equals(other.type)
&& version == other.version
&& found == other.found
&& tookInMillis == tookInMillis
&& Objects.equals(terms, other.terms);
}
11
Pattern-based analysis
Type inference
interface Human { .... }
class Parent implements Human{ .... }
class Child extends Parent { .... }
....
class Animal { ... }
....
boolean someMethod(List<Child> list, Animal animal)
{
if (list.remove(animal))
return false;
....
} 12
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
13
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
14
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
15
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
16
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
17
Method annotations
Class("java.lang.Math")
- Function("max", Type::Int32, Type::Int32)
.Pure()
.Set(FunctionClassification::NoDiscard)
.Requires(NotEquals(Arg1, Arg2))
.Returns(Arg1, Arg2,[](const Int &v1,
const Int &v2)
{
return v1.Max(v2);
})
18
int test(int a, int b) {
....
Math.max(a, b);
....
}
19
Method annotations
int test(int a, int b)
{
....
return Math.max(a, a);
}
20
Method annotations
int test(int a, int b)
{
if (a > 5 && b < 2) {
// a = [6..INT_MAX]
// b = [INT_MIN..1]
if (Math.max(a, b) > 0)
{....}
}
....
}
21
Method annotations
Data-flow analysis
void func(int x) {
// x: [-2147483648..2147483647] //1
if (x > 3) {
// x: [4..2147483647] //2
if (x < 10) {
// x: [4..9] //3
}
} else {
// x: [-2147483648..3] //4
}
}
22
Symbolic execution
int someMethod(int A, int B)
{
if (A == B)
return 10 / (A - B);
return 1;
}
23
Some Examples of Defects in Real Projects
24
PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header,
uintptr_t header_mask, int value)
{
char buf[128];
sprintf(buf, "%d", value);
return set_value_buffer(dest, header, header_mask, buf);
}
StarEngine, C++
PVS-Studio: V614 Uninitialized buffer 'buf' used. pugixml.cpp 3362
It Came Up Unexpectedly...
25
It Came Up Unexpectedly...
PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header,
uintptr_t header_mask, int value)
{
char buf[128];
sprintf(buf, "%d", value);
return set_value_buffer(dest, header, header_mask, buf);
}
PVS-Studio: V614 Uninitialized buffer 'buf' used. pugixml.cpp 3362
#define schar char
#define suchar unsigned schar
#define sprintf std::printf
#define satof atof
#define satoi atoi
26
StarEngine, C++
PVS-Studio: V6007 Expression 'StringUtils.isNotEmpty("handleTabKey")' is always true.
SourceCodeEditorLoader.java 60
Copy Paste
public void loadComponent() {
....
String handleTabKey = element.attributeValue("handleTabKey");
if (StringUtils.isNotEmpty("handleTabKey")) {
resultComponent.setHandleTabKey(....);
}
....
}
27
CUBA Platform, Java
V778 Two similar code fragments were found. Perhaps, this is a typo and 'cap_resy'
variable should be used instead of 'cap_resx'. cyapa.c 1458
Copy Paste
static int
cyapa_raw_input(struct cyapa_softc *sc, ....)
{
....
if (sc->delta_x > sc->cap_resx) sc->delta_x = sc->cap_resx;
if (sc->delta_x < -sc->cap_resx) sc->delta_x = -sc->cap_resx;
if (sc->delta_y > sc->cap_resx) sc->delta_y = sc->cap_resy;
if (sc->delta_y < -sc->cap_resy) sc->delta_y = -sc->cap_resy;
....
}
28
FreeBSD Kernel, C
V778 Two similar code fragments were found. Perhaps, this is a typo and 'cap_resy'
variable should be used instead of 'cap_resx'. cyapa.c 1458
Copy Paste
static int
cyapa_raw_input(struct cyapa_softc *sc, ....)
{
....
if (sc->delta_x > sc->cap_resx) sc->delta_x = sc->cap_resx;
if (sc->delta_x < -sc->cap_resx) sc->delta_x = -sc->cap_resx;
if (sc->delta_y > sc->cap_resx) sc->delta_y = sc->cap_resy;
if (sc->delta_y < -sc->cap_resy) sc->delta_y = -sc->cap_resy;
....
}
29
FreeBSD Kernel, C
char c;
printf("%s .... ");
rewind(blk_alloc_file);
while ((c = fgetc(blk_alloc_file)) != EOF)
{
fputc(c, base_fs_file);
}
Android, C
PVS-Studio: V739 CWE-20 EOF should not be compared with a value of the 'char' type. The
'(c = fgetc(blk_alloc_file))' should be of the 'int' type. blk_alloc_to_base_fs.c 61
30
Unlucky Character
Compiler Deletes Code to Wipe Buffer
static void FwdLockGlue_InitializeRoundKeys() {
unsigned char keyEncryptionKey[KEY_SIZE];
....
memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data.
}
PVS-Studio: V597 CWE-14 The compiler could delete the 'memset' function call, which is
used to flush 'keyEncryptionKey' buffer. The memset_s() function should be used to erase
the private data. FwdLockGlue.c 102
31
Android, C
How to Blend Static Analysis into Software
Development Process
• Every developer has a static analysis tool in their
workplace
• Analysis of the entire codebase during night builds. If
suspicious code is found, the guilty one will receive an
email
32
How to Start Using Static Analysis Tools in Big
Projects and Keep Your Cool
1. Check the project
2. Put all issued warnings in a special suppression file to indicate that
now you are not interested in all issued warnings
3. Put the markup file into the version control system
4. Run the analyzer and receive warnings only for new or changed
code
5. PROFIT!
33
Summary
• Static analysis helps to immediately find some errors while the cost to
fix them is low
• Static analysis should be used regularly
• You can start using the analysis right away and fix some old errors
after
• Static analysis is not a silver bullet, it’s important to use different
techniques
34
Thank you for your attention!
35
Useful Links
The podcast about static analysis
with PVS-Studio founders [RU]
The list of static analysis tools
The PVS-Studio site

More Related Content

Static code analysis: what? how? why?

  • 1. STATIC CODE ANALYSIS: WHAT? HOW? WHY? Maxim Stefanov PVS-Studio, C++/Java developer, Tula 1
  • 2. About The Speaker • Maxim Stefanov (stefanov@viva64.com) • C++/Java developer in PVS-Studio • Duties: • Develops C++ core of the analyzer • Develops Java analyzer 2
  • 3. • Theory  The importance of code quality (bugs, vulnerabilities, ...)  Defect prevention methods • From code review to static code analysis  Code review VS Static code analysis • Static code analysis techniques • Some examples of defects in real projects • More about static analysis • Summary 3 We’ll Talk About...
  • 4. • Perception • Enhancement • Support • Absence of bugs • ... et cetera 4 High-Quality Code
  • 5. •It prevents technical debt if the project is new •It helps not to lose users if the project is mature 5 Why Is Code Quality Important?
  • 6. Cost to Fix a Defect
  • 7. •Helps to find high-level bugs without shooting yourself in the foot •Allows to share experience with padawans •Together you’ll learn some new things about the project and its secrets 7 Code review
  • 8. •Code review is very expensive: – Expectation: «We’ll review this edit for 10-15 min» – Reality – sometimes code review takes hours •You get tired quickly 8 But...
  • 9. Pros Cons Finds defects before code review You can’t find high-level errors An analyzer can’t get tired, it’s ready to work at any time False positives You can find errors without even knowing about such a pattern You can find errors which are difficult to notice 9 Static Code Analysis Comes to Rescue
  • 10. •Pattern-based analysis •Type inference •Method annotations •Data-flow analysis •Symbolic execution 10 Static Code Analysis Techniques
  • 11. @Override public boolean equals(Object obj) { .... return index.equals(other.index) && type.equals(other.type) && version == other.version && found == other.found && tookInMillis == tookInMillis && Objects.equals(terms, other.terms); } 11 Pattern-based analysis
  • 12. Type inference interface Human { .... } class Parent implements Human{ .... } class Child extends Parent { .... } .... class Animal { ... } .... boolean someMethod(List<Child> list, Animal animal) { if (list.remove(animal)) return false; .... } 12
  • 13. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 13
  • 14. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 14
  • 15. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 15
  • 16. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 16
  • 17. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 17
  • 18. Method annotations Class("java.lang.Math") - Function("max", Type::Int32, Type::Int32) .Pure() .Set(FunctionClassification::NoDiscard) .Requires(NotEquals(Arg1, Arg2)) .Returns(Arg1, Arg2,[](const Int &v1, const Int &v2) { return v1.Max(v2); }) 18
  • 19. int test(int a, int b) { .... Math.max(a, b); .... } 19 Method annotations
  • 20. int test(int a, int b) { .... return Math.max(a, a); } 20 Method annotations
  • 21. int test(int a, int b) { if (a > 5 && b < 2) { // a = [6..INT_MAX] // b = [INT_MIN..1] if (Math.max(a, b) > 0) {....} } .... } 21 Method annotations
  • 22. Data-flow analysis void func(int x) { // x: [-2147483648..2147483647] //1 if (x > 3) { // x: [4..2147483647] //2 if (x < 10) { // x: [4..9] //3 } } else { // x: [-2147483648..3] //4 } } 22
  • 23. Symbolic execution int someMethod(int A, int B) { if (A == B) return 10 / (A - B); return 1; } 23
  • 24. Some Examples of Defects in Real Projects 24
  • 25. PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) { char buf[128]; sprintf(buf, "%d", value); return set_value_buffer(dest, header, header_mask, buf); } StarEngine, C++ PVS-Studio: V614 Uninitialized buffer 'buf' used. pugixml.cpp 3362 It Came Up Unexpectedly... 25
  • 26. It Came Up Unexpectedly... PUGI__FN bool set_value_convert(char_t*& dest, uintptr_t& header, uintptr_t header_mask, int value) { char buf[128]; sprintf(buf, "%d", value); return set_value_buffer(dest, header, header_mask, buf); } PVS-Studio: V614 Uninitialized buffer 'buf' used. pugixml.cpp 3362 #define schar char #define suchar unsigned schar #define sprintf std::printf #define satof atof #define satoi atoi 26 StarEngine, C++
  • 27. PVS-Studio: V6007 Expression 'StringUtils.isNotEmpty("handleTabKey")' is always true. SourceCodeEditorLoader.java 60 Copy Paste public void loadComponent() { .... String handleTabKey = element.attributeValue("handleTabKey"); if (StringUtils.isNotEmpty("handleTabKey")) { resultComponent.setHandleTabKey(....); } .... } 27 CUBA Platform, Java
  • 28. V778 Two similar code fragments were found. Perhaps, this is a typo and 'cap_resy' variable should be used instead of 'cap_resx'. cyapa.c 1458 Copy Paste static int cyapa_raw_input(struct cyapa_softc *sc, ....) { .... if (sc->delta_x > sc->cap_resx) sc->delta_x = sc->cap_resx; if (sc->delta_x < -sc->cap_resx) sc->delta_x = -sc->cap_resx; if (sc->delta_y > sc->cap_resx) sc->delta_y = sc->cap_resy; if (sc->delta_y < -sc->cap_resy) sc->delta_y = -sc->cap_resy; .... } 28 FreeBSD Kernel, C
  • 29. V778 Two similar code fragments were found. Perhaps, this is a typo and 'cap_resy' variable should be used instead of 'cap_resx'. cyapa.c 1458 Copy Paste static int cyapa_raw_input(struct cyapa_softc *sc, ....) { .... if (sc->delta_x > sc->cap_resx) sc->delta_x = sc->cap_resx; if (sc->delta_x < -sc->cap_resx) sc->delta_x = -sc->cap_resx; if (sc->delta_y > sc->cap_resx) sc->delta_y = sc->cap_resy; if (sc->delta_y < -sc->cap_resy) sc->delta_y = -sc->cap_resy; .... } 29 FreeBSD Kernel, C
  • 30. char c; printf("%s .... "); rewind(blk_alloc_file); while ((c = fgetc(blk_alloc_file)) != EOF) { fputc(c, base_fs_file); } Android, C PVS-Studio: V739 CWE-20 EOF should not be compared with a value of the 'char' type. The '(c = fgetc(blk_alloc_file))' should be of the 'int' type. blk_alloc_to_base_fs.c 61 30 Unlucky Character
  • 31. Compiler Deletes Code to Wipe Buffer static void FwdLockGlue_InitializeRoundKeys() { unsigned char keyEncryptionKey[KEY_SIZE]; .... memset(keyEncryptionKey, 0, KEY_SIZE); // Zero out key data. } PVS-Studio: V597 CWE-14 The compiler could delete the 'memset' function call, which is used to flush 'keyEncryptionKey' buffer. The memset_s() function should be used to erase the private data. FwdLockGlue.c 102 31 Android, C
  • 32. How to Blend Static Analysis into Software Development Process • Every developer has a static analysis tool in their workplace • Analysis of the entire codebase during night builds. If suspicious code is found, the guilty one will receive an email 32
  • 33. How to Start Using Static Analysis Tools in Big Projects and Keep Your Cool 1. Check the project 2. Put all issued warnings in a special suppression file to indicate that now you are not interested in all issued warnings 3. Put the markup file into the version control system 4. Run the analyzer and receive warnings only for new or changed code 5. PROFIT! 33
  • 34. Summary • Static analysis helps to immediately find some errors while the cost to fix them is low • Static analysis should be used regularly • You can start using the analysis right away and fix some old errors after • Static analysis is not a silver bullet, it’s important to use different techniques 34
  • 35. Thank you for your attention! 35
  • 36. Useful Links The podcast about static analysis with PVS-Studio founders [RU] The list of static analysis tools The PVS-Studio site