From abfc19ff202d287742483e15fd478ddd6ada2187 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 15 Jun 2020 16:02:42 +0300 Subject: coccinelle: api: add device_attr_show script According to the documentation[1] show() methods of device attributes should return the number of bytes printed into the buffer. This is the return value of scnprintf(). show() must not use snprintf() when formatting the value to be returned to user space. snprintf() returns the length the resulting string would be, assuming it all fit into the destination array[2]. scnprintf() return the length of the string actually created in buf. If one can guarantee that an overflow will never happen sprintf() can be used otherwise scnprintf(). [1] Documentation/filesystems/sysfs.txt [2] "snprintf() confusion" https://lwn.net/Articles/69419/ Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/api/device_attr_show.cocci | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 scripts/coccinelle/api/device_attr_show.cocci (limited to 'scripts/coccinelle') 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") -- cgit v1.2.3 From 2fbecb7dfefdbe8250a86f92bb81d87deebc0488 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Fri, 19 Jun 2020 16:23:07 +0300 Subject: coccinelle: api/kstrdup: fix coccinelle position There is a typo in rule r2. Position p1 should be attached to kzalloc() call. Fixes: 29a36d4dec6c ("scripts/coccinelle: improve the coverage of some semantic patches") Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/api/kstrdup.cocci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/coccinelle') 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 \) -- cgit v1.2.3 From de50862520480b90ec2fcaed6ad1ad41462fbf30 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Tue, 23 Jun 2020 01:10:56 +0300 Subject: coccinelle: misc: add array_size_dup script to detect missed overflow checks Detect an opencoded expression that is used before or after array_size()/array3_size()/struct_size() to compute the same size. Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/misc/array_size_dup.cocci | 209 +++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 scripts/coccinelle/misc/array_size_dup.cocci (limited to 'scripts/coccinelle') 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) -- cgit v1.2.3 From 7cf4be68b177855b620dbc045bf26606eed197ac Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Fri, 17 Jul 2020 14:57:09 +0300 Subject: coccinelle: api: add kzfree script Check for memset()/memzero_explicit() followed by kfree()/vfree()/kvfree(). Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/api/kzfree.cocci | 101 ++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 scripts/coccinelle/api/kzfree.cocci (limited to 'scripts/coccinelle') 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") -- cgit v1.2.3 From 547ade42ced037db77a82e67d70b55c0eecc49e0 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 20 Jul 2020 19:22:14 +0300 Subject: coccinelle: api: extend memdup_user transformation with GFP_USER Match GFP_USER and optional __GFP_NOWARN allocations with memdup_user.cocci rule. Commit 6c2c97a24f09 ("memdup_user(): switch to GFP_USER") switched memdup_user() from GFP_KERNEL to GFP_USER. In almost all cases it is still a good idea to recommend memdup_user() for GFP_KERNEL allocations. The motivation behind altering memdup_user() to GFP_USER: https://lkml.org/lkml/2018/1/6/333 Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/api/memdup_user.cocci | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'scripts/coccinelle') diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci index c809ab10bbce..0e29d41ecab8 100644 --- a/scripts/coccinelle/api/memdup_user.cocci +++ b/scripts/coccinelle/api/memdup_user.cocci @@ -20,7 +20,9 @@ expression from,to,size; identifier l1,l2; @@ -- to = \(kmalloc\|kzalloc\)(size,GFP_KERNEL); +- to = \(kmalloc\|kzalloc\) +- (size,\(GFP_KERNEL\|GFP_USER\| +- \(GFP_KERNEL\|GFP_USER\)|__GFP_NOWARN\)); + to = memdup_user(from,size); if ( - to==NULL @@ -43,7 +45,9 @@ position 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 -- cgit v1.2.3 From 9c568dbd677bcfc975220d3157c89c48669a23e3 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 20 Jul 2020 19:22:15 +0300 Subject: coccinelle: api: extend memdup_user rule with vmemdup_user() Add vmemdup_user() transformations to the memdup_user.cocci rule. Commit 50fd2f298bef ("new primitive: vmemdup_user()") introduced vmemdup_user(). The function uses kvmalloc with GPF_USER flag. Signed-off-by: Denis Efremov --- scripts/coccinelle/api/memdup_user.cocci | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'scripts/coccinelle') diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci index 0e29d41ecab8..60027e21c5e6 100644 --- a/scripts/coccinelle/api/memdup_user.cocci +++ b/scripts/coccinelle/api/memdup_user.cocci @@ -39,6 +39,28 @@ identifier l1,l2; - ...+> - } +@depends on patch@ +expression from,to,size; +identifier l1,l2; +@@ + +- to = \(kvmalloc\|kvzalloc\)(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; @@ -52,6 +74,17 @@ statement S1,S2; if (copy_from_user(to, from, size) != 0) S2 +@rv depends on !patch@ +expression from,to,size; +position 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 + @script:python depends on org@ p << r.p; @@ @@ -63,3 +96,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") -- cgit v1.2.3 From d05f94ad28c76cbd98aecee8e617f18454050f57 Mon Sep 17 00:00:00 2001 From: Denis Efremov Date: Mon, 20 Jul 2020 19:22:16 +0300 Subject: coccinelle: api: filter out memdup_user definitions Don't match memdup_user/vmemdup_user. Signed-off-by: Denis Efremov Signed-off-by: Julia Lawall --- scripts/coccinelle/api/memdup_user.cocci | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'scripts/coccinelle') diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci index 60027e21c5e6..e01e95108405 100644 --- a/scripts/coccinelle/api/memdup_user.cocci +++ b/scripts/coccinelle/api/memdup_user.cocci @@ -15,12 +15,20 @@ 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\) +- to = \(kmalloc@p\|kzalloc@p\) - (size,\(GFP_KERNEL\|GFP_USER\| - \(GFP_KERNEL\|GFP_USER\)|__GFP_NOWARN\)); + to = memdup_user(from,size); @@ -42,9 +50,10 @@ identifier l1,l2; @depends on patch@ expression from,to,size; identifier l1,l2; +position p : script:python() { relevant(p) }; @@ -- to = \(kvmalloc\|kvzalloc\)(size,\(GFP_KERNEL\|GFP_USER\)); +- to = \(kvmalloc@p\|kvzalloc@p\)(size,\(GFP_KERNEL\|GFP_USER\)); + to = vmemdup_user(from,size); if ( - to==NULL @@ -63,7 +72,7 @@ identifier l1,l2; @r depends on !patch@ expression from,to,size; -position p; +position p : script:python() { relevant(p) }; statement S1,S2; @@ @@ -76,7 +85,7 @@ statement S1,S2; @rv depends on !patch@ expression from,to,size; -position p; +position p : script:python() { relevant(p) }; statement S1,S2; @@ -- cgit v1.2.3