diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll index d56ebf10bbca..d44b28f041da 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcat.qll @@ -1,11 +1,12 @@ import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.SideEffect /** * The standard function `strcat` and its wide, sized, and Microsoft variants. */ -class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction { +class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction { StrcatFunction() { exists(string name | name = getName() | name = "strcat" or // strcat(dst, src) @@ -56,4 +57,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction { override predicate hasArrayWithNullTerminator(int param) { param = 1 } override predicate hasArrayWithUnknownSize(int param) { param = 0 } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { any() } + + override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { + i = 0 and + buffer = true and + mustWrite = false + } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + (i = 0 or i = 1) and + buffer = true + } } diff --git a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll index d4d7bcf28817..c7f0898f3582 100644 --- a/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll +++ b/cpp/ql/src/semmle/code/cpp/models/implementations/Strcpy.qll @@ -1,11 +1,12 @@ import semmle.code.cpp.models.interfaces.ArrayFunction import semmle.code.cpp.models.interfaces.DataFlow import semmle.code.cpp.models.interfaces.Taint +import semmle.code.cpp.models.interfaces.SideEffect /** * The standard function `strcpy` and its wide, sized, and Microsoft variants. */ -class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction { +class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction { StrcpyFunction() { this.hasName("strcpy") or this.hasName("_mbscpy") or @@ -74,4 +75,23 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction { output.isReturnValueDeref() ) } + + override predicate hasOnlySpecificReadSideEffects() { any() } + + override predicate hasOnlySpecificWriteSideEffects() { any() } + + override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) { + i = 0 and + buffer = true and + mustWrite = false + } + + override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { + i = 1 and + buffer = true + } + + override ParameterIndex getParameterSizeIndex(ParameterIndex i) { + hasArrayWithVariableSize(i, result) + } } diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index abce5a6a19d8..f6d9fdc8a35b 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -8475,6 +8475,76 @@ ir.cpp: # 1243| Type = [PointerType] const char * # 1243| ValueCategory = prvalue(load) # 1244| 3: [ReturnStmt] return ... +# 1248| [TopLevelFunction] char* strcpy(char*, char const*) +# 1248| params: +# 1248| 0: [Parameter] destination +# 1248| Type = [CharPointerType] char * +# 1248| 1: [Parameter] source +# 1248| Type = [PointerType] const char * +# 1249| [TopLevelFunction] char* strcat(char*, char const*) +# 1249| params: +# 1249| 0: [Parameter] destination +# 1249| Type = [CharPointerType] char * +# 1249| 1: [Parameter] source +# 1249| Type = [PointerType] const char * +# 1251| [TopLevelFunction] void test_strings(char*, char*) +# 1251| params: +# 1251| 0: [Parameter] s1 +# 1251| Type = [CharPointerType] char * +# 1251| 1: [Parameter] s2 +# 1251| Type = [CharPointerType] char * +# 1251| body: [Block] { ... } +# 1252| 0: [DeclStmt] declaration +# 1252| 0: [VariableDeclarationEntry] definition of buffer +# 1252| Type = [ArrayType] char[1024] +# 1252| init: [Initializer] initializer for buffer +# 1252| expr: [ArrayAggregateLiteral] {...} +# 1252| Type = [ArrayType] char[1024] +# 1252| ValueCategory = prvalue +# 1252| [0]: [CStyleCast] (char)... +# 1252| Conversion = [IntegralConversion] integral conversion +# 1252| Type = [PlainCharType] char +# 1252| Value = [CStyleCast] 0 +# 1252| ValueCategory = prvalue +# 1252| expr: [Literal] 0 +# 1252| Type = [IntType] int +# 1252| Value = [Literal] 0 +# 1252| ValueCategory = prvalue +# 1254| 1: [ExprStmt] ExprStmt +# 1254| 0: [FunctionCall] call to strcpy +# 1254| Type = [CharPointerType] char * +# 1254| ValueCategory = prvalue +# 1254| 0: [ArrayToPointerConversion] array to pointer conversion +# 1254| Type = [CharPointerType] char * +# 1254| ValueCategory = prvalue +# 1254| expr: [VariableAccess] buffer +# 1254| Type = [ArrayType] char[1024] +# 1254| ValueCategory = lvalue +# 1254| 1: [CStyleCast] (const char *)... +# 1254| Conversion = [PointerConversion] pointer conversion +# 1254| Type = [PointerType] const char * +# 1254| ValueCategory = prvalue +# 1254| expr: [VariableAccess] s1 +# 1254| Type = [CharPointerType] char * +# 1254| ValueCategory = prvalue(load) +# 1255| 2: [ExprStmt] ExprStmt +# 1255| 0: [FunctionCall] call to strcat +# 1255| Type = [CharPointerType] char * +# 1255| ValueCategory = prvalue +# 1255| 0: [ArrayToPointerConversion] array to pointer conversion +# 1255| Type = [CharPointerType] char * +# 1255| ValueCategory = prvalue +# 1255| expr: [VariableAccess] buffer +# 1255| Type = [ArrayType] char[1024] +# 1255| ValueCategory = lvalue +# 1255| 1: [CStyleCast] (const char *)... +# 1255| Conversion = [PointerConversion] pointer conversion +# 1255| Type = [PointerType] const char * +# 1255| ValueCategory = prvalue +# 1255| expr: [VariableAccess] s2 +# 1255| Type = [CharPointerType] char * +# 1255| ValueCategory = prvalue(load) +# 1256| 3: [ReturnStmt] return ... perf-regression.cpp: # 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&) # 4| params: diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index c1c1e55145be..6f6ab081347e 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1243,4 +1243,16 @@ void staticLocalWithConstructor(const char* dynamic) { static String c(dynamic); } +// --- strings --- + +char *strcpy(char *destination, const char *source); +char *strcat(char *destination, const char *source); + +void test_strings(char *s1, char *s2) { + char buffer[1024] = {0}; + + strcpy(buffer, s1); + strcat(buffer, s2); +} + // semmle-extractor-options: -std=c++17 --clang diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index e4bbac446ba2..69d8782eb8f6 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -6417,6 +6417,57 @@ ir.cpp: # 1241| mu1241_6(bool) = Store : &:r1241_1, r1241_5 #-----| Goto -> Block 1 +# 1251| void test_strings(char*, char*) +# 1251| Block 0 +# 1251| v1251_1(void) = EnterFunction : +# 1251| mu1251_2(unknown) = AliasedDefinition : +# 1251| mu1251_3(unknown) = InitializeNonLocal : +# 1251| mu1251_4(unknown) = UnmodeledDefinition : +# 1251| r1251_5(glval) = VariableAddress[s1] : +# 1251| mu1251_6(char *) = InitializeParameter[s1] : &:r1251_5 +# 1251| r1251_7(char *) = Load : &:r1251_5, ~mu1251_6 +# 1251| mu1251_8(unknown) = InitializeIndirection[s1] : &:r1251_7 +# 1251| r1251_9(glval) = VariableAddress[s2] : +# 1251| mu1251_10(char *) = InitializeParameter[s2] : &:r1251_9 +# 1251| r1251_11(char *) = Load : &:r1251_9, ~mu1251_10 +# 1251| mu1251_12(unknown) = InitializeIndirection[s2] : &:r1251_11 +# 1252| r1252_1(glval) = VariableAddress[buffer] : +# 1252| mu1252_2(char[1024]) = Uninitialized[buffer] : &:r1252_1 +# 1252| r1252_3(int) = Constant[0] : +# 1252| r1252_4(glval) = PointerAdd[1] : r1252_1, r1252_3 +# 1252| r1252_5(char) = Constant[0] : +# 1252| mu1252_6(char) = Store : &:r1252_4, r1252_5 +# 1252| r1252_7(int) = Constant[1] : +# 1252| r1252_8(glval) = PointerAdd[1] : r1252_1, r1252_7 +# 1252| r1252_9(unknown[1023]) = Constant[0] : +# 1252| mu1252_10(unknown[1023]) = Store : &:r1252_8, r1252_9 +# 1254| r1254_1(glval) = FunctionAddress[strcpy] : +# 1254| r1254_2(glval) = VariableAddress[buffer] : +# 1254| r1254_3(char *) = Convert : r1254_2 +# 1254| r1254_4(glval) = VariableAddress[s1] : +# 1254| r1254_5(char *) = Load : &:r1254_4, ~mu1251_4 +# 1254| r1254_6(char *) = Convert : r1254_5 +# 1254| r1254_7(char *) = Call : func:r1254_1, 0:r1254_3, 1:r1254_6 +# 1254| v1254_8(void) = ^BufferReadSideEffect[1] : &:r1254_6, ~mu1251_4 +# 1254| mu1254_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r1254_3 +# 1255| r1255_1(glval) = FunctionAddress[strcat] : +# 1255| r1255_2(glval) = VariableAddress[buffer] : +# 1255| r1255_3(char *) = Convert : r1255_2 +# 1255| r1255_4(glval) = VariableAddress[s2] : +# 1255| r1255_5(char *) = Load : &:r1255_4, ~mu1251_4 +# 1255| r1255_6(char *) = Convert : r1255_5 +# 1255| r1255_7(char *) = Call : func:r1255_1, 0:r1255_3, 1:r1255_6 +# 1255| v1255_8(void) = ^BufferReadSideEffect[0] : &:r1255_3, ~mu1251_4 +# 1255| v1255_9(void) = ^BufferReadSideEffect[1] : &:r1255_6, ~mu1251_4 +# 1255| mu1255_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r1255_3 +# 1256| v1256_1(void) = NoOp : +# 1251| v1251_13(void) = ReturnIndirection : &:r1251_7, ~mu1251_4 +# 1251| v1251_14(void) = ReturnIndirection : &:r1251_11, ~mu1251_4 +# 1251| v1251_15(void) = ReturnVoid : +# 1251| v1251_16(void) = UnmodeledUse : mu* +# 1251| v1251_17(void) = AliasedUse : ~mu1251_4 +# 1251| v1251_18(void) = ExitFunction : + perf-regression.cpp: # 6| void Big::Big() # 6| Block 0