tools: add coccinelle spatches

Add some Coccinelle semantic patches we can use to automatically
refactor code in the future.

Signed-off-by: Quentin Young <qlyoung@cumulusnetworks.com>
This commit is contained in:
Quentin Young 2019-02-26 17:36:31 +00:00
parent aa406bbcc8
commit 25af5f0d79
30 changed files with 2283 additions and 0 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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)")

View File

@ -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

View File

@ -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)

View File

@ -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 |")

View File

@ -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)

View File

@ -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 ||")

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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>

View File

@ -0,0 +1,8 @@
@@
type T;
T[] E;
expression buf, srclen;
@@
- strncpy(E, src, srclen)
+ strlcpy(E, src, sizeof(E))

View File

@ -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)

View File

@ -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)

View File

@ -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';

View File

@ -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)

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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)
- {
- ...
- }