├── 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 |
--------------------------------------------------------------------------------