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)