350 likes | 404 Views
Finding Bugs with PC-lint A Static Analysis Tool for C/C++. Philippe CHARMAN charman@fr.ibm.com http://users.polytech.unice.fr/~charman/. Last update: 05-15-2014. PC-lint & FlexeLint.
E N D
Finding Bugs with PC-lintA Static Analysis Tool for C/C++ Philippe CHARMAN charman@fr.ibm.com http://users.polytech.unice.fr/~charman/ Last update: 05-15-2014
PC-lint & FlexeLint • Check the source code of C/C++ and detect any potential issue: bug, inconsistency, non-portable code, suspicious code, etc. • The analyse isperformed on all files • Pseudo-execution of the code • Available on Windows (PC-lint) and Unix (FlexeLint) • License: About 500$, no trial license
PC-lint & FlexeLint • An interactive demoisavailablehere: http://www.gimpel-online.com/OnlineTesting.html by clicking on Do-It-YourselfExample (C++)
PC-lint & FlexeLint • For each of the following 10 examples: • Without compiling the code, try to find the bug • If you haven’t found the bug, compile the code and look if the compiler reports a warning • If you still haven’t found the bug, copy-paste the code in the PC-lint demo window and click on the button « Analyse Code » to see the messages reported by PC-lint. • Fix the code, compile it and check the execution results and check the fix in the window demo
Example 1 #include <stdio.h> int main() { int i; int a[] = {1,2,3}; int n = sizeof(a)/sizeof(int); for (i=0;i<=n;i++) printf("a[%d]=%d\n",i,a[i]); return 0; }
Analysing example 1 1 #include <stdio.h>2 3 int main()4 { 5 int i; 6 int a[] = {1,2,3}; 7 int n = sizeof(a)/sizeof(int);8 for (i=0;i<=n;i++) _9 printf("a[%d]=%d\n",i,a[i]); diy.cpp 9 Warning 661: Possible access of out-of-bounds pointer (1 beyond end of data) by operator '[' [Reference: file diy.cpp: lines 7, 8, 9]10 return 0;11 }12
Explanation of warning 661 661 possible access of out-of-bounds pointer ('Integer' beyond end of data) by operator 'String' An out-of-bounds pointer may have been accessed. See message 415 for a description of the parameters Integer and String. For example: int a[10]; if( n <= 10 ) a[n] = 0; Here the programmer presumably should have written n<10. This message is similar to messages 415 and 796 but differs from them by the degree of probability.
Example 2 #include <string.h> class X { int *p; public: X() { p = new int[20]; } void init() { memset( p, 20, 'a' ); } ~X() { delete p; } };
Analysing example 2 1 #include <string.h> 2 3 class X { 4 int *p; 5 public: 6 X() 7 { p = new int[20]; } 8 void init() 9 { memset( p, 20, 'a' ); }10 ~X()11 { delete p; } _ 7 { p = new int[20]; }diy.cpp 7 Info 1732: new in constructor for class 'X' which has no assignment operatordiy.cpp 7 Info 1733: new in constructor for class 'X' which has no copy constructor _ 9 { memset( p, 20, 'a' ); }diy.cpp 9 Warning 669: Possible data overrun for function 'memset(void *, int, unsigned int)', argument 3 (size=97) exceeds argument 1 (size=80) [Reference: file diy.cpp: lines 7, 9] _11 { delete p; }diy.cpp 11 Warning 424: Inappropriate deallocation (delete) for 'new[]' data12 };
Explanation of warning 669 669 Possible data overrun for function 'Symbol', argument Integer exceeds argument IntegerReference This message is for data transfer functions such as memcpy, strcpy, fgets, etc. when the size indicated by the first cited argument (or arguments) can possibly exceed the size of the buffer area cited by the second. The message may also be issued for user functions via the -function option See Function Mimicry and Value Tracking.
Example 3 #include <stdio.h> int quotient(int *q, int *p) { if(*p) return *q/*p /* compute ratio */ ; else return *q; } int main() { int n = 20, m = 4; int q = quotient( &n, &m ); printf( "%d/%d = %d\n", n, m, q ); // displays 20 instead of 5 return 0; }
Analysing example 3 1 #include <stdio.h>2 3 int quotient(int *q, int *p) { _4 if(*p) return *q/*p /* compute ratio */ ;diy.cpp 4 Warning 602: Comment within comment5 else return *q; _6 }diy.cpp 6 Info 818: Pointer parameter 'p' (line 3) could be declared as pointing to constdiy.cpp 6 Info 818: Pointer parameter 'q' (line 3) could be declared as pointing to const7 8 int main() {9 int n = 20, m = 4;10 int q = quotient( &n, &m );11 printf( "%d/%d = %d\n", n, m, q );12 return 0;13 }
Explanation of warning 602 02 Comment within comment The sequence /* was found within a comment. Was this deliberate? Or was a comment end inadvertently omitted? If you want PC-lint/FlexeLint to recognize nested comments you should set the Nested Comment flag using the +fnc option. Then this warning will not be issued. If it is your practice to use the sequence: /* /* */ then use -e602.
Example 4 #include <stdio.h> #include <stdlib.h> const char *flowers[] = { "rose", "tulip", "daisy" "petunia", "orchid", "lily" }; int main() { int i; int choice; for( i = 0; i < 25; i++ ) { choice = rand() % 6; printf( "%s\n", flowers[choice] ); } return 0; }
Analysing example 4 1 #include <stdio.h> 2 #include <stdlib.h> 3 4 const char *flowers[] = { 5 "rose", "tulip", "daisy" _ 6 "petunia", "orchid", "lily"diy.cpp 6 Info 786: String concatenation within initializer 7 }; 8 9 int main() { 10 int i; 11 int choice; 12 13 for( i = 0; i < 25; i++ ) { 14 choice = rand() % 6; 15 printf( "%s\n", flowers[choice] ); 16 } 17 return 0; 18 }
Explanation of info 786 786 String concatenation within initializer Although it is perfectly 'legal' to concatenate string constants within an initializer, this is a frequent source of error. Consider: char *s[] = { "abc" "def" }; Did the programmer intend to have an array of two strings but forget the comma separator? Or was a single string intended?
Example 5 The code is supposed to return the sum of all elements of a matrix #include <stdio.h> int a[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; int sum( int a[3][3] ) { int i=0, j=0, k=0; for( i = 0; i < 3; i++ ) { for( i = 0; i < 3; i++ ) { k += a[i][j]; } } return k; } int main() { printf( "The triangular sum is %d\n", sum(a) ); // displays 12 return 0; }
Analysing example 5 1 #include <stdio.h> 2 3 int a[3][3] = { {1,2,3}, {4,5,6}, {7,8,9} }; _ 4 int sum( int a[3][3] ) { diy.cpp 4 Warning 578: Declaration of symbol 'a' hides symbol 'a' (line 3)5 int i=0, j=0, k=0; _ 6 for( i = 0; i < 3; i++ ) { diy.cpp 6 Info 838: Previously assigned value to variable 'i' has not been used _ 7 for( i = 0; i < 3; i++ ) { diy.cpp 7 Warning 445: Reuse of for loop variable 'i' at 'line 6' could cause chaos 8 k += a[i][j]; 9 }10 } _11 return k; diy.cpp 11 Info 850: for loop index variable 'i' whose type category is 'integral' is modified in body of the for loop that began at 'line 6' _12 } diy.cpp 12 Info 818: Pointer parameter 'a' (line 4) could be declared as pointing to const13 14 int main() {15 printf( "The triangular sum is %d\n", sum(a) );16 return 0;17 }
Explanation of warning 445 445 reuse of for loop variable 'Symbol' at 'Location' could cause chaos A for loop nested within another for loop employed the same loop variable. For example: for( i = 0; i < 100; i++ ) { ... for( i = 0; i < n; i++ ) { ... } }
Example 6 //lint -passes(4) int f( int n ) { return 10/n; } int g( int n ) { if( n == -1 ) return 0; return f(n) + g( n - 1 ); } int main() { return g( 3 ); // a crash appears }
Analysing example 6 /// Start of Pass 4 ///--- Module: diy.cpp (C++) 1 //lint -passes(4) 2 3 int f( int n ) 4 { 5 return 10/n;During Specific Walk:diy.cpp 16 g(3) #2diy.cpp 11 g(2) #3diy.cpp 11 g(1) #4diy.cpp 11 g(0) #5diy.cpp 11 f(0) #5diy.cpp 5 Warning 414: Possible division by 0 [Reference: file diy.cpp: lines 11, 16] 6 } 7 8 int g( int n ) 9 { 10 if( n == -1 ) return 0; 11 return f(n) + g( n - 1 ); 12 } 13 14 int main() 15 { 16 return g( 3 ); 17 }
Explanation of warning 414 414 Possible division by 0 The second argument to either the division operator (/) or the modulus operator (%) may be zero. Information is taken from earlier statements including assignments, initialization and tests.
Example 7 #include <stdio.h> struct { int bit:1; } s; bool f() { if( s.bit > 0 ) return true; else return false; } int main() { s.bit = 1; if( f() ) printf( "the bit is ON\n" ); else printf( "the bit is OFF\n" ); // this is what is displayed return 0; }
Analysing example 7 1 #include <stdio.h> 2 _ 3 struct { int bit:1; } s;diy.cpp 3 Info 846: Signedness of bit-field is implementation defineddiy.cpp 3 Info 806: Small bit field is signed rather than unsigned 4 5 bool f() { _ 6 if( s.bit > 0 ) return true;diy.cpp 6 Warning 685: Relational operator '>' always evaluates to 'false' 7 else return false; 8 } 9 10 int main() { _ 11 s.bit = 1;diy.cpp 11 Warning 542: Excessive size for bit field 12 if( f() ) printf( "the bit is ON\n" ); 13 else printf( "the bit is OFF\n" ); 14 return 0; 15 }
Explanation of info 846 846 Signedness of bit-field is implementation defined A bit-field was detected having the form: int a:5; Most bit fields are more useful when they are unsigned. If you want to have a signed bit field you must explicitly indicate this as follows: signed int a:5; The same also holds for typedef's. For example, typedef int INT; typedef signed int SINT; struct { INT a:16; // Info 846 SINT b:16; // OK }: It is very unusual in C or C++ to distinguish between signed int and just plain int. This is one of those rare cases.
Example 8 #include <stdio.h> struct { int a[3], b; } w[] = { { 1, 2, 3 }, 2 }; int main() { printf( "w[0].b = %d\n", w[0].b ); // displays 0 return 0; }
Analysing example 8 1 #include <stdio.h> 2 3 struct { int a[3], b; } w[] = { { 1, 2, 3 }, 2 }; diy.cpp 3 Warning 651: Potentially confusing initializer diy.cpp 3 Info 785: Too few initializers for aggregate 'unknown-name‘ of type '{...}‘ diy.cpp 3 Info 785: Too few initializers for aggregate 'unknown-name‘ of type 'int [3]‘ diy.cpp 3 Info 785: Too few initializers for aggregate 'unknown-name‘ of type '{...} 4 5 int main() 6 { 7 printf( "w[0].b = %d\n", w[0].b ); 8 return 0; 9 }
Explanation of warning 651 651 Potentially confusing initializer An initializer for a complex aggregate is being processed that contains some subaggregates that are bracketed and some that are not. ANSI recommends either "minimally bracketed" initializers in which there are no interior braces or "fully bracketed" initializers in which all interior aggregates are bracketed.
Example 9 class X { public: int upper; int lower; X( int init ) : lower( init ), upper( lower+1 ) {} }; X x(1); // x.upper is equal to 1 instead of 2
Analysing example 9 1 class X2 {3 public:4 int upper;5 int lower;6 X( int init ) : lower( init ), upper( lower+1 )7 {} _6 X( int init ) : lower( init ), upper( lower+1 )diy.cpp 6 Info 1729: Initializer inversion detected for member 'X::upper' _8 };diy.cpp 8 Info 1712: default constructor not defined for class 'X'9 10 X x(1);
Explanation of info 1729 1729 Initializer inversion detected for member 'Symbol‘ In a constructor initializer the order of evaluation is determined by the member order not the order in which the initializers are given. At least one of the initializers was given out of order. Was there a reason for this? Did the programmer think that by changing the order that he/she would affect the order of evaluation? Place the initializers in the order of their occurrence within the class so that there can be no mistaken assumptions.
Example 10 #include <iostream> using namespace std; struct WhiteHouse { int *p; WhiteHouse(int n) { p = new int; *p = n; } ~WhiteHouse() { delete p; } }; WhiteHouse ww(1912); void f() { WhiteHouse fdr(1932); fdr = ww; } int main() { f(); WhiteHouse gwb(2000); cout << *ww.p; // crash or displays 2000 return 0; }
Analysing example 10 1 #include <iostream>2 using namespace std;3 4 struct WhiteHouse {5 int *p;6 WhiteHouse(int n) { p = new int; *p = n; }7 ~WhiteHouse() { delete p; } _6 WhiteHouse(int n) { p = new int; *p = n; }diy.cpp 6 Info 1732: new in constructor for class 'WhiteHouse' which has no assignment operatordiy.cpp 6 Info 1733: new in constructor for class 'WhiteHouse' which has no copy constructor _8 };diy.cpp 8 Info 1712: default constructor not defined for class 'WhiteHouse'9 10 WhiteHouse ww(1912);11 12 void f() { WhiteHouse fdr(1932); fdr = ww; }13 14 int main() {15 f();16 WhiteHouse gwb(2000);17 cout << *ww.p; // crash or displays 200018 return 0; _19 } diy.cpp 19 Info 1788: Variable 'gwb' (line 16) (type 'WhiteHouse') is referenced only by its constructor or destructor
Explanation of info 1732 1732 new in constructor for class 'Name' which has no assignment operator Within a constructor for the cited class, there appeared a new. However, no assignment operator was declared for this class. Presumably some class member (or members) points to dynamically allocated memory. Such memory is not treated properly by the default assignment operator. Normally a custom assignment operator would be needed. Thus, if x and y are both of type Name x = y; will result in pointer duplication. A later delete would create chaos.