diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /tools/coccinelle | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/coccinelle')
62 files changed, 3547 insertions, 0 deletions
diff --git a/tools/coccinelle/README.md b/tools/coccinelle/README.md new file mode 100644 index 0000000..262ccc1 --- /dev/null +++ b/tools/coccinelle/README.md @@ -0,0 +1,14 @@ +Coccinelle patches +================== + +This collection of coccinelle patches represents some of the broader, +codebase-wide changes that have been made. If you maintain a fork of +FRR and find that your codebase needs to be updated to align with +these changes, the coccinelle tool should help you make that update. + +The coccinelle tool is documented at: + https://coccinelle.gitlabpages.inria.fr/website/ + +To run a coccinelle patch script: + + spatch --sp-file tools/coccinelle/semicolon.cocci zebra/*.c diff --git a/tools/coccinelle/__func__.cocci b/tools/coccinelle/__func__.cocci new file mode 100644 index 0000000..fb68494 --- /dev/null +++ b/tools/coccinelle/__func__.cocci @@ -0,0 +1,10 @@ +@@ +@@ + +( +- __PRETTY_FUNCTION__ ++ __func__ +| +- __FUNCTION__ ++ __func__ +) diff --git a/tools/coccinelle/alloc_cast.cocci b/tools/coccinelle/alloc_cast.cocci new file mode 100644 index 0000000..b1497c7 --- /dev/null +++ b/tools/coccinelle/alloc_cast.cocci @@ -0,0 +1,101 @@ +/// Remove casting the values returned by memory allocation functions +/// like XMALLOC and XCALLOC. +/// +// This makes an effort to find cases of casting of values returned by # +// XMALLOC and XCALLOC and removes the casting as it is not required. The +// result in the patch case may need some reformatting. +// +// Confidence: High +// Copyright: (C) 2014 Himangi Saraogi GPLv2. +// Copyright: (C) 2017 Himanshu Jha GPLv2. +// Copyright: (C) 2019 Quentin Young GPLv2. +// Comments: +// Options: --no-includes --include-headers +// + +virtual context +virtual patch +virtual org +virtual report + +@initialize:python@ +@@ +import re +pattern = '__' +m = re.compile(pattern) + +@r1 depends on context || patch@ +type T; +@@ + + (T *) + \(XMALLOC\|XCALLOC\)(...) + +//---------------------------------------------------------- +// For context mode +//---------------------------------------------------------- + +@script:python depends on context@ +t << r1.T; +@@ + +if m.search(t) != None: + cocci.include_match(False) + +@depends on context && r1@ +type r1.T; +@@ + +* (T *) + \(XMALLOC\|XCALLOC\)(...) + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@script:python depends on patch@ +t << r1.T; +@@ + +if m.search(t) != None: + cocci.include_match(False) + +@depends on patch && r1@ +type r1.T; +@@ + +- (T *) + \(XMALLOC\|XCALLOC\)(...) + +//---------------------------------------------------------- +// For org and report mode +//---------------------------------------------------------- + +@r2 depends on org || report@ +type T; +position p; +@@ + + (T@p *) + \(XMALLOC\|XCALLOC\)(...) + +@script:python depends on org@ +p << r2.p; +t << r2.T; +@@ + +if m.search(t) != None: + cocci.include_match(False) +else: + coccilib.org.print_safe_todo(p[0], t) + +@script:python depends on report@ +p << r2.p; +t << r2.T; +@@ + +if m.search(t) != None: + cocci.include_match(False) +else: + msg="WARNING: casting value returned by memory allocation function to (%s *) is useless." % (t) + coccilib.report.print_report(p[0], msg) diff --git a/tools/coccinelle/argv_find.cocci b/tools/coccinelle/argv_find.cocci new file mode 100644 index 0000000..1ab19b7 --- /dev/null +++ b/tools/coccinelle/argv_find.cocci @@ -0,0 +1,23 @@ +@@ +identifier idx; +identifier argv; +identifier argc; +expression e1; +expression e2; +identifier I; +@@ + +( +- argv_find(argv, argc, e1, &idx); + if ( +- idx ++ argv_find(argv, argc, e1, &idx) + ) + { + e2; + } +| +- argv_find(argv, argc, e1, &idx); +... when != I = idx; + when strict +) diff --git a/tools/coccinelle/array_size.cocci b/tools/coccinelle/array_size.cocci new file mode 100644 index 0000000..f977b8a --- /dev/null +++ b/tools/coccinelle/array_size.cocci @@ -0,0 +1,83 @@ +/// Use array_size instead of dividing sizeof array with sizeof an element +/// +//# This makes an effort to find cases where array_size can be used such as +//# where there is a division of sizeof the array by the sizeof its first +//# element or by any indexed element or the element type. It replaces the +//# division of the two sizeofs by array_size. +// +// Confidence: High +// Copyright: (C) 2014 Himangi Saraogi. GPLv2. +// Copyright: (C) 2019 Quentin Young. GPLv2. +// Comments: +// Options: --no-includes --include-headers + +virtual patch +virtual context +virtual org +virtual report + +//---------------------------------------------------------- +// For context mode +//---------------------------------------------------------- + +@depends on context@ +type T; +T[] E; +@@ +( +* (sizeof(E)/sizeof(*E)) +| +* (sizeof(E)/sizeof(E[...])) +| +* (sizeof(E)/sizeof(T)) +) + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@depends on patch@ +type T; +T[] E; +@@ +( +- (sizeof(E)/sizeof(*E)) ++ array_size(E) +| +- (sizeof(E)/sizeof(E[...])) ++ array_size(E) +| +- (sizeof(E)/sizeof(T)) ++ array_size(E) +) + +//---------------------------------------------------------- +// For org and report mode +//---------------------------------------------------------- + +@r depends on (org || report)@ +type T; +T[] E; +position p; +@@ +( + (sizeof(E)@p /sizeof(*E)) +| + (sizeof(E)@p /sizeof(E[...])) +| + (sizeof(E)@p /sizeof(T)) +) + +@script:python depends on org@ +p << r.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING should use array_size") + +@script:python depends on report@ +p << r.p; +@@ + +msg="WARNING: Use array_size" +coccilib.report.print_report(p[0], msg) + diff --git a/tools/coccinelle/badty.cocci b/tools/coccinelle/badty.cocci new file mode 100644 index 0000000..481cf30 --- /dev/null +++ b/tools/coccinelle/badty.cocci @@ -0,0 +1,76 @@ +/// Use ARRAY_SIZE instead of dividing sizeof array with sizeof an element +/// +//# This makes an effort to find cases where the argument to sizeof is wrong +//# in memory allocation functions by checking the type of the allocated memory +//# when it is a double pointer and ensuring the sizeof argument takes a pointer +//# to the the memory being allocated. There are false positives in cases the +//# sizeof argument is not used in constructing the return value. The result +//# may need some reformatting. +// +// Confidence: Moderate +// Copyright: (C) 2014 Himangi Saraogi. GPLv2. +// Comments: +// Options: + +virtual patch +virtual context +virtual org +virtual report + +//---------------------------------------------------------- +// For context mode +//---------------------------------------------------------- + +@depends on context disable sizeof_type_expr@ +type T; +T **x; +@@ + + x = + <+...sizeof( +* T + )...+> + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@depends on patch disable sizeof_type_expr@ +type T; +T **x; +@@ + + x = + <+...sizeof( +- T ++ *x + )...+> + +//---------------------------------------------------------- +// For org and report mode +//---------------------------------------------------------- + +@r depends on (org || report) disable sizeof_type_expr@ +type T; +T **x; +position p; +@@ + + x = + <+...sizeof( + T@p + )...+> + +@script:python depends on org@ +p << r.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING sizeof argument should be pointer type, not structure type") + +@script:python depends on report@ +p << r.p; +@@ + +msg="WARNING: Use correct pointer type argument for sizeof" +coccilib.report.print_report(p[0], msg) + diff --git a/tools/coccinelle/badzero.cocci b/tools/coccinelle/badzero.cocci new file mode 100644 index 0000000..f597c80 --- /dev/null +++ b/tools/coccinelle/badzero.cocci @@ -0,0 +1,238 @@ +/// Compare pointer-typed values to NULL rather than 0 +/// +//# This makes an effort to choose between !x and x == NULL. !x is used +//# if it has previously been used with the function used to initialize x. +//# This relies on type information. More type information can be obtained +//# using the option -all_includes and the option -I to specify an +//# include path. +// +// Confidence: High +// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Requires: 1.0.0 +// Options: + +virtual patch +virtual context +virtual org +virtual report + +@initialize:ocaml@ +@@ +let negtable = Hashtbl.create 101 + +@depends on patch@ +expression *E; +identifier f; +@@ + +( + (E = f(...)) == +- 0 ++ NULL +| + (E = f(...)) != +- 0 ++ NULL +| +- 0 ++ NULL + == (E = f(...)) +| +- 0 ++ NULL + != (E = f(...)) +) + + +@t1 depends on !patch@ +expression *E; +identifier f; +position p; +@@ + +( + (E = f(...)) == +* 0@p +| + (E = f(...)) != +* 0@p +| +* 0@p + == (E = f(...)) +| +* 0@p + != (E = f(...)) +) + +@script:python depends on org@ +p << t1.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0") + +@script:python depends on report@ +p << t1.p; +@@ + +coccilib.report.print_report(p[0], "WARNING comparing pointer to 0") + +// Tests of returned values + +@s@ +identifier f; +expression E,E1; +@@ + + E = f(...) + ... when != E = E1 + !E + +@script:ocaml depends on s@ +f << s.f; +@@ + +try let _ = Hashtbl.find negtable f in () +with Not_found -> Hashtbl.add negtable f () + +@ r disable is_zero,isnt_zero exists @ +expression *E; +identifier f; +@@ + +E = f(...) +... +(E == 0 +|E != 0 +|0 == E +|0 != E +) + +@script:ocaml@ +f << r.f; +@@ + +try let _ = Hashtbl.find negtable f in () +with Not_found -> include_match false + +// This rule may lead to inconsistent path problems, if E is defined in two +// places +@ depends on patch disable is_zero,isnt_zero @ +expression *E; +expression E1; +identifier r.f; +@@ + +E = f(...) +<... +( +- E == 0 ++ !E +| +- E != 0 ++ E +| +- 0 == E ++ !E +| +- 0 != E ++ E +) +...> +?E = E1 + +@t2 depends on !patch disable is_zero,isnt_zero @ +expression *E; +expression E1; +identifier r.f; +position p1; +position p2; +@@ + +E = f(...) +<... +( +* E == 0@p1 +| +* E != 0@p2 +| +* 0@p1 == E +| +* 0@p1 != E +) +...> +?E = E1 + +@script:python depends on org@ +p << t2.p1; +@@ + +coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0, suggest !E") + +@script:python depends on org@ +p << t2.p2; +@@ + +coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0") + +@script:python depends on report@ +p << t2.p1; +@@ + +coccilib.report.print_report(p[0], "WARNING comparing pointer to 0, suggest !E") + +@script:python depends on report@ +p << t2.p2; +@@ + +coccilib.report.print_report(p[0], "WARNING comparing pointer to 0") + +@ depends on patch disable is_zero,isnt_zero @ +expression *E; +@@ + +( + E == +- 0 ++ NULL +| + E != +- 0 ++ NULL +| +- 0 ++ NULL + == E +| +- 0 ++ NULL + != E +) + +@ t3 depends on !patch disable is_zero,isnt_zero @ +expression *E; +position p; +@@ + +( +* E == 0@p +| +* E != 0@p +| +* 0@p == E +| +* 0@p != E +) + +@script:python depends on org@ +p << t3.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING comparing pointer to 0") + +@script:python depends on report@ +p << t3.p; +@@ + +coccilib.report.print_report(p[0], "WARNING comparing pointer to 0") diff --git a/tools/coccinelle/bool_assignment.cocci b/tools/coccinelle/bool_assignment.cocci new file mode 100644 index 0000000..e6146ea --- /dev/null +++ b/tools/coccinelle/bool_assignment.cocci @@ -0,0 +1,13 @@ +@@ +bool b; +@@ + +( + b = +- 0 ++ false +| + b = +- 1 ++ true +) diff --git a/tools/coccinelle/bool_expression.cocci b/tools/coccinelle/bool_expression.cocci new file mode 100644 index 0000000..c0c329c --- /dev/null +++ b/tools/coccinelle/bool_expression.cocci @@ -0,0 +1,29 @@ +@@ +bool t; +@@ + +( +- t == true ++ t +| +- true == t ++ t +| +- t != true ++ !t +| +- true != t ++ !t +| +- t == false ++ !t +| +- false == t ++ !t +| +- t != false ++ t +| +- false != t ++ t +) diff --git a/tools/coccinelle/bool_function.cocci b/tools/coccinelle/bool_function.cocci new file mode 100644 index 0000000..0328ecf --- /dev/null +++ b/tools/coccinelle/bool_function.cocci @@ -0,0 +1,21 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +@@ + +bool fn ( ... ) +{ +<... +return +( +- 0 ++ false +| +- 1 ++ true +) + ; +...> +} diff --git a/tools/coccinelle/bool_function_type.cocci b/tools/coccinelle/bool_function_type.cocci new file mode 100644 index 0000000..71bf4f5 --- /dev/null +++ b/tools/coccinelle/bool_function_type.cocci @@ -0,0 +1,19 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +@@ + +- int ++ bool +fn (...) +{ +?... +return +( + true +| + false +); +} diff --git a/tools/coccinelle/boolconv.cocci b/tools/coccinelle/boolconv.cocci new file mode 100644 index 0000000..33c464d --- /dev/null +++ b/tools/coccinelle/boolconv.cocci @@ -0,0 +1,90 @@ +/// Remove unneeded conversion to bool +/// +//# Relational and logical operators evaluate to bool, +//# explicit conversion is overly verbose and unneeded. +// +// Copyright: (C) 2016 Andrew F. Davis <afd@ti.com> GPLv2. + +virtual patch +virtual context +virtual org +virtual report + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@depends on patch@ +expression A, B; +symbol true, false; +@@ + +( + A == B +| + A != B +| + A > B +| + A < B +| + A >= B +| + A <= B +| + A && B +| + A || B +) +- ? true : false + +//---------------------------------------------------------- +// For context mode +//---------------------------------------------------------- + +@r depends on !patch@ +expression A, B; +symbol true, false; +position p; +@@ + +( + A == B +| + A != B +| + A > B +| + A < B +| + A >= B +| + A <= B +| + A && B +| + A || B +) +* ? true : false@p + +//---------------------------------------------------------- +// For org mode +//---------------------------------------------------------- + +@script:python depends on r&&org@ +p << r.p; +@@ + +msg = "WARNING: conversion to bool not needed here" +coccilib.org.print_todo(p[0], msg) + +//---------------------------------------------------------- +// For report mode +//---------------------------------------------------------- + +@script:python depends on r&&report@ +p << r.p; +@@ + +msg = "WARNING: conversion to bool not needed here" +coccilib.report.print_report(p[0], msg) diff --git a/tools/coccinelle/boolinit.cocci b/tools/coccinelle/boolinit.cocci new file mode 100644 index 0000000..aabb581 --- /dev/null +++ b/tools/coccinelle/boolinit.cocci @@ -0,0 +1,194 @@ +/// Bool initializations should use true and false. Bool tests don't need +/// comparisons. Based on contributions from Joe Perches, Rusty Russell +/// and Bruce W Allan. +/// +// Confidence: High +// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Options: --include-headers + +virtual patch +virtual context +virtual org +virtual report + +@boolok@ +symbol true,false; +@@ +( +true +| +false +) + +@depends on patch@ +bool t; +@@ + +( +- t == true ++ t +| +- true == t ++ t +| +- t != true ++ !t +| +- true != t ++ !t +| +- t == false ++ !t +| +- false == t ++ !t +| +- t != false ++ t +| +- false != t ++ t +) + +@depends on patch disable is_zero, isnt_zero@ +bool t; +@@ + +( +- t == 1 ++ t +| +- t != 1 ++ !t +| +- t == 0 ++ !t +| +- t != 0 ++ t +) + +@depends on patch && boolok@ +bool b; +@@ +( + b = +- 0 ++ false +| + b = +- 1 ++ true +) + +// --------------------------------------------------------------------- + +@r1 depends on !patch@ +bool t; +position p; +@@ + +( +* t@p == true +| +* true == t@p +| +* t@p != true +| +* true != t@p +| +* t@p == false +| +* false == t@p +| +* t@p != false +| +* false != t@p +) + +@r2 depends on !patch disable is_zero, isnt_zero@ +bool t; +position p; +@@ + +( +* t@p == 1 +| +* t@p != 1 +| +* t@p == 0 +| +* t@p != 0 +) + +@r3 depends on !patch && boolok@ +bool b; +position p1; +@@ +( +*b@p1 = 0 +| +*b@p1 = 1 +) + +@r4 depends on !patch@ +bool b; +position p2; +identifier i; +constant c != {0,1}; +@@ +( + b = i +| +*b@p2 = c +) + +@script:python depends on org@ +p << r1.p; +@@ + +cocci.print_main("WARNING: Comparison to bool",p) + +@script:python depends on org@ +p << r2.p; +@@ + +cocci.print_main("WARNING: Comparison of 0/1 to bool variable",p) + +@script:python depends on org@ +p1 << r3.p1; +@@ + +cocci.print_main("WARNING: Assignment of 0/1 to bool variable",p1) + +@script:python depends on org@ +p2 << r4.p2; +@@ + +cocci.print_main("ERROR: Assignment of non-0/1 constant to bool variable",p2) + +@script:python depends on report@ +p << r1.p; +@@ + +coccilib.report.print_report(p[0],"WARNING: Comparison to bool") + +@script:python depends on report@ +p << r2.p; +@@ + +coccilib.report.print_report(p[0],"WARNING: Comparison of 0/1 to bool variable") + +@script:python depends on report@ +p1 << r3.p1; +@@ + +coccilib.report.print_report(p1[0],"WARNING: Assignment of 0/1 to bool variable") + +@script:python depends on report@ +p2 << r4.p2; +@@ + +coccilib.report.print_report(p2[0],"ERROR: Assignment of non-0/1 constant to bool variable") diff --git a/tools/coccinelle/boolreturn.cocci b/tools/coccinelle/boolreturn.cocci new file mode 100644 index 0000000..29d2bf4 --- /dev/null +++ b/tools/coccinelle/boolreturn.cocci @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +/// Return statements in functions returning bool should use +/// true/false instead of 1/0. +// +// Confidence: High +// Options: --no-includes --include-headers + +virtual patch +virtual report +virtual context + +@r1 depends on patch@ +identifier fn; +typedef bool; +symbol false; +symbol true; +@@ + +bool fn ( ... ) +{ +<... +return +( +- 0 ++ false +| +- 1 ++ true +) + ; +...> +} + +@r2 depends on report || context@ +identifier fn; +position p; +@@ + +bool fn ( ... ) +{ +<... +return +( +* 0@p +| +* 1@p +) + ; +...> +} + + +@script:python depends on report@ +p << r2.p; +fn << r2.fn; +@@ + +msg = "WARNING: return of 0/1 in function '%s' with return type bool" % fn +coccilib.report.print_report(p[0], msg) diff --git a/tools/coccinelle/cast_to_larger_sizes.cocci b/tools/coccinelle/cast_to_larger_sizes.cocci new file mode 100644 index 0000000..d97e1f9 --- /dev/null +++ b/tools/coccinelle/cast_to_larger_sizes.cocci @@ -0,0 +1,20 @@ +// spatch -sp_file tools/coccinelle/cast_to_larger_sizes.cocci --recursive-includes ./ + +@r@ +typedef uint8_t; +typedef uint16_t; +typedef uint32_t; +typedef uint64_t; +uint8_t *i8; +position p; +@@ + + \( + (uint64_t *) i8@p\|(uint32_t *) i8@p\|(uint16_t *) i8@p + \) + +@script:python@ +p << r.p; +@@ + +coccilib.report.print_report(p[0],"Bad typecast to larger size") diff --git a/tools/coccinelle/cond_no_effect.cocci b/tools/coccinelle/cond_no_effect.cocci new file mode 100644 index 0000000..8467dbd --- /dev/null +++ b/tools/coccinelle/cond_no_effect.cocci @@ -0,0 +1,64 @@ +///Find conditions where if and else branch are functionally +// identical. +// +// There can be false positives in cases where the positional +// information is used (as with lockdep) or where the identity +// is a placeholder for not yet handled cases. +// Unfortunately there also seems to be a tendency to use +// the last if else/else as a "default behavior" - which some +// might consider a legitimate coding pattern. From discussion +// on kernelnewbies though it seems that this is not really an +// accepted pattern and if at all it would need to be commented +// +// In the Linux kernel it does not seem to actually report +// false positives except for those that were documented as +// being intentional. +// the two known cases are: +// arch/sh/kernel/traps_64.c:read_opcode() +// } else if ((pc & 1) == 0) { +// /* SHcompact */ +// /* TODO : provide handling for this. We don't really support +// user-mode SHcompact yet, and for a kernel fault, this would +// have to come from a module built for SHcompact. */ +// return -EFAULT; +// } else { +// /* misaligned */ +// return -EFAULT; +// } +// fs/kernfs/file.c:kernfs_fop_open() +// * Both paths of the branch look the same. They're supposed to +// * look that way and give @of->mutex different static lockdep keys. +// */ +// if (has_mmap) +// mutex_init(&of->mutex); +// else +// mutex_init(&of->mutex); +// +// All other cases look like bugs or at least lack of documentation +// +// Confidence: Moderate +// Copyright: (C) 2016 Nicholas Mc Guire, OSADL. GPLv2. +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report + +@cond@ +statement S1; +position p; +@@ + +* if@p (...) S1 else S1 + +@script:python depends on org@ +p << cond.p; +@@ + +cocci.print_main("WARNING: possible condition with no effect (if == else)",p) + +@script:python depends on report@ +p << cond.p; +@@ + +coccilib.report.print_report(p[0],"WARNING: possible condition with no effect (if == else)") diff --git a/tools/coccinelle/ctype_cast.cocci b/tools/coccinelle/ctype_cast.cocci new file mode 100644 index 0000000..b0b0095 --- /dev/null +++ b/tools/coccinelle/ctype_cast.cocci @@ -0,0 +1,11 @@ + +@@ +identifier func =~ "^(to|is)(alnum|cntrl|print|xdigit|alpha|digit|punct|ascii|graph|space|blank|lower|upper)$"; +expression e; +@@ + + func( +- (int) ++ (unsigned char) + e) + diff --git a/tools/coccinelle/deref_null.cocci b/tools/coccinelle/deref_null.cocci new file mode 100644 index 0000000..cbc6184 --- /dev/null +++ b/tools/coccinelle/deref_null.cocci @@ -0,0 +1,282 @@ +/// +/// A variable is dereferenced under a NULL test. +/// Even though it is known to be NULL. +/// +// Confidence: Moderate +// Copyright: (C) 2010 Nicolas Palix, DIKU. GPLv2. +// Copyright: (C) 2010 Julia Lawall, DIKU. GPLv2. +// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: -I ... -all_includes can give more complete results +// Options: + +virtual context +virtual org +virtual report + +// The following two rules are separate, because both can match a single +// expression in different ways +@pr1 expression@ +expression E; +identifier f; +position p1; +@@ + + (E != NULL && ...) ? <+...E->f@p1...+> : ... + +@pr2 expression@ +expression E; +identifier f; +position p2; +@@ + +( + (E != NULL) && ... && <+...E->f@p2...+> +| + (E == NULL) || ... || <+...E->f@p2...+> +| + sizeof(<+...E->f@p2...+>) +) + +@ifm@ +expression *E; +statement S1,S2; +position p1; +@@ + +if@p1 ((E == NULL && ...) || ...) S1 else S2 + +// For org and report modes + +@r depends on !context && (org || report) exists@ +expression subE <= ifm.E; +expression *ifm.E; +expression E1,E2; +identifier f; +statement S1,S2,S3,S4; +iterator iter; +position p!={pr1.p1,pr2.p2}; +position ifm.p1; +@@ + +if@p1 ((E == NULL && ...) || ...) +{ + ... when != if (...) S1 else S2 +( + iter(subE,...) S4 // no use +| + list_remove_head(E2,subE,...) +| + subE = E1 +| + for(subE = E1;...;...) S4 +| + subE++ +| + ++subE +| + --subE +| + subE-- +| + &subE +| + E->f@p // bad use +) + ... when any + return ...; +} +else S3 + +@script:python depends on !context && !org && report@ +p << r.p; +p1 << ifm.p1; +x << ifm.E; +@@ + +msg="ERROR: %s is NULL but dereferenced." % (x) +coccilib.report.print_report(p[0], msg) +cocci.include_match(False) + +@script:python depends on !context && org && !report@ +p << r.p; +p1 << ifm.p1; +x << ifm.E; +@@ + +msg="ERROR: %s is NULL but dereferenced." % (x) +msg_safe=msg.replace("[","@(").replace("]",")") +cocci.print_main(msg_safe,p) +cocci.include_match(False) + +@s depends on !context && (org || report) exists@ +expression subE <= ifm.E; +expression *ifm.E; +expression E1,E2; +identifier f; +statement S1,S2,S3,S4; +iterator iter; +position p!={pr1.p1,pr2.p2}; +position ifm.p1; +@@ + +if@p1 ((E == NULL && ...) || ...) +{ + ... when != if (...) S1 else S2 +( + iter(subE,...) S4 // no use +| + list_remove_head(E2,subE,...) +| + subE = E1 +| + for(subE = E1;...;...) S4 +| + subE++ +| + ++subE +| + --subE +| + subE-- +| + &subE +| + E->f@p // bad use +) + ... when any +} +else S3 + +@script:python depends on !context && !org && report@ +p << s.p; +p1 << ifm.p1; +x << ifm.E; +@@ + +msg="ERROR: %s is NULL but dereferenced." % (x) +coccilib.report.print_report(p[0], msg) + +@script:python depends on !context && org && !report@ +p << s.p; +p1 << ifm.p1; +x << ifm.E; +@@ + +msg="ERROR: %s is NULL but dereferenced." % (x) +msg_safe=msg.replace("[","@(").replace("]",")") +cocci.print_main(msg_safe,p) + +// For context mode + +@depends on context && !org && !report exists@ +expression subE <= ifm.E; +expression *ifm.E; +expression E1,E2; +identifier f; +statement S1,S2,S3,S4; +iterator iter; +position p!={pr1.p1,pr2.p2}; +position ifm.p1; +@@ + +if@p1 ((E == NULL && ...) || ...) +{ + ... when != if (...) S1 else S2 +( + iter(subE,...) S4 // no use +| + list_remove_head(E2,subE,...) +| + subE = E1 +| + for(subE = E1;...;...) S4 +| + subE++ +| + ++subE +| + --subE +| + subE-- +| + &subE +| +* E->f@p // bad use +) + ... when any + return ...; +} +else S3 + +// The following three rules are duplicates of ifm, pr1 and pr2 respectively. +// It is need because the previous rule as already made a "change". + +@pr11 depends on context && !org && !report expression@ +expression E; +identifier f; +position p1; +@@ + + (E != NULL && ...) ? <+...E->f@p1...+> : ... + +@pr12 depends on context && !org && !report expression@ +expression E; +identifier f; +position p2; +@@ + +( + (E != NULL) && ... && <+...E->f@p2...+> +| + (E == NULL) || ... || <+...E->f@p2...+> +| + sizeof(<+...E->f@p2...+>) +) + +@ifm1 depends on context && !org && !report@ +expression *E; +statement S1,S2; +position p1; +@@ + +if@p1 ((E == NULL && ...) || ...) S1 else S2 + +@depends on context && !org && !report exists@ +expression subE <= ifm1.E; +expression *ifm1.E; +expression E1,E2; +identifier f; +statement S1,S2,S3,S4; +iterator iter; +position p!={pr11.p1,pr12.p2}; +position ifm1.p1; +@@ + +if@p1 ((E == NULL && ...) || ...) +{ + ... when != if (...) S1 else S2 +( + iter(subE,...) S4 // no use +| + list_remove_head(E2,subE,...) +| + subE = E1 +| + for(subE = E1;...;...) S4 +| + subE++ +| + ++subE +| + --subE +| + subE-- +| + &subE +| +* E->f@p // bad use +) + ... when any +} +else S3 diff --git a/tools/coccinelle/double_lock.cocci b/tools/coccinelle/double_lock.cocci new file mode 100644 index 0000000..002752f --- /dev/null +++ b/tools/coccinelle/double_lock.cocci @@ -0,0 +1,92 @@ +/// Find double locks. False positives may occur when some paths cannot +/// occur at execution, due to the values of variables, and when there is +/// an intervening function call that releases the lock. +/// +// Confidence: Moderate +// Copyright: (C) 2010 Nicolas Palix, DIKU. GPLv2. +// Copyright: (C) 2010 Julia Lawall, DIKU. GPLv2. +// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report + +@locked@ +position p1; +expression E1; +position p; +@@ + +( +mutex_lock@p1 +| +mutex_trylock@p1 +| +spin_lock@p1 +| +spin_trylock@p1 +| +read_lock@p1 +| +read_trylock@p1 +| +write_lock@p1 +| +write_trylock@p1 +) (E1@p,...); + +@balanced@ +position p1 != locked.p1; +position locked.p; +identifier lock,unlock; +expression x <= locked.E1; +expression E,locked.E1; +expression E2; +@@ + +if (E) { + <+... when != E1 + lock(E1@p,...) + ...+> +} +... when != E1 + when != \(x = E2\|&x\) + when forall +if (E) { + <+... when != E1 + unlock@p1(E1,...) + ...+> +} + +@r depends on !balanced exists@ +expression x <= locked.E1; +expression locked.E1; +expression E2; +identifier lock; +position locked.p,p1,p2; +@@ + +lock@p1 (E1@p,...); +... when != E1 + when != \(x = E2\|&x\) +lock@p2 (E1,...); + +@script:python depends on org@ +p1 << r.p1; +p2 << r.p2; +lock << r.lock; +@@ + +cocci.print_main(lock,p1) +cocci.print_secs("second lock",p2) + +@script:python depends on report@ +p1 << r.p1; +p2 << r.p2; +lock << r.lock; +@@ + +msg = "second lock on line %s" % (p2[0].line) +coccilib.report.print_report(p1[0],msg) diff --git a/tools/coccinelle/doublebitand.cocci b/tools/coccinelle/doublebitand.cocci new file mode 100644 index 0000000..72f1572 --- /dev/null +++ b/tools/coccinelle/doublebitand.cocci @@ -0,0 +1,54 @@ +/// Find bit operations that include the same argument more than once +//# One source of false positives is when the argument performs a side +//# effect. Another source of false positives is when a neutral value +//# such as 0 for | is used to indicate no information, to maintain the +//# same structure as other similar expressions +/// +// Confidence: Moderate +// Copyright: (C) 2010 Nicolas Palix, DIKU. GPLv2. +// Copyright: (C) 2010 Julia Lawall, DIKU. GPLv2. +// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual context +virtual org +virtual report + +@r expression@ +expression E; +position p; +@@ + +( +* E@p + & ... & E +| +* E@p + | ... | E +| +* E@p + & ... & !E +| +* E@p + | ... | !E +| +* !E@p + & ... & E +| +* !E@p + | ... | E +) + +@script:python depends on org@ +p << r.p; +@@ + +cocci.print_main("duplicated argument to & or |",p) + +@script:python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0],"duplicated argument to & or |") diff --git a/tools/coccinelle/doubleinit.cocci b/tools/coccinelle/doubleinit.cocci new file mode 100644 index 0000000..c0c3371 --- /dev/null +++ b/tools/coccinelle/doubleinit.cocci @@ -0,0 +1,53 @@ +/// Find duplicate field initializations. This has a high rate of false +/// positives due to #ifdefs, which Coccinelle is not aware of in a structure +/// initialization. +/// +// Confidence: Low +// Copyright: (C) 2010-2012 Nicolas Palix. GPLv2. +// Copyright: (C) 2010-2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: requires at least Coccinelle 0.2.4, lex or parse error otherwise +// Options: --no-includes --include-headers + +virtual org +virtual report + +@r@ +identifier I, s, fld; +position p0,p; +expression E; +@@ + +struct I s =@p0 { ..., .fld@p = E, ...}; + +@s@ +identifier I, s, r.fld; +position r.p0,p; +expression E; +@@ + +struct I s =@p0 { ..., .fld@p = E, ...}; + +@script:python depends on org@ +p0 << r.p0; +fld << r.fld; +ps << s.p; +pr << r.p; +@@ + +if int(ps[0].line) < int(pr[0].line) or (int(ps[0].line) == int(pr[0].line) and int(ps[0].column) < int(pr[0].column)): + cocci.print_main(fld,p0) + cocci.print_secs("s",ps) + cocci.print_secs("r",pr) + +@script:python depends on report@ +p0 << r.p0; +fld << r.fld; +ps << s.p; +pr << r.p; +@@ + +if int(ps[0].line) < int(pr[0].line) or (int(ps[0].line) == int(pr[0].line) and int(ps[0].column) < int(pr[0].column)): + msg = "%s: first occurrence line %s, second occurrence line %s" % (fld,ps[0].line,pr[0].line) + coccilib.report.print_report(p0[0],msg) diff --git a/tools/coccinelle/doubletest.cocci b/tools/coccinelle/doubletest.cocci new file mode 100644 index 0000000..7af2ce7 --- /dev/null +++ b/tools/coccinelle/doubletest.cocci @@ -0,0 +1,58 @@ +/// Find &&/|| operations that include the same argument more than once +//# A common source of false positives is when the expression, or +//# another expresssion in the same && or || operation, performs a +//# side effect. +/// +// Confidence: Moderate +// Copyright: (C) 2010 Nicolas Palix, DIKU. GPLv2. +// Copyright: (C) 2010 Julia Lawall, DIKU. GPLv2. +// Copyright: (C) 2010 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual context +virtual org +virtual report + +@r expression@ +expression E; +position p; +@@ + +( + E@p || ... || E +| + E@p && ... && E +) + +@bad@ +expression r.E,e1,e2,fn; +position r.p; +assignment operator op; +@@ + +( +E@p +& + <+... \(fn(...)\|e1 op e2\|e1++\|e1--\|++e1\|--e1\) ...+> +) + +@depends on context && !bad@ +expression r.E; +position r.p; +@@ + +*E@p + +@script:python depends on org && !bad@ +p << r.p; +@@ + +cocci.print_main("duplicated argument to && or ||",p) + +@script:python depends on report && !bad@ +p << r.p; +@@ + +coccilib.report.print_report(p[0],"duplicated argument to && or ||") diff --git a/tools/coccinelle/frr_with_mutex.cocci b/tools/coccinelle/frr_with_mutex.cocci new file mode 100644 index 0000000..ec8b739 --- /dev/null +++ b/tools/coccinelle/frr_with_mutex.cocci @@ -0,0 +1,23 @@ +@@ +expression E; +iterator name frr_with_mutex; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { +- { + ... +- } +- pthread_mutex_unlock(E); ++ } + + +@@ +expression E; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { + ... +- pthread_mutex_unlock(E); ++ } diff --git a/tools/coccinelle/hash_compare_null_values_check.cocci b/tools/coccinelle/hash_compare_null_values_check.cocci new file mode 100644 index 0000000..38649a2 --- /dev/null +++ b/tools/coccinelle/hash_compare_null_values_check.cocci @@ -0,0 +1,20 @@ +// There is no need to test for null values in the hash compare +// function as that we are guaranteed to send in data in +// the hash compare functions. +@@ +identifier fn =~ "_hash_cmp"; +type T; +identifier p1; +identifier p2; +@@ + +?static +T fn(...) +{ +... +- if (p1 == NULL && p2 == NULL) +- return ...; +- if (p1 == NULL || p2 == NULL) +- return ...; +... +} diff --git a/tools/coccinelle/hash_const.cocci b/tools/coccinelle/hash_const.cocci new file mode 100644 index 0000000..9c53cb0 --- /dev/null +++ b/tools/coccinelle/hash_const.cocci @@ -0,0 +1,76 @@ +// +// Transition hash key signatures to take their argument as const. +// Does not handle headers or weirdly named hash functions. +// +@noconst disable optional_qualifier@ +identifier A; +identifier func =~ ".*key$|.*key_make$|.*hash_make$|.*hash_keymake$|.*hash_key$|.*hash_key.*"; +@@ + +- func (void *A) ++ func (const void *A) + { ... } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func( ... ) { +<... +- T b = A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b = (T) A; ++ const T b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... + b = A; +...> + } + +@ depends on noconst disable optional_qualifier @ +identifier noconst.A; +identifier noconst.func; +identifier b; +type T; +@@ + +func(...) + { +<... +- T b; ++ const T b; +... +- b = (T) A; ++ b = A; +...> + } diff --git a/tools/coccinelle/ifaddr.cocci b/tools/coccinelle/ifaddr.cocci new file mode 100644 index 0000000..c2663c6 --- /dev/null +++ b/tools/coccinelle/ifaddr.cocci @@ -0,0 +1,34 @@ +/// The address of a variable or field is likely always to be non-zero. +/// +// Confidence: High +// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report +virtual context + +@r@ +expression x; +statement S1,S2; +position p; +@@ + +*if@p (&x) + S1 else S2 + +@script:python depends on org@ +p << r.p; +@@ + +cocci.print_main("test of a variable/field address",p) + +@script:python depends on report@ +p << r.p; +@@ + +msg = "ERROR: test of a variable/field address" +coccilib.report.print_report(p[0],msg) diff --git a/tools/coccinelle/ifnullxfree.cocci b/tools/coccinelle/ifnullxfree.cocci new file mode 100644 index 0000000..85fc23e --- /dev/null +++ b/tools/coccinelle/ifnullxfree.cocci @@ -0,0 +1,15 @@ +/// NULL check before some freeing functions is not needed. +/// +// Copyright: (C) 2014 Fabian Frederick. GPLv2. +// Copyright: (C) 2019 Quentin Young. GPLv2. +// Comments: - +// Options: --no-includes --include-headers + +virtual patch + +@r2 depends on patch@ +expression E; +expression Y; +@@ +- if (E != NULL) +XFREE(Y, E); diff --git a/tools/coccinelle/int_to_bool_function.cocci b/tools/coccinelle/int_to_bool_function.cocci new file mode 100644 index 0000000..f86fe70 --- /dev/null +++ b/tools/coccinelle/int_to_bool_function.cocci @@ -0,0 +1,24 @@ +@@ +identifier fn; +typedef bool; +symbol false; +symbol true; +identifier I; +struct thread *thread; +@@ + +- int ++ bool +fn (...) +{ +... when strict + when != I = THREAD_ARG(thread); +( +- return 0; ++ return false; +| +- return 1; ++ return true; +) +?... +} diff --git a/tools/coccinelle/itnull.cocci b/tools/coccinelle/itnull.cocci new file mode 100644 index 0000000..f58732b --- /dev/null +++ b/tools/coccinelle/itnull.cocci @@ -0,0 +1,94 @@ +/// Many iterators have the property that the first argument is always bound +/// to a real list element, never NULL. +//# False positives arise for some iterators that do not have this property, +//# or in cases when the loop cursor is reassigned. The latter should only +//# happen when the matched code is on the way to a loop exit (break, goto, +//# or return). +/// +// Confidence: Moderate +// Copyright: (C) 2010-2012 Nicolas Palix. GPLv2. +// Copyright: (C) 2010-2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual patch +virtual context +virtual org +virtual report + +@depends on patch@ +iterator I; +expression x,E,E1,E2; +statement S,S1,S2; +@@ + +I(x,...) { <... +( +- if (x == NULL && ...) S +| +- if (x != NULL || ...) + S +| +- (x == NULL) || + E +| +- (x != NULL) && + E +| +- (x == NULL && ...) ? E1 : + E2 +| +- (x != NULL || ...) ? + E1 +- : E2 +| +- if (x == NULL && ...) S1 else + S2 +| +- if (x != NULL || ...) + S1 +- else S2 +| ++ BAD( + x == NULL ++ ) +| ++ BAD( + x != NULL ++ ) +) + ...> } + +@r depends on !patch exists@ +iterator I; +expression x,E; +position p1,p2; +@@ + +*I@p1(x,...) +{ ... when != x = E +( +* x@p2 == NULL +| +* x@p2 != NULL +) + ... when any +} + +@script:python depends on org@ +p1 << r.p1; +p2 << r.p2; +@@ + +cocci.print_main("iterator-bound variable",p1) +cocci.print_secs("useless NULL test",p2) + +@script:python depends on report@ +p1 << r.p1; +p2 << r.p2; +@@ + +msg = "ERROR: iterator variable bound on line %s cannot be NULL" % (p1[0].line) +coccilib.report.print_report(p2[0], msg) diff --git a/tools/coccinelle/json_object_add_camel_case.cocci b/tools/coccinelle/json_object_add_camel_case.cocci new file mode 100644 index 0000000..279ba21 --- /dev/null +++ b/tools/coccinelle/json_object_add_camel_case.cocci @@ -0,0 +1,19 @@ +// Catch whitespaces in JSON keys + +@r@ +identifier json; +constant key; +identifier func =~ "json_object_"; +position p; +@@ + +func(json, key, ...)@p + +@script:python@ +fmt << r.key; +p << r.p; +@@ +if " " in str(fmt): + print("Whitespace detected in JSON keys %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)) +if str(fmt)[1].isupper(): + print("Capital first detected in JSON keys %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)) diff --git a/tools/coccinelle/json_object_string_addf_inet_ntop.cocci b/tools/coccinelle/json_object_string_addf_inet_ntop.cocci new file mode 100644 index 0000000..d9f92e5 --- /dev/null +++ b/tools/coccinelle/json_object_string_addf_inet_ntop.cocci @@ -0,0 +1,19 @@ +@@ +identifier json; +expression family, buf, value; +constant key, buflen; +@@ + +( +-json_object_string_add(json, key, inet_ntop(AF_INET, &value, buf, sizeof(buf))); ++json_object_string_addf(json, key, "%pI4", &value); +| +-json_object_string_add(json, key, inet_ntop(AF_INET, &value, buf, buflen)); ++json_object_string_addf(json, key, "%pI4", &value); +| +-json_object_string_add(json, key, inet_ntop(AF_INET6, &value, buf, sizeof(buf))); ++json_object_string_addf(json, key, "%pI6", &value); +| +-json_object_string_add(json, key, inet_ntop(AF_INET6, &value, buf, buflen)); ++json_object_string_addf(json, key, "%pI6", &value); +) diff --git a/tools/coccinelle/json_object_string_addf_prefix2str.cocci b/tools/coccinelle/json_object_string_addf_prefix2str.cocci new file mode 100644 index 0000000..ae012b9 --- /dev/null +++ b/tools/coccinelle/json_object_string_addf_prefix2str.cocci @@ -0,0 +1,16 @@ +@@ +identifier json; +expression family, value; +expression prefix; +constant key; +@@ + +( +-prefix2str(prefix, value, ...); +... +-json_object_string_add(json, key, value); ++json_object_string_addf(json, key, "%pFX", prefix); +| +-json_object_string_add(json, key, prefix2str(prefix, value, ...)); ++json_object_string_addf(json, key, "%pFX", prefix); +) diff --git a/tools/coccinelle/memset.cocci b/tools/coccinelle/memset.cocci new file mode 100644 index 0000000..0da576c --- /dev/null +++ b/tools/coccinelle/memset.cocci @@ -0,0 +1,21 @@ +// + +@@ +identifier src, dst; +identifier str, len; +type t =~ "struct"; + +@@ + +( +- memset(&dst, 0, sizeof(t)); ++ memset(&dst, 0, sizeof(dst)); +| +- memcpy(&dst, &src, sizeof(t)); ++ memcpy(&dst, &src, sizeof(dst)); +| +- char str[...]; +... +- memset(&str, 0, ...); ++ memset(&str, 0, sizeof(str)); +) diff --git a/tools/coccinelle/mini_lock.cocci b/tools/coccinelle/mini_lock.cocci new file mode 100644 index 0000000..19c6ee5 --- /dev/null +++ b/tools/coccinelle/mini_lock.cocci @@ -0,0 +1,98 @@ +/// Find missing unlocks. This semantic match considers the specific case +/// where the unlock is missing from an if branch, and there is a lock +/// before the if and an unlock after the if. False positives are due to +/// cases where the if branch represents a case where the function is +/// supposed to exit with the lock held, or where there is some preceding +/// function call that releases the lock. +/// +// Confidence: Moderate +// Copyright: (C) 2010-2012 Nicolas Palix. GPLv2. +// Copyright: (C) 2010-2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual context +virtual org +virtual report + +@prelocked@ +position p1,p; +expression E1; +@@ + +( +mutex_lock@p1 +| +mutex_trylock@p1 +| +spin_lock@p1 +| +spin_trylock@p1 +| +read_lock@p1 +| +read_trylock@p1 +| +write_lock@p1 +| +write_trylock@p1 +| +read_lock_irq@p1 +| +write_lock_irq@p1 +| +read_lock_irqsave@p1 +| +write_lock_irqsave@p1 +| +spin_lock_irq@p1 +| +spin_lock_irqsave@p1 +) (E1@p,...); + +@looped@ +position r; +@@ + +for(...;...;...) { <+... return@r ...; ...+> } + +@err exists@ +expression E1; +position prelocked.p; +position up != prelocked.p1; +position r!=looped.r; +identifier lock,unlock; +@@ + +*lock(E1@p,...); +... when != E1 + when any +if (...) { + ... when != E1 +* return@r ...; +} +... when != E1 + when any +*unlock@up(E1,...); + +@script:python depends on org@ +p << prelocked.p1; +lock << err.lock; +unlock << err.unlock; +p2 << err.r; +@@ + +cocci.print_main(lock,p) +cocci.print_secs(unlock,p2) + +@script:python depends on report@ +p << prelocked.p1; +lock << err.lock; +unlock << err.unlock; +p2 << err.r; +@@ + +msg = "preceding lock on line %s" % (p[0].line) +coccilib.report.print_report(p2[0],msg) diff --git a/tools/coccinelle/nb-cbs.cocci b/tools/coccinelle/nb-cbs.cocci new file mode 100644 index 0000000..6e4d32e --- /dev/null +++ b/tools/coccinelle/nb-cbs.cocci @@ -0,0 +1,298 @@ +@@ +identifier func =~ ".*_create$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_create_args *args) + { +<... +( +- event ++ args->event +| +- dnode ++ args->dnode +| +- resource ++ args->resource +) +...> + } + +@@ +identifier func =~ ".*_modify$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_modify_args *args) + { +<... +( +- event ++ args->event +| +- dnode ++ args->dnode +| +- resource ++ args->resource +) +...> + } + +@@ +identifier func =~ ".*_destroy$"; +identifier event, dnode; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode) ++ func(struct nb_cb_destroy_args *args) + { +<... +( +- dnode ++ args->dnode +| +- event ++ args->event +) +...> + } + +@@ +identifier func =~ ".*_pre_validate$"; +identifier dnode; +@@ + +int +- func(const struct lyd_node dnode) ++ func(struct nb_cb_pre_validate_args *args) + { +<... +- dnode ++ args->dnode +...> + } + +@@ +identifier func =~ ".*_apply_finish$"; +identifier dnode; +@@ + +void +- func(const struct lyd_node *dnode) ++ func(struct nb_cb_apply_finish_args *args) + { +<... +- dnode ++ args->dnode +...> + } + +@@ +identifier func =~ ".*_get_elem$"; +identifier xpath, list_entry; +@@ + +struct yang_data * +- func(const char *xpath, const void *list_entry) ++ func(struct nb_cb_get_elem_args *args) + { +<... +( +- xpath ++ args->xpath +| +- list_entry ++ args->list_entry +) +...> + } + +@@ +identifier func =~ ".*_get_next$"; +identifier parent_list_entry, list_entry; +@@ + +const void * +- func(const void *parent_list_entry, const void *list_entry) ++ func(struct nb_cb_get_next_args *args) + { +<... +( +- parent_list_entry ++ args->parent_list_entry +| +- list_entry ++ args->list_entry +) +...> + } + +@@ +identifier func =~ ".*_get_keys$"; +identifier list_entry, keys; +@@ + +int +- func(const void *list_entry, struct yang_list_keys *keys) ++ func(struct nb_cb_get_keys_args *args) + { +<... +( +- list_entry ++ args->list_entry +| +- keys ++ args->keys +) +...> + } + +@@ +identifier func =~ ".*_lookup_entry$"; +identifier parent_list_entry, keys; +@@ + +const void * +- func(const void *parent_list_entry, const struct yang_list_keys *keys) ++ func(struct nb_cb_lookup_entry_args *args) + { +<... +( +- parent_list_entry ++ args->parent_list_entry +| +- keys ++ args->keys +) +...> + } + +@@ +identifier func =~ ".*_rpc$"; +identifier xpath, input, output; +@@ + +int +- func(const char *xpath, const struct list *input, struct list *output) ++ func(struct nb_cb_rpc_args *args) + { +<... +( +- xpath ++ args->xpath +| +- input ++ args->input +| +- output ++ args->output +) +...> + } + +@@ +identifier func =~ ".*_create$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_create_args *args) +; + +@@ +identifier func =~ ".*_modify$"; +identifier event, dnode, resource; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode, union nb_resource *resource) ++ func(struct nb_cb_modify_args *args) +; + +@@ +identifier func =~ ".*_destroy$"; +identifier event, dnode; +@@ + +int +- func(enum nb_event event, const struct lyd_node *dnode) ++ func(struct nb_cb_destroy_args *args) +; + +@@ +identifier func =~ ".*_pre_validate$"; +identifier dnode; +@@ + +int +- func(const struct lyd_node dnode) ++ func(struct nb_cb_pre_validate_args *args) +; + +@@ +identifier func =~ ".*_apply_finish$"; +identifier dnode; +@@ + +void +- func(const struct lyd_node *dnode) ++ func(struct nb_cb_apply_finish_args *args) +; + +@@ +identifier func =~ ".*_get_elem$"; +identifier xpath, list_entry; +@@ + +struct yang_data * +- func(const char *xpath, const void *list_entry) ++ func(struct nb_cb_get_elem_args *args) +; + +@@ +identifier func =~ ".*_get_next$"; +identifier parent_list_entry, list_entry; +@@ + +const void * +- func(const void *parent_list_entry, const void *list_entry) ++ func(struct nb_cb_get_next_args *args) +; + +@@ +identifier func =~ ".*_get_keys$"; +identifier list_entry, keys; +@@ + +int +- func(const void *list_entry, struct yang_list_keys *keys) ++ func(struct nb_cb_get_keys_args *args) +; + +@@ +identifier func =~ ".*_lookup_entry$"; +identifier parent_list_entry, keys; +@@ + +const void * +- func(const void *parent_list_entry, const struct yang_list_keys *keys) ++ func(struct nb_cb_lookup_entry_args *args) +; + +@@ +identifier func =~ ".*_rpc$"; +identifier xpath, input, output; +@@ + +int +- func(const char *xpath, const struct list *input, struct list *output) ++ func(struct nb_cb_rpc_args *args) +; diff --git a/tools/coccinelle/noderef.cocci b/tools/coccinelle/noderef.cocci new file mode 100644 index 0000000..ca289d5 --- /dev/null +++ b/tools/coccinelle/noderef.cocci @@ -0,0 +1,81 @@ +/// sizeof when applied to a pointer typed expression gives the size of +/// the pointer +/// +// Confidence: High +// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report +virtual context +virtual patch + +@depends on patch@ +expression *x; +expression f; +expression i; +type T; +@@ + +( +x = <+... sizeof( +- x ++ *x + ) ...+> +| +f(...,(T)(x),...,sizeof( +- x ++ *x + ),...) +| +f(...,sizeof( +- x ++ *x + ),...,(T)(x),...) +| +f(...,(T)(x),...,i*sizeof( +- x ++ *x + ),...) +| +f(...,i*sizeof( +- x ++ *x + ),...,(T)(x),...) +) + +@r depends on !patch@ +expression *x; +expression f; +expression i; +position p; +type T; +@@ + +( +*x = <+... sizeof@p(x) ...+> +| +*f(...,(T)(x),...,sizeof@p(x),...) +| +*f(...,sizeof@p(x),...,(T)(x),...) +| +*f(...,(T)(x),...,i*sizeof@p(x),...) +| +*f(...,i*sizeof@p(x),...,(T)(x),...) +) + +@script:python depends on org@ +p << r.p; +@@ + +cocci.print_main("application of sizeof to pointer",p) + +@script:python depends on report@ +p << r.p; +@@ + +msg = "ERROR: application of sizeof to pointer" +coccilib.report.print_report(p[0],msg)<Paste> diff --git a/tools/coccinelle/replace-strncpy.cocci b/tools/coccinelle/replace-strncpy.cocci new file mode 100644 index 0000000..18ff131 --- /dev/null +++ b/tools/coccinelle/replace-strncpy.cocci @@ -0,0 +1,8 @@ +@@ +type T; +T[] E; +expression buf, srclen; +@@ + +- strncpy(E, src, srclen) ++ strlcpy(E, src, sizeof(E)) diff --git a/tools/coccinelle/replace_bgp_flag_functions.cocci b/tools/coccinelle/replace_bgp_flag_functions.cocci new file mode 100644 index 0000000..3064fc0 --- /dev/null +++ b/tools/coccinelle/replace_bgp_flag_functions.cocci @@ -0,0 +1,14 @@ +@@ +expression e1, e2; +@@ + +( +- bgp_flag_check(e1, e2) ++ CHECK_FLAG(e1->flags, e2) +| +- bgp_flag_set(e1, e2) ++ SET_FLAG(e1->flags, e2) +| +- bgp_flag_unset(e1, e2) ++ UNSET_FLAG(e1->flags, e2) +) diff --git a/tools/coccinelle/return_without_parenthesis.cocci b/tools/coccinelle/return_without_parenthesis.cocci new file mode 100644 index 0000000..7097e87 --- /dev/null +++ b/tools/coccinelle/return_without_parenthesis.cocci @@ -0,0 +1,9 @@ +// Do not apply only for ldpd daemon since it uses the BSD coding style, +// where parentheses on return is expected. + +@@ +constant c; +@@ + +- return (c); ++ return c; diff --git a/tools/coccinelle/returnvar.cocci b/tools/coccinelle/returnvar.cocci new file mode 100644 index 0000000..d8286ef --- /dev/null +++ b/tools/coccinelle/returnvar.cocci @@ -0,0 +1,66 @@ +/// +/// Remove unneeded variable used to store return value. +/// +// Confidence: Moderate +// Copyright: (C) 2012 Peter Senna Tschudin, INRIA/LIP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: Comments on code can be deleted if near code that is removed. +// "when strict" can be removed to get more hits, but adds false +// positives +// Options: --no-includes --include-headers + +virtual patch +virtual report +virtual context +virtual org + +@depends on patch@ +type T; +constant C; +identifier ret; +@@ +- T ret = C; +... when != ret + when strict +return +- ret ++ C +; + +@depends on context@ +type T; +constant C; +identifier ret; +@@ +* T ret = C; +... when != ret + when strict +* return ret; + +@r1 depends on report || org@ +type T; +constant C; +identifier ret; +position p1, p2; +@@ +T ret@p1 = C; +... when != ret + when strict +return ret@p2; + +@script:python depends on report@ +p1 << r1.p1; +p2 << r1.p2; +C << r1.C; +ret << r1.ret; +@@ +coccilib.report.print_report(p1[0], "Unneeded variable: \"" + ret + "\". Return \"" + C + "\" on line " + p2[0].line) + +@script:python depends on org@ +p1 << r1.p1; +p2 << r1.p2; +C << r1.C; +ret << r1.ret; +@@ +cocci.print_main("unneeded \"" + ret + "\" variable", p1) +cocci.print_sec("return " + C + " here", p2) diff --git a/tools/coccinelle/route_map_apply.cocci b/tools/coccinelle/route_map_apply.cocci new file mode 100644 index 0000000..ccca619 --- /dev/null +++ b/tools/coccinelle/route_map_apply.cocci @@ -0,0 +1,15 @@ +@rmap@ +identifier ret; +position p; +@@ + +int ret@p; +... +* ret = route_map_apply(...); + +@script:python@ +p << rmap.p; +@@ + +msg = "ERROR: Invalid type of return value variable for route_map_apply_ext()" +coccilib.report.print_report(p[0], msg) diff --git a/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci b/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci new file mode 100644 index 0000000..bd7f4af --- /dev/null +++ b/tools/coccinelle/s_addr_0_to_INADDR_ANY.cocci @@ -0,0 +1,14 @@ +@@ +expression e; +@@ + +( +- e.s_addr == 0 ++ e.s_addr == INADDR_ANY +| +- e.s_addr != 0 ++ e.s_addr != INADDR_ANY +| +- e.s_addr = 0 ++ e.s_addr = INADDR_ANY +) diff --git a/tools/coccinelle/same_type_casting.cocci b/tools/coccinelle/same_type_casting.cocci new file mode 100644 index 0000000..58fd756 --- /dev/null +++ b/tools/coccinelle/same_type_casting.cocci @@ -0,0 +1,7 @@ +@@ +type T; +T *ptr; +@@ + +- (T *)ptr ++ ptr diff --git a/tools/coccinelle/semicolon.cocci b/tools/coccinelle/semicolon.cocci new file mode 100644 index 0000000..6740c65 --- /dev/null +++ b/tools/coccinelle/semicolon.cocci @@ -0,0 +1,83 @@ +/// +/// Remove unneeded semicolon. +/// +// Confidence: Moderate +// Copyright: (C) 2012 Peter Senna Tschudin, INRIA/LIP6. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: Some false positives on empty default cases in switch statements. +// Options: --no-includes --include-headers + +virtual patch +virtual report +virtual context +virtual org + +@r_default@ +position p; +@@ +switch (...) +{ +default: ...;@p +} + +@r_case@ +position p; +@@ +( +switch (...) +{ +case ...:;@p +} +| +switch (...) +{ +case ...:... +case ...:;@p +} +| +switch (...) +{ +case ...:... +case ...: +case ...:;@p +} +) + +@r1@ +statement S; +position p1; +position p != {r_default.p, r_case.p}; +identifier label; +@@ +( +label:; +| +S@p1;@p +) + +@script:python@ +p << r1.p; +p1 << r1.p1; +@@ +if p[0].line != p1[0].line_end: + cocci.include_match(False) + +@depends on patch@ +position r1.p; +@@ +-;@p + +@script:python depends on report@ +p << r1.p; +@@ +coccilib.report.print_report(p[0],"Unneeded semicolon") + +@depends on context@ +position r1.p; +@@ +*;@p + +@script:python depends on org@ +p << r1.p; +@@ +cocci.print_main("Unneeded semicolon",p) diff --git a/tools/coccinelle/shorthand_operator.cocci b/tools/coccinelle/shorthand_operator.cocci new file mode 100644 index 0000000..f7019d4 --- /dev/null +++ b/tools/coccinelle/shorthand_operator.cocci @@ -0,0 +1,12 @@ +@@ +identifier data; +constant x; +@@ + +( +- data = data + x ++ data += x +| +- data = data - x ++ data -= x +) diff --git a/tools/coccinelle/strncpy_truncation.cocci b/tools/coccinelle/strncpy_truncation.cocci new file mode 100644 index 0000000..28b5c2a --- /dev/null +++ b/tools/coccinelle/strncpy_truncation.cocci @@ -0,0 +1,41 @@ +/// Use strlcpy rather than strncpy(dest,..,sz) + dest[sz-1] = '\0' +/// +// Confidence: High +// Comments: +// Options: --no-includes --include-headers + +virtual patch +virtual context +virtual report +virtual org + +@r@ +expression dest, src, sz; +position p; +@@ + +strncpy@p(dest, src, sz); +dest[sz - 1] = '\0'; + +@script:python depends on org@ +p << r.p; +@@ + +cocci.print_main("strncpy followed by truncation can be strlcpy",p) + +@script:python depends on report@ +p << r.p; +@@ + +msg = "SUGGESTION: strncpy followed by truncation can be strlcpy" +coccilib.report.print_report(p[0],msg) + +@ok depends on patch@ +expression r.dest, r.src, r.sz; +position r.p; +@@ + +-strncpy@p( ++strlcpy( + dest, src, sz); +-dest[sz - 1] = '\0'; diff --git a/tools/coccinelle/struct_thread_double_pointer.cocci b/tools/coccinelle/struct_thread_double_pointer.cocci new file mode 100644 index 0000000..a08e672 --- /dev/null +++ b/tools/coccinelle/struct_thread_double_pointer.cocci @@ -0,0 +1,35 @@ +@r1@ +identifier fn, m, f, a, v, t; +identifier func =~ "thread_add_"; +type T1, T2; +position p; +@@ + +?static +T1 fn(T2 *t) +{ +... +func(m,f,a,v,&t)@p +... +} + +@r2@ +identifier m, f, a, v, t; +identifier func =~ "thread_add_"; +type T1; +position p; +@@ + +T1 *t; +... +func(m,f,a,v,&t)@p + +@script:python@ +p << r1.p; +@@ +coccilib.report.print_report(p[0],"Passed double 'struct thread' pointer") + +@script:python@ +p << r2.p; +@@ +coccilib.report.print_report(p[0],"Passed double 'struct thread' pointer") diff --git a/tools/coccinelle/struct_thread_null.cocci b/tools/coccinelle/struct_thread_null.cocci new file mode 100644 index 0000000..4867b44 --- /dev/null +++ b/tools/coccinelle/struct_thread_null.cocci @@ -0,0 +1,9 @@ +@@ +identifier I; +identifier func =~ "thread_add_"; +struct thread *thread; +@@ + +*thread = NULL; +... +func diff --git a/tools/coccinelle/test_after_assert.cocci b/tools/coccinelle/test_after_assert.cocci new file mode 100644 index 0000000..30596a8 --- /dev/null +++ b/tools/coccinelle/test_after_assert.cocci @@ -0,0 +1,7 @@ +@@ +identifier i; +@@ + +assert(i); +- if (!i) +- return ...; diff --git a/tools/coccinelle/thread_cancel_api.cocci b/tools/coccinelle/thread_cancel_api.cocci new file mode 100644 index 0000000..cc34f93 --- /dev/null +++ b/tools/coccinelle/thread_cancel_api.cocci @@ -0,0 +1,68 @@ +@ptrupdate@ +expression E; +@@ +- thread_cancel(E); ++ thread_cancel(&E); + +@nullcheckremove depends on ptrupdate@ +expression E; +@@ + +thread_cancel(&E); +- E = NULL; + +@cancelguardremove depends on nullcheckremove@ +expression E; +@@ +- if (E) +- { + thread_cancel(&E); +- } + +@cancelguardremove2 depends on nullcheckremove@ +expression E; +@@ +- if (E != NULL) +- { + thread_cancel(&E); +- } + +@cancelguardremove3 depends on nullcheckremove@ +expression E; +@@ +- if (E) + thread_cancel(&E); + +@cancelguardremove4 depends on nullcheckremove@ +expression E; +@@ +- if (E != NULL) + thread_cancel(&E); + +@replacetimeroff@ +expression E; +@@ + +- THREAD_TIMER_OFF(E); ++ thread_cancel(&E); + +@replacewriteoff@ +expression E; +@@ + +- THREAD_WRITE_OFF(E); ++ thread_cancel(&E); + +@replacereadoff@ +expression E; +@@ + +- THREAD_READ_OFF(E); ++ thread_cancel(&E); + +@replacethreadoff@ +expression E; +@@ + +- THREAD_OFF(E); ++ thread_cancel(&E);
\ No newline at end of file diff --git a/tools/coccinelle/unsigned_lesser_than_zero.cocci b/tools/coccinelle/unsigned_lesser_than_zero.cocci new file mode 100644 index 0000000..8fa5a3c --- /dev/null +++ b/tools/coccinelle/unsigned_lesser_than_zero.cocci @@ -0,0 +1,75 @@ +/// Unsigned expressions cannot be lesser than zero. Presence of +/// comparisons 'unsigned (<|<=|>|>=) 0' often indicates a bug, +/// usually wrong type of variable. +/// +/// To reduce number of false positives following tests have been added: +/// - parts of range checks are skipped, eg. "if (u < 0 || u > 15) ...", +/// developers prefer to keep such code, +/// - comparisons "<= 0" and "> 0" are performed only on results of +/// signed functions/macros, +/// - hardcoded list of signed functions/macros with always non-negative +/// result is used to avoid false positives difficult to detect by other ways +/// +// Confidence: Average +// Copyright: (C) 2015 Andrzej Hajda, Samsung Electronics Co., Ltd. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Options: --all-includes + +virtual context +virtual org +virtual report + +@r_cmp@ +position p; +typedef bool, u8, u16, u32, u64; +{unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long, + size_t, bool, u8, u16, u32, u64} v; +expression e; +@@ + + \( v = e \| &v \) + ... + (\( v@p < 0 \| v@p <= 0 \| v@p >= 0 \| v@p > 0 \)) + +@r@ +position r_cmp.p; +typedef s8, s16, s32, s64; +{char, short, int, long, long long, ssize_t, s8, s16, s32, s64} vs; +expression c, e, v; +identifier f !~ "^(ata_id_queue_depth|btrfs_copy_from_user|dma_map_sg|dma_map_sg_attrs|fls|fls64|gameport_time|get_write_extents|nla_len|ntoh24|of_flat_dt_match|of_get_child_count|uart_circ_chars_pending|[A-Z0-9_]+)$"; +@@ + +( + v = f(...)@vs; + ... when != v = e; +* (\( v@p <=@e 0 \| v@p >@e 0 \)) + ... when any +| +( + (\( v@p < 0 \| v@p <= 0 \)) || ... || (\( v >= c \| v > c \)) +| + (\( v >= c \| v > c \)) || ... || (\( v@p < 0 \| v@p <= 0 \)) +| + (\( v@p >= 0 \| v@p > 0 \)) && ... && (\( v < c \| v <= c \)) +| + ((\( v < c \| v <= c \) && ... && \( v@p >= 0 \| v@p > 0 \))) +| +* (\( v@p <@e 0 \| v@p >=@e 0 \)) +) +) + +@script:python depends on org@ +p << r_cmp.p; +e << r.e; +@@ + +msg = "WARNING: Unsigned expression compared with zero: %s" % (e) +coccilib.org.print_todo(p[0], msg) + +@script:python depends on report@ +p << r_cmp.p; +e << r.e; +@@ + +msg = "WARNING: Unsigned expression compared with zero: %s" % (e) +coccilib.report.print_report(p[0], msg) diff --git a/tools/coccinelle/void_no_return.cocci b/tools/coccinelle/void_no_return.cocci new file mode 100644 index 0000000..7da9e73 --- /dev/null +++ b/tools/coccinelle/void_no_return.cocci @@ -0,0 +1,9 @@ +@@ +identifier f; +expression e; +@@ +void f(...) { + ... +- return + e; +} diff --git a/tools/coccinelle/vty_check.cocci b/tools/coccinelle/vty_check.cocci new file mode 100644 index 0000000..7e5fcc4 --- /dev/null +++ b/tools/coccinelle/vty_check.cocci @@ -0,0 +1,22 @@ +/* + * VTY_DECLVAR_CONTEXT contains a built-in "if (!var) return;" + */ +@@ +identifier var, typ; +statement S; +@@ + + { + ... + \( + VTY_DECLVAR_CONTEXT(typ, var); + \| + VTY_DECLVAR_CONTEXT_SUB(typ, var); + \) + ... +- if ( +- \( !var \| var == NULL \) +- ) +- S + ... + } diff --git a/tools/coccinelle/vty_index.cocci b/tools/coccinelle/vty_index.cocci new file mode 100644 index 0000000..eabbaa1 --- /dev/null +++ b/tools/coccinelle/vty_index.cocci @@ -0,0 +1,244 @@ +/* + * prep: strip off casts, they cause things to fail matching later. + */ + +@@ +identifier casttarget; +symbol vty; +@@ + +- (struct casttarget *)vty->index ++ vty->index + +/* + * variant 1: local variable assigned from vty->index + */ + +@@ +identifier sn, nn; +identifier fn; +@@ + + int fn(...) + { ++ VTY_DECLVAR_CONTEXT (sn, nn); + ... + \( +- struct sn *nn; + ... +- nn = vty->index; + \| +- struct sn *nn = vty->index; + \| +- struct sn *nn = vty->index; + ... +- nn = vty->index; + \) + ... + } + +@@ +identifier sn, nn; +identifier fn; +type Tr; +@@ + + Tr *fn(...) + { ++ struct sn *nn = VTY_GET_CONTEXT(sn); + ... + \( +- struct sn *nn; + ... +- nn = vty->index; ++ if (!nn) { ++ return NULL; ++ } + \| +- struct sn *nn = vty->index; ++ if (!nn) { ++ return NULL; ++ } + \| +- struct sn *nn = vty->index; + ... +- nn = vty->index; ++ if (!nn) { ++ return NULL; ++ } + \) + ... + } + +/* + * variant 2: vty wrapper func with (vty, vty->index, ...) signature + */ + +/* find calls of this pattern first; arg will be dropped in rule3 */ +@rule1@ +identifier fn !~ "generic_(set|match)_"; +expression arg; +@@ + + fn(arg, arg->index, ...) + +@ script:python @ +fn << rule1.fn; +arg << rule1.arg; +@@ +print "R01 removing vty-index argument on %s(%s, ...)" % (fn, arg) + +#/* strip arg on the vty wrapper func, add local handling */ +@ rule2 @ +identifier rule1.fn; +identifier arg; +identifier T; +@@ + + static int fn (struct vty *vty, +- struct T * arg, + ...) + { ++ VTY_DECLVAR_CONTEXT (T, arg); + ... + } + +/* drop argument on call sites identified earlier */ +@ rule3 @ +identifier rule1.fn; +expression arg; +@@ + + fn(arg, +- arg->index, + ...) + + +/* + * variant 3: function calls with "vty->index" argument (but no vty) + * + * a bit more complicated since we need to find the type from the header. + */ + +/* find call sites first + * remember function name for later declvar insertion + */ +@ rule11 exists@ +identifier fn; +identifier fparent; +type Tr; +@@ + + Tr fparent (...) + { + ... + fn(vty->index, ...) + ... + } + +@ script:python @ +fn << rule11.fn; +@@ +print "R11 removing vty-index argument on %s(...)" % (fn) + +#/* find type of the argument - note args are mostly unnamed in FRR :( */ +@ rule12 @ +identifier rule11.fn; +identifier T, argname; +type Tr; +@@ + +( + Tr fn(struct T *, ...); +| + Tr fn(struct T * argname, ...); +) + +@ script:python @ +fn << rule11.fn; +T << rule12.T; +@@ +print "R12 removing vty-index type is %s for %s(...)" % (T, fn) + +#/* add declvar +# * this is split from rule14 so we support multiple calls in one func */ +@ rule13a @ +identifier rule11.fparent; +identifier rule12.T; +@@ + + int fparent (...) + { ++ VTY_DECLVAR_CONTEXT(T, T); + ... + } + +@ rule13b @ +identifier rule11.fparent; +identifier rule12.T; +type Tr; +@@ + + Tr *fparent (...) + { ++ struct T *T = VTY_GET_CONTEXT(T); ++ if (!T) { ++ return NULL; ++ } + ... + } + +/* now replace the argument in the call */ +@ rule14 exists @ +identifier rule11.fn; +identifier rule12.T; +@@ + + { + ... + \( + fn( +- vty->index, ++ T, + ...) + \| + fn( +- vty->index ++ T + ) + \) + ... + } + +/* special case ... */ +@rule30@ +identifier fn =~ "generic_(set|match)_"; +expression arg; +@@ + + fn(arg, +- arg->index, ++ VTY_GET_CONTEXT(route_map_index), + ...) + +/* and finally - PUSH_CONTEXT */ +@ rule99a exists @ +identifier tnode; +identifier vexpr =~ "NULL"; +@@ + +- vty->node = tnode; + ... +- vty->index = vexpr; ++ VTY_PUSH_CONTEXT_NULL(tnode); + +@ rule99b exists @ +identifier tnode; +expression vexpr; +@@ + +- vty->node = tnode; + ... +- vty->index = vexpr; ++ VTY_PUSH_CONTEXT(tnode, vexpr); + diff --git a/tools/coccinelle/vty_json.cocci b/tools/coccinelle/vty_json.cocci new file mode 100644 index 0000000..2f2f321 --- /dev/null +++ b/tools/coccinelle/vty_json.cocci @@ -0,0 +1,10 @@ +@@ +identifier vty; +identifier json; +constant fmt; +@@ + +-vty_out(vty, fmt, json_object_to_json_string_ext(json, ...)); +... +-json_object_free(json); ++vty_json(vty, json); diff --git a/tools/coccinelle/xcalloc-simple.cocci b/tools/coccinelle/xcalloc-simple.cocci new file mode 100644 index 0000000..5be4daf --- /dev/null +++ b/tools/coccinelle/xcalloc-simple.cocci @@ -0,0 +1,52 @@ +/// +/// Use zeroing allocator rather than allocator followed by memset with 0 +/// +/// This considers some simple cases that are common and easy to validate +/// Note in particular that there are no ...s in the rule, so all of the +/// matched code has to be contiguous +/// +// Confidence: High +// Copyright: (C) 2009-2010 Julia Lawall, Nicolas Palix, DIKU. GPLv2. +// Copyright: (C) 2009-2010 Gilles Muller, INRIA/LiP6. GPLv2. +// Copyright: (C) 2017 Himanshu Jha GPLv2. +// Copyright: (C) 2019 Quentin Young. GPLv2. +// URL: http://coccinelle.lip6.fr/rules/kzalloc.html +// Options: --no-includes --include-headers +// +// Keywords: XMALLOC, XCALLOC +// Version min: < 2.6.12 kmalloc +// Version min: 2.6.14 kzalloc +// + +virtual context +virtual patch + +//---------------------------------------------------------- +// For context mode +//---------------------------------------------------------- + +@depends on context@ +type T, T2; +expression x; +expression E1; +expression t; +@@ + +* x = (T)XMALLOC(t, E1); +* memset((T2)x,0,E1); + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@depends on patch@ +type T, T2; +expression x; +expression E1; +expression t; +@@ + +- x = (T)XMALLOC(t, E1); ++ x = (T)XCALLOC(t, E1); +- memset((T2)x,0,E1); + diff --git a/tools/coccinelle/xcalloc-xmalloc.cocci b/tools/coccinelle/xcalloc-xmalloc.cocci new file mode 100644 index 0000000..2a091d4 --- /dev/null +++ b/tools/coccinelle/xcalloc-xmalloc.cocci @@ -0,0 +1,17 @@ +// No need checking against NULL for XMALLOC/XCALLOC. +// If that happens, we have more problems with memory. + +@@ +type T; +T *ptr; +@@ + +ptr = +( +XCALLOC(...) +| +XMALLOC(...) +) +... +- if (ptr == NULL) +- return ...; diff --git a/tools/coccinelle/xfree.cocci b/tools/coccinelle/xfree.cocci new file mode 100644 index 0000000..eb38f0d --- /dev/null +++ b/tools/coccinelle/xfree.cocci @@ -0,0 +1,122 @@ +/// Find a use after free. +//# Values of variables may imply that some +//# execution paths are not possible, resulting in false positives. +//# Another source of false positives are macros such as +//# SCTP_DBG_OBJCNT_DEC that do not actually evaluate their argument +/// +// Confidence: Moderate +// Copyright: (C) 2010-2012 Nicolas Palix. GPLv2. +// Copyright: (C) 2010-2012 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2010-2012 Gilles Muller, INRIA/LiP6. GPLv2. +// Copyright: (C) 2019 Quentin Young. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report + +@free@ +expression E, t; +position p1; +@@ + +* XFREE@p1(t, E) + +@print expression@ +constant char [] c; +expression free.E,E2; +type T; +position p; +identifier f; +@@ + +( + f(...,c,...,(T)E@p,...) +| + E@p == E2 +| + E@p != E2 +| + E2 == E@p +| + E2 != E@p +| + !E@p +| + E@p || ... +) + +@sz@ +expression free.E; +position p; +@@ + + sizeof(<+...E@p...+>) + +@loop exists@ +expression E, t; +identifier l; +position ok; +@@ + +while (1) { ... +* XFREE@ok(t, E) + ... when != break; + when != goto l; + when forall +} + +@r exists@ +expression free.E, subE<=free.E, E2; +expression E1; +iterator iter; +statement S; +position free.p1!=loop.ok,p2!={print.p,sz.p}; +@@ + +* XFREE@p1(t, E) +... +( + iter(...,subE,...) S // no use +| + list_remove_head(E1,subE,...) +| + subE = E2 +| + subE++ +| + ++subE +| + --subE +| + subE-- +| + &subE +| + BUG(...) +| + BUG_ON(...) +| + return_VALUE(...) +| + return_ACPI_STATUS(...) +| + E@p2 // bad use +) + +@script:python depends on org@ +p1 << free.p1; +p2 << r.p2; +@@ + +cocci.print_main("kfree",p1) +cocci.print_secs("ref",p2) + +@script:python depends on report@ +p1 << free.p1; +p2 << r.p2; +@@ + +msg = "ERROR: reference preceded by free on line %s" % (p1[0].line) +coccilib.report.print_report(p2[0],msg) diff --git a/tools/coccinelle/xfreeaddr.cocci b/tools/coccinelle/xfreeaddr.cocci new file mode 100644 index 0000000..c99c7ac --- /dev/null +++ b/tools/coccinelle/xfreeaddr.cocci @@ -0,0 +1,33 @@ +/// Free of a structure field +/// +// Confidence: High +// Copyright: (C) 2013 Julia Lawall, INRIA/LIP6. GPLv2. +// Copyright: (C) 2019 Quentin Young. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: --no-includes --include-headers + +virtual org +virtual report +virtual context + +@r depends on context || report || org @ +expression e, t; +identifier f; +position p; +@@ + +* XFREE@p(t, &e->f) + +@script:python depends on org@ +p << r.p; +@@ + +cocci.print_main("XFREE",p) + +@script:python depends on report@ +p << r.p; +@@ + +msg = "ERROR: invalid free of structure field" +coccilib.report.print_report(p[0],msg) diff --git a/tools/coccinelle/xmalloc_returnval.cocci b/tools/coccinelle/xmalloc_returnval.cocci new file mode 100644 index 0000000..8e0ad10 --- /dev/null +++ b/tools/coccinelle/xmalloc_returnval.cocci @@ -0,0 +1,37 @@ +/// XMALLOC, XCALLOC etc either return non-null, or abort the program. +/// Never nullcheck these. +// +// Copyright: (C) 2019 Quentin Young. GPLv2. + +virtual patch + +//---------------------------------------------------------- +// For patch mode +//---------------------------------------------------------- + +@depends on patch@ +identifier alloc; +@@ + +alloc = XMALLOC(...); + +... + +- if (alloc == NULL) +- { +- ... +- } + +@depends on patch@ +identifier alloc; +@@ + +alloc = XCALLOC(...); + +... + +- if (alloc == NULL) +- { +- ... +- } + diff --git a/tools/coccinelle/zlog_no_newline.cocci b/tools/coccinelle/zlog_no_newline.cocci new file mode 100644 index 0000000..20cf9d2 --- /dev/null +++ b/tools/coccinelle/zlog_no_newline.cocci @@ -0,0 +1,20 @@ +// zlog_* should not have \n or \r at the end usually. +// spatch --sp-file tools/coccinelle/zlog_no_newline.cocci --macro-file tools/cocci.h ./ 2>/dev/null + +@r@ +expression fmt; +identifier func =~ "zlog_"; +position p; +@@ +( + func(fmt)@p +| + func(fmt, ...)@p +) + +@script:python@ +fmt << r.fmt; +p << r.p; +@@ +if "\\n" in str(fmt) or "\\r" in str(fmt): + print("Newline in logging function detected %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt)) diff --git a/tools/coccinelle/zprivs.cocci b/tools/coccinelle/zprivs.cocci new file mode 100644 index 0000000..11628a7 --- /dev/null +++ b/tools/coccinelle/zprivs.cocci @@ -0,0 +1,76 @@ +@@ +identifier change; +identifier end; +expression E, f, g; +iterator name frr_with_privs; +@@ + +- if (E.change(ZPRIVS_RAISE)) +- f; ++ frr_with_privs(&E) { + <+... +- goto end; ++ break; + ...+> +- end: +- if (E.change(ZPRIVS_LOWER)) +- g; ++ } + +@@ +identifier change, errno, safe_strerror, exit; +expression E, f1, f2, f3, ret, fn; +iterator name frr_with_privs; +@@ + + if (E.change(ZPRIVS_RAISE)) + f1; + ... + if (...) { +- int save_errno = errno; + ... +- if (E.change(ZPRIVS_LOWER)) +- f2; + ... +- safe_strerror(save_errno) ++ safe_strerror(errno) + ... + \( return ret; \| exit(ret); \) + } + ... + if (E.change(ZPRIVS_LOWER)) + f3; + +@@ +identifier change; +expression E, f1, f2, f3, ret; +iterator name frr_with_privs; +@@ + + if (E.change(ZPRIVS_RAISE)) + f1; + ... + if (...) { + ... +- if (E.change(ZPRIVS_LOWER)) +- f2; + ... + return ret; + } + ... + if (E.change(ZPRIVS_LOWER)) + f3; + +@@ +identifier change; +expression E, f, g; +iterator name frr_with_privs; +@@ + +- if (E.change(ZPRIVS_RAISE)) +- f; ++ frr_with_privs(&E) { + ... +- if (E.change(ZPRIVS_LOWER)) +- g; ++ } |