coccicheck: add a test for repeat copy_from_user
This is usually a sign of a resized request. Make sure there aren't
races or confusions possible.
Signed-off-by: Kees Cook <keescook@chromium.org>
diff --git a/scripts/coccinelle/tests/regetuser-wang.cocci b/scripts/coccinelle/tests/regetuser-wang.cocci
new file mode 100644
index 0000000..5292320
--- /dev/null
+++ b/scripts/coccinelle/tests/regetuser-wang.cocci
@@ -0,0 +1,391 @@
+/// Recopying from the same user buffer frequently indicates a pattern of
+/// Reading a size header, allocating, and then re-reading an entire
+/// structure. If the structure's size is not re-validated, this can lead
+/// to structure or data size confusions.
+///
+// Confidence: Moderate
+// Copyright: (C) 2013 Kees Cook, Chromium.
+// Copyright: (C) 2016 Pengfei Wang.
+// License: GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual report
+virtual org
+virtual context
+
+@initialize:python@
+@@
+# -------------------------Post Matching Process--------------------------
+def post_match_process(rule,p1,p2,src,ptr):
+ filename = p1[0].file
+ first = int(p1[0].line)
+ second = int(p2[0].line)
+
+ src_str = str(src)
+ ptr_str = str(ptr)
+ #print "src1:", src_str
+ #print "src2:", ptr_str
+ #print "first:", first
+ #print "second:", second
+
+ # Remove loop case, first and second fetch are not supposed to be
+ # in the same line.
+ if first == second:
+ return
+
+ # Remove reverse loop case, where first fetch behind second fetch
+ # but in last loop .
+ if first > second:
+ return
+
+ # Remove case of get_user(a, src++) or get_user(a, src + 4)
+ if src_str.find("+") != -1 or ptr_str.find("+") != -1:
+ return
+
+ # Remove case of get_user(a, src[i])
+ if src_str.find("[") != -1 or ptr_str.find("[") != -1:
+ return
+
+ # Remove case of get_user(a, src--) or get_user(a, src - 4)
+ if src_str.find("-") != -1 and src_str.find("->") == -1:
+ return
+ if ptr_str.find("-") != -1 and ptr_str.find("->") == -1:
+ return
+
+ # Remove false matching of src ===> (int*)src , but leave
+ # function call like u64_to_uptr(ctl_sccb.sccb)
+ if src_str.find("(") == 0 or ptr_str.find("(") == 0:
+ return
+
+ coccilib.report.print_report(p2[0],
+ "potentially dangerous second get_user() (rule %d: line %d vs line %d)" %
+ (rule, first, second))
+
+
+//-----------------Pattern Matching Rules-----------------------------------
+//------------------------------- case 1: normal case without src assignment
+@ rule1 disable drop_cast exists @
+expression addr,exp1,exp2,src,offset;
+position p1,p2;
+identifier func;
+type T1,T2;
+@@
+ func(...){
+ ...
+(
+ get_user(exp1, (T1)src)@p1
+|
+ get_user(exp1, src)@p1
+|
+ __get_user(exp1, (T1)src)@p1
+|
+ __get_user(exp1, src)@p1
+)
+ ... when any
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+
+(
+ get_user(exp2, (T2)src)@p2
+|
+ get_user(exp2, src)@p2
+|
+ __get_user(exp2,(T2)src)@p2
+|
+ __get_user(exp2, src)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p11 << rule1.p1;
+p12 << rule1.p2;
+s1 << rule1.src;
+@@
+
+if p11 and p12:
+ post_match_process(1, p11, p12, s1, s1)
+
+//----------------------------------- case 2: ptr = src at beginning, ptr first
+@ rule2 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,offset;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+(
+ get_user(exp1, (T1)ptr)@p1
+|
+ get_user(exp1, ptr)@p1
+|
+ __get_user(exp1, (T1)ptr)@p1
+|
+ __get_user(exp1, ptr)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+(
+ get_user(exp2, (T2)src)@p2
+|
+ get_user(exp2, src)@p2
+|
+ __get_user(exp2,(T2)src)@p2
+|
+ __get_user(exp2, src)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p21 << rule2.p1;
+p22 << rule2.p2;
+p2 << rule2.ptr;
+s2 << rule2.src;
+@@
+if p21 and p22:
+ post_match_process(2, p21, p22, s2, p2)
+//------------------------- case 3: ptr = src at beginning, src first
+@ rule3 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,offset;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+(
+ get_user(exp1, (T1)src)@p1
+|
+ get_user(exp1, src)@p1
+|
+ __get_user(exp1, (T1)src)@p1
+|
+ __get_user(exp1, src)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+(
+ get_user(exp2, (T2)ptr)@p2
+|
+ get_user(exp2, ptr)@p2
+|
+ __get_user(exp2,(T2)ptr)@p2
+|
+ __get_user(exp2, ptr)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p31 << rule3.p1;
+p32 << rule3.p2;
+p3 << rule3.ptr;
+s3 << rule3.src;
+@@
+if p31 and p32:
+ post_match_process(3, p31, p32, s3, p3)
+//----------------------------------- case 4: ptr = src at middle
+
+@ rule4 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,offset;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ get_user(exp1, (T1)src)@p1
+|
+ get_user(exp1, src)@p1
+|
+ __get_user(exp1, (T1)src)@p1
+|
+ __get_user(exp1, src)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+ when != ptr = addr
+ when != ptr ++
+ when != ptr --
+ when != ptr += offset
+ when != ptr -= offset
+ when != ptr = ptr + offset
+ when != ptr = ptr - offset
+
+(
+ get_user(exp2, (T2)ptr)@p2
+|
+ get_user(exp2, ptr)@p2
+|
+ __get_user(exp2,(T2)ptr)@p2
+|
+ __get_user(exp2, ptr)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p41 << rule4.p1;
+p42 << rule4.p2;
+p4 << rule4.ptr;
+s4 << rule4.src;
+@@
+if p41 and p42:
+ post_match_process(4, p41, p42, s4, p4)
+//----------------------------------- case 5: first element, then ptr, copy from structure
+@ rule5 disable drop_cast exists @
+identifier func, f1;
+expression addr,exp1,exp2,src,offset;
+position p1,p2;
+type T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ get_user(exp1, (T1)src->f1)@p1
+|
+ get_user(exp1, src->f1)@p1
+|
+ get_user(exp1, &(src->f1))@p1
+|
+ __get_user(exp1, (T1)src->f1)@p1
+|
+ __get_user(exp1, src->f1)@p1
+|
+ __get_user(exp1, &(src->f1))@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+(
+ get_user(exp2,(T2)src)@p2
+|
+ get_user(exp2,src)@p2
+|
+ __get_user(exp2,(T2)src)@p2
+|
+ __get_user(exp2,src)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p51 << rule5.p1;
+p52 << rule5.p2;
+s5 << rule5.src;
+e5 << rule5.f1;
+@@
+if p51 and p52:
+ post_match_process(5, p51, p52, s5, e5)
+
+
+//---------------------- case 6: first element, then ptr, copy from pointer
+@ rule6 disable drop_cast exists @
+identifier func, f1;
+expression addr,exp1,exp2,src,offset;
+position p1,p2;
+type T1,T2;
+@@
+ func(...){
+ ...
+(
+ get_user(exp1, (T1)src.f1)@p1
+|
+ get_user(exp1, src.f1)@p1
+|
+ get_user(exp1, &(src.f1))@p1
+|
+ __get_user(exp1, (T1)src.f1)@p1
+|
+ __get_user(exp1, src.f1)@p1
+|
+ __get_user(exp1, &(src.f1))@p1
+)
+ ...
+ when != &src = addr
+ when != &src ++
+ when != &src --
+ when != &src += offset
+ when != &src -= offset
+ when != &src = &src + offset
+ when != &src = &src - offset
+(
+ get_user(exp2,(T2)&src)@p2
+|
+ get_user(exp2,&src)@p2
+|
+ __get_user(exp2,(T2)&src)@p2
+|
+ __get_user(exp2,&src)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p61 << rule6.p1;
+p62 << rule6.p2;
+s6 << rule6.src;
+e6 << rule6.f1;
+@@
+if p61 and p62:
+ post_match_process(6, p61, p62, s6, e6)
diff --git a/scripts/coccinelle/tests/reusercopy-cook.cocci b/scripts/coccinelle/tests/reusercopy-cook.cocci
new file mode 100644
index 0000000..b0a71f9
--- /dev/null
+++ b/scripts/coccinelle/tests/reusercopy-cook.cocci
@@ -0,0 +1,49 @@
+/// Recopying from the same user buffer frequently indicates a pattern of
+/// Reading a size header, allocating, and then re-reading an entire
+/// structure. If the structure's size is not re-validated, this can lead
+/// to structure or data size confusions.
+///
+// Confidence: Moderate
+// Copyright: (C) 2013 Kees Cook, Chromium. GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual report
+virtual org
+virtual context
+
+@ok@
+position p;
+expression src,dest;
+@@
+
+copy_from_user@p(&dest, src, sizeof(dest))
+
+@cfu_twice@
+position p != ok.p;
+identifier src;
+expression dest1, dest2, size1, size2, offset, e1, e2;
+@@
+
+*copy_from_user(dest1, src, size1)
+ ... when != src = offset
+ when != src += offset
+ when != src -= offset
+ when != src ++
+ when != src --
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+*copy_from_user@p(dest2, src, size2)
+
+@script:python depends on org@
+p << cfu_twice.p;
+@@
+
+cocci.print_main("potentially dangerous second copy_from_user()",p)
+
+@script:python depends on report@
+p << cfu_twice.p;
+@@
+
+coccilib.report.print_report(p[0],"potentially dangerous second copy_from_user()")
diff --git a/scripts/coccinelle/tests/reusercopy-wang.cocci b/scripts/coccinelle/tests/reusercopy-wang.cocci
new file mode 100644
index 0000000..f57a1d9
--- /dev/null
+++ b/scripts/coccinelle/tests/reusercopy-wang.cocci
@@ -0,0 +1,389 @@
+/// Recopying from the same user buffer frequently indicates a pattern of
+/// reading a size header, allocating, and then re-reading an entire
+/// structure. If the structure's size is not re-validated, this can lead
+/// to structure or data size confusions.
+///
+// Confidence: Moderate
+// Copyright: (C) 2013 Kees Cook, Chromium.
+// Copyright: (C) 2016 Pengfei Wang.
+// License: GPLv2.
+// URL: http://coccinelle.lip6.fr/
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual report
+virtual org
+virtual context
+
+@initialize:python@
+@@
+# -------------------------Post Matching Process--------------------------
+def post_match_process(rule,p1,p2,src,ptr):
+ filename = p1[0].file
+ first = int(p1[0].line)
+ second = int(p2[0].line)
+
+ src_str = str(src)
+ ptr_str = str(ptr)
+
+ # Remove loop case, first and second fetch are not supposed to be
+ # in the same line.
+ if first == second:
+ return
+
+ # Remove reverse loop case, where first fetch behind second fetch
+ # but in last loop .
+ if first > second:
+ return
+
+ # Remove false matching of src ===> (int*)src , but leave
+ # function call like u64_to_uptr(ctl_sccb.sccb)
+ if src_str.find("(") == 0 or ptr_str.find("(") == 0:
+ return
+
+ coccilib.report.print_report(p2[0],
+ "potentially dangerous second copy_from_user() (rule %d: line %d vs line %d)" %
+ (rule, first, second))
+
+
+//-----------------Pattern Matching Rules-----------------------------------
+//------------------------------- case 1: normal case without src assignment
+@ rule1 disable drop_cast exists @
+expression addr,exp1,exp2,src,size1,size2,offset,e1,e2;
+position p1,p2;
+identifier func;
+type T1,T2;
+@@
+ func(...){
+ ...
+(
+ copy_from_user(exp1, (T1)src, size1)@p1
+|
+ copy_from_user(exp1, src, size1)@p1
+|
+ __copy_from_user(exp1, (T1)src, size1)@p1
+|
+ __copy_from_user(exp1, src, size1)@p1
+
+)
+ ... when any
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+
+(
+ __copy_from_user(exp2,(T2)src,size2)@p2
+|
+ __copy_from_user(exp2, src,size2)@p2
+|
+ copy_from_user(exp2,(T2)src,size2)@p2
+|
+ copy_from_user(exp2, src,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p11 << rule1.p1;
+p12 << rule1.p2;
+s1 << rule1.src;
+@@
+
+if p11 and p12:
+ post_match_process(1, p11, p12, s1, s1)
+
+//------------------------------ case 2: ptr = src at beginning, ptr first
+@ rule2 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,size1,size2,offset,e1,e2;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+(
+ copy_from_user(exp1, (T1)ptr,size1)@p1
+|
+ copy_from_user(exp1, ptr,size1)@p1
+|
+ __copy_from_user(exp1, (T1)ptr,size1)@p1
+|
+ __copy_from_user(exp1, ptr,size1)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+(
+ __copy_from_user(exp2,(T2)src,size2)@p2
+|
+ __copy_from_user(exp2, src,size2)@p2
+|
+ copy_from_user(exp2,(T2)src,size2)@p2
+|
+ copy_from_user(exp2, src,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p21 << rule2.p1;
+p22 << rule2.p2;
+p2 << rule2.ptr;
+s2 << rule2.src;
+@@
+if p21 and p22:
+ post_match_process(2, p21, p22, s2, p2)
+//------------------------- case 3: ptr = src at beginning, src first
+@ rule3 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,size1,size2,offset,e1,e2;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+(
+ copy_from_user(exp1, (T1)src,size1)@p1
+|
+ copy_from_user(exp1, src,size1)@p1
+|
+ __copy_from_user(exp1, (T1)src,size1)@p1
+|
+ __copy_from_user(exp1, src,size1)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+(
+ __copy_from_user(exp2,(T2)ptr,size2)@p2
+|
+ __copy_from_user(exp2, ptr,size2)@p2
+|
+ copy_from_user(exp2,(T2)ptr,size2)@p2
+|
+ copy_from_user(exp2, ptr,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p31 << rule3.p1;
+p32 << rule3.p2;
+p3 << rule3.ptr;
+s3 << rule3.src;
+@@
+if p31 and p32:
+ post_match_process(3, p31, p32, s3, p3)
+//----------------------------------- case 4: ptr = src at middle
+
+@ rule4 disable drop_cast exists @
+identifier func;
+expression addr,exp1,exp2,src,ptr,size1,size2,offset,e1,e2;
+position p0,p1,p2;
+type T0,T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ copy_from_user(exp1, (T1)src,size1)@p1
+|
+ copy_from_user(exp1, src,size1)@p1
+|
+ __copy_from_user(exp1, (T1)src,size1)@p1
+|
+ __copy_from_user(exp1, src,size1)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+
+(
+ ptr = (T0)src@p0 // potential assignment case
+|
+ ptr = src@p0
+)
+ ...
+ when != ptr = addr
+ when != ptr ++
+ when != ptr --
+ when != ptr += offset
+ when != ptr -= offset
+ when != ptr = ptr + offset
+ when != ptr = ptr - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+
+(
+ __copy_from_user(exp2,(T2)ptr,size2)@p2
+|
+ __copy_from_user(exp2, ptr,size2)@p2
+|
+ copy_from_user(exp2,(T2)ptr,size2)@p2
+|
+ copy_from_user(exp2, ptr,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p41 << rule4.p1;
+p42 << rule4.p2;
+p4 << rule4.ptr;
+s4 << rule4.src;
+@@
+if p41 and p42:
+ post_match_process(4, p41, p42, s4, p4)
+
+//-------------------- case 5: first element, then ptr, copy from structure
+@ rule5 disable drop_cast exists @
+identifier func, f1;
+expression addr,exp1,exp2,src,size1,size2,offset,e1,e2;
+position p1,p2;
+type T1,T2;
+@@
+
+
+ func(...){
+ ...
+(
+ copy_from_user(exp1, (T1)src->f1,size1)@p1
+|
+ copy_from_user(exp1, src->f1,size1)@p1
+|
+ copy_from_user(exp1, &(src->f1),size1)@p1
+|
+ __copy_from_user(exp1, (T1)src->f1,size1)@p1
+|
+ __copy_from_user(exp1, src->f1,size1)@p1
+|
+ __copy_from_user(exp1, &(src->f1),size1)@p1
+)
+ ...
+ when != src = addr
+ when != src ++
+ when != src --
+ when != src += offset
+ when != src -= offset
+ when != src = src + offset
+ when != src = src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+(
+ __copy_from_user(exp2,(T2)src,size2)@p2
+|
+ __copy_from_user(exp2,src,size2)@p2
+|
+ copy_from_user(exp2,(T2)src,size2)@p2
+|
+ copy_from_user(exp2,src,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p51 << rule5.p1;
+p52 << rule5.p2;
+s5 << rule5.src;
+e5 << rule5.f1;
+@@
+if p51 and p52:
+ post_match_process(5, p51, p52, s5, e5)
+
+
+//---------------------- case 6: first element, then ptr, copy from pointer
+@ rule6 disable drop_cast exists @
+identifier func, f1;
+expression addr,exp1,exp2,src,size1,size2,offset,e1,e2;
+position p1,p2;
+type T1,T2;
+@@
+ func(...){
+ ...
+(
+ copy_from_user(exp1, (T1)src.f1, size1)@p1
+|
+ copy_from_user(exp1, src.f1, size1)@p1
+|
+ copy_from_user(exp1, &(src.f1), size1)@p1
+|
+ __copy_from_user(exp1, (T1)src.f1, size1)@p1
+|
+ __copy_from_user(exp1, src.f1, size1)@p1
+|
+ __copy_from_user(exp1, &(src.f1), size1)@p1
+)
+ ...
+ when != &src = addr
+ when != &src ++
+ when != &src --
+ when != &src += offset
+ when != &src -= offset
+ when != &src = &src + offset
+ when != &src = &src - offset
+ when != if (size2 > e1 || ...) { ... return ...; }
+ when != if (size2 > e1 || ...) { ... size2 = e2 ... }
+(
+ __copy_from_user(exp2,(T2)&src,size2)@p2
+|
+ __copy_from_user(exp2,&src,size2)@p2
+|
+ copy_from_user(exp2,(T2)&src,size2)@p2
+|
+ copy_from_user(exp2,&src,size2)@p2
+)
+ ...
+ }
+
+@script:python depends on report@
+p61 << rule6.p1;
+p62 << rule6.p2;
+s6 << rule6.src;
+e6 << rule6.f1;
+@@
+if p61 and p62:
+ post_match_process(6, p61, p62, s6, e6)