diff options
-rw-r--r-- | scripts/coccinelle/api/device_attr_show.cocci | 55 | ||||
-rw-r--r-- | scripts/coccinelle/api/kstrdup.cocci | 2 | ||||
-rw-r--r-- | scripts/coccinelle/api/kzfree.cocci | 101 | ||||
-rw-r--r-- | scripts/coccinelle/api/memdup_user.cocci | 64 | ||||
-rw-r--r-- | scripts/coccinelle/misc/array_size_dup.cocci | 209 |
5 files changed, 427 insertions, 4 deletions
diff --git a/scripts/coccinelle/api/device_attr_show.cocci b/scripts/coccinelle/api/device_attr_show.cocci new file mode 100644 index 000000000000..d8ec4bb8ac41 --- /dev/null +++ b/scripts/coccinelle/api/device_attr_show.cocci @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only +/// +/// From Documentation/filesystems/sysfs.txt: +/// show() must not use snprintf() when formatting the value to be +/// returned to user space. If you can guarantee that an overflow +/// will never happen you can use sprintf() otherwise you must use +/// scnprintf(). +/// +// Confidence: High +// Copyright: (C) 2020 Denis Efremov ISPRAS +// Options: --no-includes --include-headers +// + +virtual report +virtual org +virtual context +virtual patch + +@r depends on !patch@ +identifier show, dev, attr, buf; +position p; +@@ + +ssize_t show(struct device *dev, struct device_attribute *attr, char *buf) +{ + <... +* return snprintf@p(...); + ...> +} + +@rp depends on patch@ +identifier show, dev, attr, buf; +@@ + +ssize_t show(struct device *dev, struct device_attribute *attr, char *buf) +{ + <... + return +- snprintf ++ scnprintf + (...); + ...> +} + +@script: python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0], "WARNING: use scnprintf or sprintf") + +@script: python depends on org@ +p << r.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING: use scnprintf or sprintf") diff --git a/scripts/coccinelle/api/kstrdup.cocci b/scripts/coccinelle/api/kstrdup.cocci index 19f2645e6076..3c6dc5469ee4 100644 --- a/scripts/coccinelle/api/kstrdup.cocci +++ b/scripts/coccinelle/api/kstrdup.cocci @@ -66,7 +66,7 @@ position p1,p2; * x = strlen(from) + 1; ... when != \( x = E1 \| from = E1 \) -* to = \(kmalloc@p1\|kzalloc@p2\)(x,flag); +* to = \(kmalloc@p1\|kzalloc@p1\)(x,flag); ... when != \(x = E2 \| from = E2 \| to = E2 \) if (to==NULL || ...) S ... when != \(x = E3 \| from = E3 \| to = E3 \) diff --git a/scripts/coccinelle/api/kzfree.cocci b/scripts/coccinelle/api/kzfree.cocci new file mode 100644 index 000000000000..33625bd7cec9 --- /dev/null +++ b/scripts/coccinelle/api/kzfree.cocci @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-only +/// +/// Use kzfree, kvfree_sensitive rather than memset or +/// memzero_explicit followed by kfree +/// +// Confidence: High +// Copyright: (C) 2020 Denis Efremov ISPRAS +// Options: --no-includes --include-headers +// +// Keywords: kzfree, kvfree_sensitive +// + +virtual context +virtual patch +virtual org +virtual report + +@initialize:python@ +@@ +# kmalloc_oob_in_memset uses memset to explicitly trigger out-of-bounds access +filter = frozenset(['kmalloc_oob_in_memset', 'kzfree', 'kvfree_sensitive']) + +def relevant(p): + return not (filter & {el.current_element for el in p}) + +@cond@ +position ok; +@@ + +if (...) + \(memset@ok\|memzero_explicit@ok\)(...); + +@r depends on !patch forall@ +expression E; +position p : script:python() { relevant(p) }; +position m != cond.ok; +type T; +@@ + +( +* memset@m((T)E, 0, ...); +| +* memzero_explicit@m((T)E, ...); +) + ... when != E + when strict +* \(kfree\|vfree\|kvfree\)(E)@p; + +@rp_memzero depends on patch@ +expression E, size; +position p : script:python() { relevant(p) }; +position m != cond.ok; +type T; +@@ + +- memzero_explicit@m((T)E, size); + ... when != E + when strict +// TODO: uncomment when kfree_sensitive will be merged. +// Only this case is commented out because developers +// may not like patches like this since kzfree uses memset +// internally (not memzero_explicit). +//( +//- kfree(E)@p; +//+ kfree_sensitive(E); +//| +- \(vfree\|kvfree\)(E)@p; ++ kvfree_sensitive(E, size); +//) + +@rp_memset depends on patch@ +expression E, size; +position p : script:python() { relevant(p) }; +position m != cond.ok; +type T; +@@ + +- memset@m((T)E, 0, size); + ... when != E + when strict +( +- kfree(E)@p; ++ kzfree(E); +| +- \(vfree\|kvfree\)(E)@p; ++ kvfree_sensitive(E, size); +) + +@script:python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0], + "WARNING: opportunity for kzfree/kvfree_sensitive") + +@script:python depends on org@ +p << r.p; +@@ + +coccilib.org.print_todo(p[0], + "WARNING: opportunity for kzfree/kvfree_sensitive") diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci index c809ab10bbce..e01e95108405 100644 --- a/scripts/coccinelle/api/memdup_user.cocci +++ b/scripts/coccinelle/api/memdup_user.cocci @@ -15,12 +15,22 @@ virtual context virtual org virtual report +@initialize:python@ +@@ +filter = frozenset(['memdup_user', 'vmemdup_user']) + +def relevant(p): + return not (filter & {el.current_element for el in p}) + @depends on patch@ expression from,to,size; identifier l1,l2; +position p : script:python() { relevant(p) }; @@ -- to = \(kmalloc\|kzalloc\)(size,GFP_KERNEL); +- to = \(kmalloc@p\|kzalloc@p\) +- (size,\(GFP_KERNEL\|GFP_USER\| +- \(GFP_KERNEL\|GFP_USER\)|__GFP_NOWARN\)); + to = memdup_user(from,size); if ( - to==NULL @@ -37,13 +47,49 @@ identifier l1,l2; - ...+> - } +@depends on patch@ +expression from,to,size; +identifier l1,l2; +position p : script:python() { relevant(p) }; +@@ + +- to = \(kvmalloc@p\|kvzalloc@p\)(size,\(GFP_KERNEL\|GFP_USER\)); ++ to = vmemdup_user(from,size); + if ( +- to==NULL ++ IS_ERR(to) + || ...) { + <+... when != goto l1; +- -ENOMEM ++ PTR_ERR(to) + ...+> + } +- if (copy_from_user(to, from, size) != 0) { +- <+... when != goto l2; +- -EFAULT +- ...+> +- } + @r depends on !patch@ expression from,to,size; -position p; +position p : script:python() { relevant(p) }; statement S1,S2; @@ -* to = \(kmalloc@p\|kzalloc@p\)(size,GFP_KERNEL); +* to = \(kmalloc@p\|kzalloc@p\) + (size,\(GFP_KERNEL\|GFP_USER\| + \(GFP_KERNEL\|GFP_USER\)|__GFP_NOWARN\)); + if (to==NULL || ...) S1 + if (copy_from_user(to, from, size) != 0) + S2 + +@rv depends on !patch@ +expression from,to,size; +position p : script:python() { relevant(p) }; +statement S1,S2; +@@ + +* to = \(kvmalloc@p\|kvzalloc@p\)(size,\(GFP_KERNEL\|GFP_USER\)); if (to==NULL || ...) S1 if (copy_from_user(to, from, size) != 0) S2 @@ -59,3 +105,15 @@ p << r.p; @@ coccilib.report.print_report(p[0], "WARNING opportunity for memdup_user") + +@script:python depends on org@ +p << rv.p; +@@ + +coccilib.org.print_todo(p[0], "WARNING opportunity for vmemdup_user") + +@script:python depends on report@ +p << rv.p; +@@ + +coccilib.report.print_report(p[0], "WARNING opportunity for vmemdup_user") diff --git a/scripts/coccinelle/misc/array_size_dup.cocci b/scripts/coccinelle/misc/array_size_dup.cocci new file mode 100644 index 000000000000..fbc2ba1401d7 --- /dev/null +++ b/scripts/coccinelle/misc/array_size_dup.cocci @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-only +/// +/// Check for array_size(), array3_size(), struct_size() duplicates. +/// These patterns are detected: +/// 1. An opencoded expression is used before array_size() to compute the same size +/// 2. An opencoded expression is used after array_size() to compute the same size +/// From security point of view only first case is relevant. These functions +/// perform arithmetic overflow check. Thus, if we use an opencoded expression +/// before a call to the *_size() function we can miss an overflow. +/// +// Confidence: High +// Copyright: (C) 2020 Denis Efremov ISPRAS +// Options: --no-includes --include-headers --no-loops + +virtual context +virtual report +virtual org + +@as@ +expression E1, E2; +@@ + +array_size(E1, E2) + +@as_next@ +expression subE1 <= as.E1; +expression subE2 <= as.E2; +expression as.E1, as.E2, E3; +assignment operator aop; +position p1, p2; +@@ + +* E1 * E2@p1 + ... when != \(subE1\|subE2\) aop E3 + when != &\(subE1\|subE2\) +* array_size(E1, E2)@p2 + +@script:python depends on report@ +p1 << as_next.p1; +p2 << as_next.p2; +@@ + +msg = "WARNING: array_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.report.print_report(p1[0], msg) + +@script:python depends on org@ +p1 << as_next.p1; +p2 << as_next.p2; +@@ + +msg = "WARNING: array_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.org.print_todo(p1[0], msg) + +@as_prev@ +expression subE1 <= as.E1; +expression subE2 <= as.E2; +expression as.E1, as.E2, E3; +assignment operator aop; +position p1, p2; +@@ + +* array_size(E1, E2)@p1 + ... when != \(subE1\|subE2\) aop E3 + when != &\(subE1\|subE2\) +* E1 * E2@p2 + +@script:python depends on report@ +p1 << as_prev.p1; +p2 << as_prev.p2; +@@ + +msg = "WARNING: array_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.report.print_report(p2[0], msg) + +@script:python depends on org@ +p1 << as_prev.p1; +p2 << as_prev.p2; +@@ + +msg = "WARNING: array_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.org.print_todo(p2[0], msg) + +@as3@ +expression E1, E2, E3; +@@ + +array3_size(E1, E2, E3) + +@as3_next@ +expression subE1 <= as3.E1; +expression subE2 <= as3.E2; +expression subE3 <= as3.E3; +expression as3.E1, as3.E2, as3.E3, E4; +assignment operator aop; +position p1, p2; +@@ + +* E1 * E2 * E3@p1 + ... when != \(subE1\|subE2\|subE3\) aop E4 + when != &\(subE1\|subE2\|subE3\) +* array3_size(E1, E2, E3)@p2 + +@script:python depends on report@ +p1 << as3_next.p1; +p2 << as3_next.p2; +@@ + +msg = "WARNING: array3_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.report.print_report(p1[0], msg) + +@script:python depends on org@ +p1 << as3_next.p1; +p2 << as3_next.p2; +@@ + +msg = "WARNING: array3_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.org.print_todo(p1[0], msg) + +@as3_prev@ +expression subE1 <= as3.E1; +expression subE2 <= as3.E2; +expression subE3 <= as3.E3; +expression as3.E1, as3.E2, as3.E3, E4; +assignment operator aop; +position p1, p2; +@@ + +* array3_size(E1, E2, E3)@p1 + ... when != \(subE1\|subE2\|subE3\) aop E4 + when != &\(subE1\|subE2\|subE3\) +* E1 * E2 * E3@p2 + +@script:python depends on report@ +p1 << as3_prev.p1; +p2 << as3_prev.p2; +@@ + +msg = "WARNING: array3_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.report.print_report(p2[0], msg) + +@script:python depends on org@ +p1 << as3_prev.p1; +p2 << as3_prev.p2; +@@ + +msg = "WARNING: array3_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.org.print_todo(p2[0], msg) + +@ss@ +expression E1, E2, E3; +@@ + +struct_size(E1, E2, E3) + +@ss_next@ +expression subE3 <= ss.E3; +expression ss.E1, ss.E2, ss.E3, E4; +assignment operator aop; +position p1, p2; +@@ + +* E1 * E2 + E3@p1 + ... when != subE3 aop E4 + when != &subE3 +* struct_size(E1, E2, E3)@p2 + +@script:python depends on report@ +p1 << ss_next.p1; +p2 << ss_next.p2; +@@ + +msg = "WARNING: struct_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.report.print_report(p1[0], msg) + +@script:python depends on org@ +p1 << ss_next.p1; +p2 << ss_next.p2; +@@ + +msg = "WARNING: struct_size is used later (line %s) to compute the same size" % (p2[0].line) +coccilib.org.print_todo(p1[0], msg) + +@ss_prev@ +expression subE3 <= ss.E3; +expression ss.E1, ss.E2, ss.E3, E4; +assignment operator aop; +position p1, p2; +@@ + +* struct_size(E1, E2, E3)@p1 + ... when != subE3 aop E4 + when != &subE3 +* E1 * E2 + E3@p2 + +@script:python depends on report@ +p1 << ss_prev.p1; +p2 << ss_prev.p2; +@@ + +msg = "WARNING: struct_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.report.print_report(p2[0], msg) + +@script:python depends on org@ +p1 << ss_prev.p1; +p2 << ss_prev.p2; +@@ + +msg = "WARNING: struct_size is already used (line %s) to compute the same size" % (p1[0].line) +coccilib.org.print_todo(p2[0], msg) |