1+ /**
2+ * Provides implementation classes modelling `strcpy` and various similar
3+ * functions. See `semmle.code.cpp.models.Models` for usage information.
4+ */
5+
16import semmle.code.cpp.models.interfaces.ArrayFunction
27import semmle.code.cpp.models.interfaces.DataFlow
38import semmle.code.cpp.models.interfaces.Taint
@@ -8,70 +13,110 @@ import semmle.code.cpp.models.interfaces.SideEffect
813 */
914class StrcpyFunction extends ArrayFunction , DataFlowFunction , TaintFunction , SideEffectFunction {
1015 StrcpyFunction ( ) {
11- this .hasName ( "strcpy" ) or
12- this .hasName ( "_mbscpy" ) or
13- this .hasName ( "wcscpy" ) or
14- this .hasName ( "strncpy" ) or
15- this .hasName ( "_strncpy_l" ) or
16- this .hasName ( "_mbsncpy" ) or
17- this .hasName ( "_mbsncpy_l" ) or
18- this .hasName ( "wcsncpy" ) or
19- this .hasName ( "_wcsncpy_l" )
16+ exists ( string name | name = getName ( ) |
17+ // strcpy(dst, src)
18+ name = "strcpy"
19+ or
20+ // wcscpy(dst, src)
21+ name = "wcscpy"
22+ or
23+ // _mbscpy(dst, src)
24+ name = "_mbscpy"
25+ or
26+ (
27+ name = "strcpy_s" or // strcpy_s(dst, max_amount, src)
28+ name = "wcscpy_s" or // wcscpy_s(dst, max_amount, src)
29+ name = "_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
30+ ) and
31+ // exclude the 2-parameter template versions
32+ // that find the size of a fixed size destination buffer.
33+ getNumberOfParameters ( ) = 3
34+ or
35+ // strncpy(dst, src, max_amount)
36+ name = "strncpy"
37+ or
38+ // _strncpy_l(dst, src, max_amount, locale)
39+ name = "_strncpy_l"
40+ or
41+ // wcsncpy(dst, src, max_amount)
42+ name = "wcsncpy"
43+ or
44+ // _wcsncpy_l(dst, src, max_amount, locale)
45+ name = "_wcsncpy_l"
46+ or
47+ // _mbsncpy(dst, src, max_amount)
48+ name = "_mbsncpy"
49+ or
50+ // _mbsncpy_l(dst, src, max_amount, locale)
51+ name = "_mbsncpy_l"
52+ )
2053 }
2154
22- override predicate hasArrayInput ( int bufParam ) { bufParam = 1 }
55+ /**
56+ * Holds if this is one of the `strcpy_s` variants.
57+ */
58+ private predicate isSVariant ( ) {
59+ exists ( string name | name = getName ( ) | name .suffix ( name .length ( ) - 2 ) = "_s" )
60+ }
61+
62+ /**
63+ * Gets the index of the parameter that is the maximum size of the copy (in characters).
64+ */
65+ int getParamSize ( ) {
66+ if isSVariant ( )
67+ then result = 1
68+ else
69+ if exists ( getName ( ) .indexOf ( "ncpy" ) )
70+ then result = 2
71+ else none ( )
72+ }
2373
24- override predicate hasArrayOutput ( int bufParam ) { bufParam = 0 }
74+ /**
75+ * Gets the index of the parameter that is the source of the copy.
76+ */
77+ int getParamSrc ( ) { if isSVariant ( ) then result = 2 else result = 1 }
2578
26- override predicate hasArrayWithNullTerminator ( int bufParam ) { bufParam = 1 }
79+ /**
80+ * Gets the index of the parameter that is the destination of the copy.
81+ */
82+ int getParamDest ( ) { result = 0 }
83+
84+ override predicate hasArrayInput ( int bufParam ) { bufParam = getParamSrc ( ) }
85+
86+ override predicate hasArrayOutput ( int bufParam ) { bufParam = getParamDest ( ) }
87+
88+ override predicate hasArrayWithNullTerminator ( int bufParam ) { bufParam = getParamSrc ( ) }
2789
2890 override predicate hasArrayWithVariableSize ( int bufParam , int countParam ) {
29- (
30- this .hasName ( "strncpy" ) or
31- this .hasName ( "_strncpy_l" ) or
32- this .hasName ( "_mbsncpy" ) or
33- this .hasName ( "_mbsncpy_l" ) or
34- this .hasName ( "wcsncpy" ) or
35- this .hasName ( "_wcsncpy_l" )
36- ) and
37- bufParam = 0 and
38- countParam = 2
91+ bufParam = getParamDest ( ) and
92+ countParam = getParamSize ( )
3993 }
4094
4195 override predicate hasArrayWithUnknownSize ( int bufParam ) {
42- (
43- this .hasName ( "strcpy" ) or
44- this .hasName ( "_mbscpy" ) or
45- this .hasName ( "wcscpy" )
46- ) and
47- bufParam = 0
96+ not exists ( getParamSize ( ) ) and
97+ bufParam = getParamDest ( )
4898 }
4999
50100 override predicate hasDataFlow ( FunctionInput input , FunctionOutput output ) {
51- input .isParameterDeref ( 1 ) and
52- output .isParameterDeref ( 0 )
101+ not exists ( getParamSize ( ) ) and
102+ input .isParameterDeref ( getParamSrc ( ) ) and
103+ output .isParameterDeref ( getParamDest ( ) )
53104 or
54- input .isParameterDeref ( 1 ) and
105+ not exists ( getParamSize ( ) ) and
106+ input .isParameterDeref ( getParamSrc ( ) ) and
55107 output .isReturnValueDeref ( )
56108 or
57- input .isParameter ( 0 ) and
109+ input .isParameter ( getParamDest ( ) ) and
58110 output .isReturnValue ( )
59111 }
60112
61113 override predicate hasTaintFlow ( FunctionInput input , FunctionOutput output ) {
114+ // these may do only a partial copy of the input buffer to the output
115+ // buffer
116+ exists ( getParamSize ( ) ) and
117+ input .isParameter ( getParamSrc ( ) ) and
62118 (
63- // these may do only a partial copy of the input buffer to the output
64- // buffer
65- this .hasName ( "strncpy" ) or
66- this .hasName ( "_strncpy_l" ) or
67- this .hasName ( "_mbsncpy" ) or
68- this .hasName ( "_mbsncpy_l" ) or
69- this .hasName ( "wcsncpy" ) or
70- this .hasName ( "_wcsncpy_l" )
71- ) and
72- input .isParameter ( 2 ) and
73- (
74- output .isParameterDeref ( 0 ) or
119+ output .isParameterDeref ( getParamDest ( ) ) or
75120 output .isReturnValueDeref ( )
76121 )
77122 }
@@ -81,17 +126,18 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
81126 override predicate hasOnlySpecificWriteSideEffects ( ) { any ( ) }
82127
83128 override predicate hasSpecificWriteSideEffect ( ParameterIndex i , boolean buffer , boolean mustWrite ) {
84- i = 0 and
129+ i = getParamDest ( ) and
85130 buffer = true and
86131 mustWrite = false
87132 }
88133
89134 override predicate hasSpecificReadSideEffect ( ParameterIndex i , boolean buffer ) {
90- i = 1 and
135+ i = getParamSrc ( ) and
91136 buffer = true
92137 }
93138
94139 override ParameterIndex getParameterSizeIndex ( ParameterIndex i ) {
95- hasArrayWithVariableSize ( i , result )
140+ i = getParamDest ( ) and
141+ result = getParamSize ( )
96142 }
97143}
0 commit comments