├── Makefile ├── README.txt ├── doc ├── test.c ├── test.rule ├── writing-rules-1.pdf ├── writing-rules-2.pdf └── writing-rules-3.pdf ├── gen_rules.py ├── rules-c.in ├── rules-cpp.in ├── rules.in ├── rules.xml └── tests ├── alloc.c ├── decl.c ├── env.c ├── expr.c ├── fio.c ├── int.c ├── misc.c ├── preproc.c ├── sig.c ├── str.c └── tmp.c /Makefile: -------------------------------------------------------------------------------- 1 | CHECK = ../cppcheck/cppcheck -q --rule-file=rules-c.xml --template=gcc --std=posix tests 2 | 3 | all: rules.xml 4 | @$(CHECK) 2> .test_result && diff .test_result test_result; rm .test_result 5 | 6 | rules-c.xml:rules.in rules-c.in gen_rules.py 7 | cat rules.in rules-c.in | @python gen_rules.py > $@ 8 | 9 | rules-cpp.xml:rules.in rules-cpp.in gen_rules.py 10 | cat rules.in rules-cpp.in | @python gen_rules.py > $@ 11 | 12 | test_result: rules-c.xml rules-cpp.xml 13 | @$(CHECK) 2> test_result 14 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Based on CERT C Coding Standard 2 | 3 | https://www.securecoding.cert.org/confluence/display/seccode/CERT+C+Coding+Standard 4 | -------------------------------------------------------------------------------- /doc/test.c: -------------------------------------------------------------------------------- 1 | void f() { 2 | if(af) free(af); 3 | if(af) free(a); 4 | } -------------------------------------------------------------------------------- /doc/test.rule: -------------------------------------------------------------------------------- 1 | 2 | 3 | if \( (\w+) \) { free \( \1 \) ; } 4 | 5 | style 6 | Redundant condition. It is valid to free a NULL pointer. 7 | 8 | -------------------------------------------------------------------------------- /doc/writing-rules-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptum/cppcheck-rules/db6a5e18e983bb73b71cc049a4d697e0bafb885f/doc/writing-rules-1.pdf -------------------------------------------------------------------------------- /doc/writing-rules-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptum/cppcheck-rules/db6a5e18e983bb73b71cc049a4d697e0bafb885f/doc/writing-rules-2.pdf -------------------------------------------------------------------------------- /doc/writing-rules-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptum/cppcheck-rules/db6a5e18e983bb73b71cc049a4d697e0bafb885f/doc/writing-rules-3.pdf -------------------------------------------------------------------------------- /gen_rules.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding:utf-8 -*- 3 | 4 | import sys 5 | 6 | step = 0 7 | tokenlist = None 8 | print '' 9 | for line in sys.stdin: 10 | line = line.strip("\n\r") 11 | if line.strip() == "" or line.strip()[:2] == "//": 12 | next 13 | ll = line.split() or [line] 14 | l = ll[0].strip() 15 | if l in ["style", "warning", "performance", "portability", "information", "error"]: 16 | severity = l 17 | step = 0 18 | tokenlist = None 19 | if len(ll) > 1: 20 | tokenlist = ll[1] 21 | else: 22 | step += 1 23 | if step == 2: 24 | summary = line 25 | print "" 26 | if tokenlist: 27 | print "\t%s" % tokenlist 28 | print "\t" % pattern 29 | print "\t" 30 | print "\t\t%s" % severity 31 | if summary == "none": 32 | print "\t\t" 33 | else: 34 | print "\t\t%s" % summary 35 | print "\t" 36 | print "" 37 | else: 38 | pattern = line 39 | -------------------------------------------------------------------------------- /rules-c.in: -------------------------------------------------------------------------------- 1 | style raw 2 | \( \) { 3 | DCL20-C. Always specify void even if a function accepts no arguments 4 | -------------------------------------------------------------------------------- /rules-cpp.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scriptum/cppcheck-rules/db6a5e18e983bb73b71cc049a4d697e0bafb885f/rules-cpp.in -------------------------------------------------------------------------------- /rules.in: -------------------------------------------------------------------------------- 1 | style simple 2 | if \( (\b\w+\b) \) { (?:g_)?free \( \b\1\b \) ; } 3 | Redundant condition. It is valid to free a NULL pointer. 4 | 5 | warning 6 | \bsystem \( 7 | ENV04-C. Do not call system() if you do not need a command processor 8 | 9 | //style define 10 | //(\+\+|\-\-) 11 | //Increment/decrement external variables inside define may have side effects. Prefer static inline functions. 12 | 13 | // Too many false-positives 14 | //style define 15 | //#define \w+ \( [^)]*(\b\w+\b) \) .*[^(,] \1 [^),] 16 | //Better wrap variables in define into () 17 | 18 | style define 19 | #define \w+ (?:const\s+)?(?:char|int|short|unsigned|unsigned int |float|double)(?: [*]+)?$ 20 | PRE03-C. Prefer typedefs to defines for encoding types 21 | 22 | style raw 23 | [?][?][=!('>/<-] 24 | PRE07-C. Avoid using repeated question marks 25 | 26 | //style define 27 | //#define \w+ \(.*?\) (?.] )(\b\w+\b)[^{};()'"]*[\[+,*/=-][^{};()'"]*(?:\b\1\b [+-]{2}|[+-]{2} \b\1\b) 56 | EXP30-C. Do not depend on order of evaluation between sequence points 57 | 58 | error normal 59 | strncpy \( (\w+) , \w+ , sizeof \( \1 \) \) 60 | The result of strncpy(a, b, sizeof(a)) will not be null-terminated 61 | 62 | style raw 63 | (? 2 | 3 | simple 4 | 5 | 6 | style 7 | Redundant condition. It is valid to free a NULL pointer. 8 | 9 | 10 | 11 | 12 | 13 | warning 14 | ENV04-C. Do not call system() if you do not need a command processor 15 | 16 | 17 | 18 | define 19 | 20 | 21 | style 22 | PRE03-C. Prefer typedefs to defines for encoding types 23 | 24 | 25 | 26 | raw 27 | /<-]]]> 28 | 29 | style 30 | PRE07-C. Avoid using repeated question marks 31 | 32 | 33 | 34 | raw 35 | 36 | 37 | style 38 | DCL04-C. Do not declare more than one variable per declaration 39 | 40 | 41 | 42 | raw 43 | 44 | 45 | style 46 | DCL05-C. Use typedefs of non-pointer types only 47 | 48 | 49 | 50 | raw 51 | 52 | 53 | style 54 | DCL20-C. Always specify void even if a function accepts no arguments 55 | 56 | 57 | 58 | 59 | 60 | style 61 | EXP08-C. Ensure pointer arithmetic is used correctly 62 | 63 | 64 | 65 | raw 66 | 67 | 68 | warning 69 | EXP06-C. Operands to the sizeof operator should not contain side effects 70 | 71 | 72 | 73 | raw 74 | 75 | 76 | warning 77 | EXP02-A. The second operands of the logical AND and OR operators should not contain side effects 78 | 79 | 80 | 81 | raw 82 | 83 | 84 | warning 85 | EXP04-A. Do not perform byte-by-byte comparisons between structures 86 | 87 | 88 | 89 | raw 90 | .] )(\b\w+\b)[^{};()'"]*[\[+,*/=-][^{};()'"]*(?:\b\1\b [+-]{2}|[+-]{2} \b\1\b)]]> 91 | 92 | warning 93 | EXP30-C. Do not depend on order of evaluation between sequence points 94 | 95 | 96 | 97 | normal 98 | 99 | 100 | error 101 | The result of strncpy(a, b, sizeof(a)) will not be null-terminated 102 | 103 | 104 | 105 | raw 106 | 107 | 108 | style 109 | STR05-A. Prefer making string literals const-qualified 110 | 111 | 112 | 113 | 114 | 115 | error 116 | The mktemp() function modifies its string argument 117 | 118 | 119 | 120 | 121 | 122 | error 123 | Buffer overrun possible for long environment variables. 124 | 125 | 126 | 127 | 128 | 129 | error 130 | The gets() function is obsolescent, and is deprecated 131 | 132 | 133 | 134 | 135 | 136 | warning 137 | rewind() cannot report about errors, use fseek 138 | 139 | 140 | 141 | 142 | 143 | style 144 | ato*() cannot report about errors, use strtol 145 | 146 | 147 | 148 | 149 | 150 | warning 151 | The snprintf() funciton can return -1 152 | 153 | 154 | 155 | 156 | 157 | style 158 | Avoid using sprintf/vsprintf 159 | 160 | 161 | 162 | 163 | 164 | warning 165 | TMP30-C. Temporary files must created with unique and unpredictable file names 166 | 167 | 168 | 169 | 170 | 171 | warning 172 | The mktemp() function was marked LEGACY in the Open Group Base Specifications Issue 6 173 | 174 | 175 | 176 | 177 | 178 | warning 179 | SIG00-A. Avoid using the same handler for multiple signals 180 | 181 | 182 | 183 | raw 184 | 185 | 186 | warning 187 | Maybe here should be == operator? 188 | 189 | 190 | 191 | 192 | 193 | warning 194 | The { } block is always executed because of the ";" following the if statement 195 | 196 | 197 | 198 | 199 | 200 | warning 201 | Using the vfork function could result in a denial of service vulnerability 202 | 203 | 204 | 205 | simple 206 | 207 | 208 | warning 209 | The string pointed to by the return value of getenv() can be modified by a subsequent call to getenv, putenv, setenv, or unsetenv 210 | 211 | 212 | -------------------------------------------------------------------------------- /tests/alloc.c: -------------------------------------------------------------------------------- 1 | void f() 2 | { 3 | malloc(1); 4 | } -------------------------------------------------------------------------------- /tests/decl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * DCL04-C. Do not declare more than one variable per declaration 3 | * */ 4 | 5 | 6 | char *src = 0, c = 0; 7 | 8 | /* 9 | * DCL05-C. Use typedefs of non-pointer types only 10 | * */ 11 | 12 | struct obj { 13 | int i; 14 | float f; 15 | }; 16 | typedef struct obj *ObjectPtr; 17 | 18 | /* 19 | * DCL20-C. Always specify void even if a function accepts no arguments 20 | * */ 21 | 22 | /* In foo.h */ 23 | void foo(); 24 | 25 | /* In foo.c */ 26 | void foo() { 27 | int i = 3; 28 | printf("i value: %d\n", i); 29 | } 30 | 31 | /* In caller.c */ 32 | #include "foo.h" 33 | 34 | foo(3); 35 | 36 | /* ok */ 37 | 38 | /* In foo.h */ 39 | void foo(void); 40 | 41 | /* In foo.c */ 42 | void foo(void) { 43 | int i = 3; 44 | printf("i value: %d\n", i); 45 | } 46 | 47 | /* In caller.c */ 48 | #include "foo.h" 49 | 50 | foo(3); -------------------------------------------------------------------------------- /tests/env.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ENV03-C. Sanitize the environment when invoking external programs 3 | * ENV04-C. Do not call system() if you do not need a command processor 4 | * */ 5 | 6 | if (system("/bin/ls dir.`date +%Y%m%d`") == -1) { 7 | /* Handle error */ 8 | } 9 | 10 | char *a = getenv("HOME"); 11 | char *b = getenv("PATH"); 12 | 13 | char *c = strdup(getenv("PATH")); -------------------------------------------------------------------------------- /tests/expr.c: -------------------------------------------------------------------------------- 1 | /* 2 | * EXP08-C. Ensure pointer arithmetic is used correctly 3 | * */ 4 | 5 | int buf[INTBUFSIZE]; 6 | int *buf_ptr = buf; 7 | 8 | while (havedata() && buf_ptr < (buf + sizeof(buf))) { 9 | *buf_ptr++ = parseint(getdata()); 10 | } 11 | 12 | /* 13 | * EXP06-C. Operands to the sizeof operator should not contain side effects 14 | * */ 15 | 16 | int a = 14; 17 | int b = sizeof(a++); 18 | 19 | 20 | /* 21 | * EXP02-A. The second operands of the logical AND and OR operators should 22 | not contain side effects 23 | * */ 24 | 25 | 26 | int i; 27 | int max; 28 | if ( (i >= 0 && (i++) <= max) ) { 29 | /* code */ 30 | } 31 | 32 | /* 33 | * EXP04-A. Do not perform byte-by-byte comparisons between structures 34 | * */ 35 | 36 | struct my_buf { 37 | size_t size; 38 | char buffer[50]; 39 | }; 40 | unsigned int buf_compare(struct my_buf *s1, struct my_buf *s2) { 41 | if (!memcmp(s1, s2, sizeof(struct my_struct))) { 42 | return 1; 43 | } 44 | return 0; 45 | } 46 | 47 | /* 48 | * EXP30-C. Do not depend on order of evaluation between sequence points 49 | * */ 50 | 51 | a = i + b[++i]; 52 | i = ++i + 1; 53 | a[i++] = i; 54 | func(i++, i); 55 | 56 | /* 57 | * EXP31-C. Do not modify constant values 58 | * */ 59 | 60 | char const **cpp; 61 | char *cp; 62 | char const c = 'A'; 63 | cpp = &cp; /* constraint violation */ 64 | *cpp = &c; /* valid */ 65 | *cp = 'B'; /* valid */ 66 | 67 | /* 68 | * EXP32-C. Do not access a volatile object through a non-volatile reference 69 | * */ 70 | 71 | static volatile int **ipp; 72 | static int *ip; 73 | static volatile int i = 0; 74 | printf("i = %d.\n", i); 75 | ipp = &ip; /* constraint violation */ 76 | *ipp = &i; /* valid */ 77 | if (*ip != 0) { /* valid */ 78 | /* ... */ 79 | } 80 | 81 | /* 82 | * EXP33-C. Do not reference uninitialized variables 83 | * */ 84 | 85 | void set_flag(int number, int *sign_flag) { 86 | if (number > 0) { 87 | *sign_flag = 1; 88 | } 89 | else if (number < 0) { 90 | *sign_flag = -1; 91 | } 92 | } 93 | void func(int number) { 94 | int sign; 95 | set_flag(number,&sign); 96 | /* ... */ 97 | } 98 | 99 | 100 | int do_auth(void) { 101 | char username[MAX_USER]; 102 | char password[MAX_PASS]; 103 | puts("Please enter your username: "); 104 | fgets(username, MAX_USER, stdin); 105 | puts("Please enter your password: "); 106 | fgets(password, MAX_PASS, stdin); 107 | if (!strcmp(username, "user") && !strcmp(password, "password")) { 108 | return 0; 109 | } 110 | return -1; 111 | } 112 | void log_error(char *msg) { 113 | char *err; 114 | char *log; 115 | char buffer[24]; 116 | sprintf(buffer, "Error: %s", log); 117 | printf("%s\n", buffer); 118 | } 119 | int main(void) { 120 | if (do_auth() == -1) { 121 | log_error("Unable to login"); 122 | } 123 | return 0; 124 | } 125 | 126 | /* 127 | * EXP35-C. Do not access or modify the result of a function call after a subsequent sequence point 128 | * */ 129 | 130 | #include 131 | struct X { char a[6]; }; 132 | struct X addressee() { 133 | struct X result = { "world" }; 134 | return result; 135 | } 136 | int main(void) { 137 | printf("Hello, %s!\n", addressee().a); 138 | return 0; 139 | } 140 | 141 | /* 142 | * EXP36-C. Do not cast between pointers to objects or types with differing alignments 143 | * */ 144 | 145 | char *loop_ptr; 146 | int *int_ptr; 147 | int *loop_function(void *v_pointer){ 148 | return v_pointer; 149 | } 150 | int_ptr = loop_function(loop_ptr); 151 | 152 | 153 | /* good */ 154 | ad->pos = ad->constrained = pos--; 155 | -------------------------------------------------------------------------------- /tests/fio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FIO33-C. Detect and handle input output errors resulting in undefined behavior 3 | * */ 4 | 5 | #include 6 | 7 | int fmt_data(char *buffer, size_t bufsize, char *s, char c, 8 | int i, float fp) { 9 | int j; 10 | 11 | j = snprintf(buffer, bufsize, " String: %s\n", s); 12 | j += snprintf(buffer + j, bufsize - j, " Character: %c\n", 13 | (unsigned char)c); 14 | j += snprintf(buffer + j, bufsize - j, " Integer: %d\n", 15 | i); 16 | j += snprintf(buffer + j, bufsize - j, " Real: %f\n", 17 | fp); 18 | 19 | return j; 20 | } 21 | -------------------------------------------------------------------------------- /tests/int.c: -------------------------------------------------------------------------------- 1 | /* 2 | * INT01-A. Use size_t for all integer values representing the size of an object 3 | * */ 4 | 5 | char *copy(size_t n, char *str) { 6 | int i; 7 | if(p == NULL) { 8 | /* Handle malloc failure */ 9 | } 10 | for ( i = 0; i < n; ++i ) { 11 | p[i] = *str++; 12 | } 13 | p[i] = '\0'; 14 | return p; 15 | } 16 | char *p = copy(SIZE_MAX, argv[1]); 17 | 18 | #define BUFF_SIZE 10 19 | int main(int argc, char *argv[]){ 20 | int size; 21 | char buf[BUFF_SIZE]; 22 | size= atoi(argv[1]); 23 | if (size <= BUFF_SIZE){ 24 | memcpy(buf, argv[2], size); 25 | } 26 | } 27 | 28 | void *alloc(unsigned int blocksize) { 29 | return malloc(blocksize); 30 | } 31 | int read_counted_string(int fd) { 32 | unsigned long length; 33 | unsigned char *data; 34 | if (read_integer_from_network(fd, &length) < 0) { 35 | return -1; 36 | } 37 | if (length + 1 == 0) { 38 | /* handle integer overflow */ 39 | } 40 | data = alloc(length + 1); 41 | if (read_network_data(fd, data, length) < 0) { 42 | free(data); 43 | return -1; 44 | } 45 | /* ... */ 46 | } 47 | -------------------------------------------------------------------------------- /tests/misc.c: -------------------------------------------------------------------------------- 1 | if (a == b); { 2 | puts("Hello"); 3 | } 4 | 5 | if (a = b) { 6 | puts("Hello"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/preproc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PRE00-C. Prefer inline or static functions to function-like macros 3 | * */ 4 | 5 | #define CUBE(X) ((X) * (X) * (X)) 6 | /* ... */ 7 | int i = 2; 8 | int a = 81 / CUBE(++i); 9 | 10 | 11 | 12 | size_t count = 0; 13 | /* bad */ 14 | #define EXEC_BUMP(func) (func(), ++count) 15 | 16 | void g(void) { 17 | printf("Called g, count = %zu.\n", count); 18 | } 19 | 20 | void aFunc(void) { 21 | size_t count = 0; 22 | while (count++ < 10) { 23 | EXEC_BUMP(g); 24 | } 25 | } 26 | 27 | /* 28 | * PRE01-C. Use parentheses within macros around parameter names 29 | * */ 30 | 31 | /* bad */ 32 | #define foreach_c_array(item, array, len) for (item = array; item < &array[len]; item++) 33 | 34 | /* good */ 35 | #define EMPTY(ptr) (!(ptr) || !*(ptr)) 36 | #define FOO(a,b,c) foo(a,b,c) 37 | 38 | /* 39 | * PRE03-C. Prefer typedefs to defines for encoding types 40 | * */ 41 | 42 | #define cstring char * 43 | cstring s1, s2; 44 | 45 | /* 46 | * PRE04-C. Do not reuse a standard header file name 47 | * */ 48 | 49 | #include "stdio.h" 50 | 51 | /* 52 | * PRE07-C. Avoid using repeated question marks 53 | * */ 54 | size_t i = /* Some initial value */; 55 | if (i > 9000) { 56 | if (puts("Over 9000!??!") == EOF) { 57 | /* Handle error */ 58 | } 59 | } 60 | 61 | /* 62 | * PRE10-C. Wrap multistatement macros in a do-while loop 63 | * */ 64 | 65 | #define SWAP(x, y) \ 66 | tmp = x; \ 67 | x = y; \ 68 | y = tmp 69 | 70 | #define SWAP(x, y) \ 71 | do { \ 72 | tmp = x; \ 73 | x = y; \ 74 | y = tmp; } \ 75 | while (0) 76 | -------------------------------------------------------------------------------- /tests/sig.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SIG00-A. Avoid using the same handler for multiple signals 3 | * */ 4 | 5 | #include 6 | #include 7 | #include 8 | volatile sig_atomic_t sig1 = 0; 9 | volatile sig_atomic_t sig2 = 0; 10 | void handler(int signum) { 11 | if (sig1) { 12 | sig2 = 1; 13 | } 14 | if (signum == SIGUSR1) { 15 | sig1 = 1; 16 | } 17 | } 18 | int main(void) { 19 | signal(SIGUSR1, handler); 20 | signal(SIGUSR2, handler); 21 | while (1) { 22 | if (sig2) break; 23 | sleep(SLEEP_TIME); 24 | } 25 | /* ... */ 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/str.c: -------------------------------------------------------------------------------- 1 | /* 2 | * STR03-A. Do not inadvertently truncate a null terminated byte string 3 | * */ 4 | 5 | char *string_data; 6 | char a[16]; 7 | /* ... */ 8 | strncpy(a, string_data, sizeof(a)); 9 | 10 | /* 11 | * STR05-A. Prefer making string literals const-qualified 12 | * */ 13 | 14 | /* bad */ 15 | char *c = "Hello"; 16 | char c2[] = "Hello"; 17 | 18 | /* good */ 19 | const char *s = "Hello"; 20 | 21 | /* 22 | * STR30-C. Do not attempt to modify string literals 23 | * */ 24 | 25 | mktemp("/tmp/edXXXXXX"); 26 | 27 | /* 28 | * STR31-C. Guarantee that storage for strings has sufficient space for character data and the null terminator 29 | * */ 30 | 31 | char dest[512]; 32 | for (int i=0; (i < sizeof(dest)); i++) { 33 | dest[i] = src[i]; 34 | } 35 | dest[i] = '\0'; 36 | 37 | int main(int argc, char *argv[]) { 38 | /* ... */ 39 | char prog_name[128]; 40 | strcpy(prog_name, argv[0]); 41 | /* ... */ 42 | } 43 | 44 | 45 | char buff[256]; 46 | strcpy(buff, getenv("EDITOR")); 47 | 48 | /* 49 | * STR32-C. Guarantee that all byte strings are null-terminated 50 | * */ 51 | 52 | char a[16]; 53 | strncpy(a, "0123456789abcdef", sizeof(a)); 54 | 55 | /* good */ 56 | 57 | char a[16]; 58 | strncpy(a, "0123456789abcdef", sizeof(a)-1); 59 | a[sizeof(a)-1] = '\0'; 60 | 61 | /* good */ 62 | strncpy (result, start, length); 63 | result [length] = '\0'; 64 | 65 | /* baaaad */ 66 | 67 | char buf[BUFSIZ]; 68 | gets(buf); 69 | 70 | -------------------------------------------------------------------------------- /tests/tmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TMP30-C. Temporary files must created with unique and unpredictable file names 3 | * */ 4 | 5 | FILE *fp = fopen("/tmp/some_file", "w"); 6 | 7 | int fd = open("/tmp/some_file", O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0600); 8 | 9 | --------------------------------------------------------------------------------