Skip to content

Commit 146bfca

Browse files
authored
Merge pull request github#3254 from dbartol/dbartol/ImplicitReturnValue2
C++: Treat implicit end of body of non`-void` function as `Unreached`
2 parents be81a1a + 2794676 commit 146bfca

File tree

13 files changed

+747
-613
lines changed

13 files changed

+747
-613
lines changed

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ CppType getEllipsisVariablePRValueType() {
4949

5050
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
5151

52+
/**
53+
* Holds if the function returns a value, as opposed to returning `void`.
54+
*/
55+
predicate hasReturnValue(Function func) { not func.getUnspecifiedType() instanceof VoidType }
56+
5257
/**
5358
* Represents the IR translation of a function. This is the root elements for
5459
* all other elements associated with this function.
@@ -312,7 +317,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
312317
/**
313318
* Holds if the function has a non-`void` return type.
314319
*/
315-
final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType }
320+
final predicate hasReturnValue() { hasReturnValue(func) }
316321

317322
/**
318323
* Gets the single `UnmodeledDefinition` instruction for this function.

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,11 @@ abstract class TranslatedReturnStmt extends TranslatedStmt {
131131
}
132132
}
133133

134+
/**
135+
* The IR translation of a `return` statement that returns a value.
136+
*/
134137
class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
135-
TranslatedReturnValueStmt() { stmt.hasExpr() }
138+
TranslatedReturnValueStmt() { stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction()) }
136139

137140
final override Instruction getInitializationSuccessor() {
138141
result = getEnclosingFunction().getReturnSuccessorInstruction()
@@ -147,8 +150,49 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, TranslatedVariable
147150
final override IRVariable getIRVariable() { result = getEnclosingFunction().getReturnVariable() }
148151
}
149152

153+
/**
154+
* The IR translation of a `return` statement that returns an expression of `void` type.
155+
*/
156+
class TranslatedReturnVoidExpressionStmt extends TranslatedReturnStmt {
157+
TranslatedReturnVoidExpressionStmt() {
158+
stmt.hasExpr() and not hasReturnValue(stmt.getEnclosingFunction())
159+
}
160+
161+
override TranslatedElement getChild(int id) {
162+
id = 0 and
163+
result = getExpr()
164+
}
165+
166+
override Instruction getFirstInstruction() { result = getExpr().getFirstInstruction() }
167+
168+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
169+
tag = OnlyInstructionTag() and
170+
opcode instanceof Opcode::NoOp and
171+
resultType = getVoidType()
172+
}
173+
174+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
175+
tag = OnlyInstructionTag() and
176+
result = getEnclosingFunction().getReturnSuccessorInstruction() and
177+
kind instanceof GotoEdge
178+
}
179+
180+
override Instruction getChildSuccessor(TranslatedElement child) {
181+
child = getExpr() and
182+
result = getInstruction(OnlyInstructionTag())
183+
}
184+
185+
private TranslatedExpr getExpr() { result = getTranslatedExpr(stmt.getExpr()) }
186+
}
187+
188+
/**
189+
* The IR translation of a `return` statement that does not return a value. This includes implicit
190+
* return statements at the end of `void`-returning functions.
191+
*/
150192
class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
151-
TranslatedReturnVoidStmt() { not stmt.hasExpr() }
193+
TranslatedReturnVoidStmt() {
194+
not stmt.hasExpr() and not hasReturnValue(stmt.getEnclosingFunction())
195+
}
152196

153197
override TranslatedElement getChild(int id) { none() }
154198

@@ -169,6 +213,33 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
169213
override Instruction getChildSuccessor(TranslatedElement child) { none() }
170214
}
171215

216+
/**
217+
* The IR translation of an implicit `return` statement generated by the extractor to handle control
218+
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
219+
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
220+
* from continuing on to pollute other analysis. The assumption is that the developer is certain
221+
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
222+
*/
223+
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
224+
TranslatedUnreachableReturnStmt() {
225+
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
226+
}
227+
228+
override TranslatedElement getChild(int id) { none() }
229+
230+
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
231+
232+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
233+
tag = OnlyInstructionTag() and
234+
opcode instanceof Opcode::Unreached and
235+
resultType = getVoidType()
236+
}
237+
238+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
239+
240+
override Instruction getChildSuccessor(TranslatedElement child) { none() }
241+
}
242+
172243
/**
173244
* The IR translation of a C++ `try` statement.
174245
*/

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8750,6 +8750,40 @@ ir.cpp:
87508750
# 1286| Type = [PointerType] A *
87518751
# 1286| ValueCategory = prvalue
87528752
# 1287| 12: [ReturnStmt] return ...
8753+
# 1289| [TopLevelFunction] int missingReturnValue(bool, int)
8754+
# 1289| params:
8755+
# 1289| 0: [Parameter] b
8756+
# 1289| Type = [BoolType] bool
8757+
# 1289| 1: [Parameter] x
8758+
# 1289| Type = [IntType] int
8759+
# 1289| body: [Block] { ... }
8760+
# 1290| 0: [IfStmt] if (...) ...
8761+
# 1290| 0: [VariableAccess] b
8762+
# 1290| Type = [BoolType] bool
8763+
# 1290| ValueCategory = prvalue(load)
8764+
# 1290| 1: [Block] { ... }
8765+
# 1291| 0: [ReturnStmt] return ...
8766+
# 1291| 0: [VariableAccess] x
8767+
# 1291| Type = [IntType] int
8768+
# 1291| ValueCategory = prvalue(load)
8769+
# 1293| 1: [ReturnStmt] return ...
8770+
# 1295| [TopLevelFunction] void returnVoid(int, int)
8771+
# 1295| params:
8772+
# 1295| 0: [Parameter] x
8773+
# 1295| Type = [IntType] int
8774+
# 1295| 1: [Parameter] y
8775+
# 1295| Type = [IntType] int
8776+
# 1295| body: [Block] { ... }
8777+
# 1296| 0: [ReturnStmt] return ...
8778+
# 1296| 0: [FunctionCall] call to IntegerOps
8779+
# 1296| Type = [VoidType] void
8780+
# 1296| ValueCategory = prvalue
8781+
# 1296| 0: [VariableAccess] x
8782+
# 1296| Type = [IntType] int
8783+
# 1296| ValueCategory = prvalue(load)
8784+
# 1296| 1: [VariableAccess] y
8785+
# 1296| Type = [IntType] int
8786+
# 1296| ValueCategory = prvalue(load)
87538787
perf-regression.cpp:
87548788
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
87558789
# 4| params:

cpp/ql/test/library-tests/ir/ir/ir.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,10 +1249,10 @@ char *strcpy(char *destination, const char *source);
12491249
char *strcat(char *destination, const char *source);
12501250

12511251
void test_strings(char *s1, char *s2) {
1252-
char buffer[1024] = {0};
1252+
char buffer[1024] = {0};
12531253

1254-
strcpy(buffer, s1);
1255-
strcat(buffer, s2);
1254+
strcpy(buffer, s1);
1255+
strcat(buffer, s2);
12561256
}
12571257

12581258
struct A {
@@ -1286,4 +1286,14 @@ void test_static_member_functions(int int_arg, A* a_arg) {
12861286
getAnInstanceOfA()->static_member_without_def();
12871287
}
12881288

1289+
int missingReturnValue(bool b, int x) {
1290+
if (b) {
1291+
return x;
1292+
}
1293+
}
1294+
1295+
void returnVoid(int x, int y) {
1296+
return IntegerOps(x, y);
1297+
}
1298+
12891299
// semmle-extractor-options: -std=c++17 --clang

cpp/ql/test/library-tests/ir/ir/raw_ir.expected

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6631,6 +6631,59 @@ ir.cpp:
66316631
# 1270| v1270_14(void) = AliasedUse : ~mu1270_4
66326632
# 1270| v1270_15(void) = ExitFunction :
66336633

6634+
# 1289| int missingReturnValue(bool, int)
6635+
# 1289| Block 0
6636+
# 1289| v1289_1(void) = EnterFunction :
6637+
# 1289| mu1289_2(unknown) = AliasedDefinition :
6638+
# 1289| mu1289_3(unknown) = InitializeNonLocal :
6639+
# 1289| mu1289_4(unknown) = UnmodeledDefinition :
6640+
# 1289| r1289_5(glval<bool>) = VariableAddress[b] :
6641+
# 1289| mu1289_6(bool) = InitializeParameter[b] : &:r1289_5
6642+
# 1289| r1289_7(glval<int>) = VariableAddress[x] :
6643+
# 1289| mu1289_8(int) = InitializeParameter[x] : &:r1289_7
6644+
# 1290| r1290_1(glval<bool>) = VariableAddress[b] :
6645+
# 1290| r1290_2(bool) = Load : &:r1290_1, ~mu1289_4
6646+
# 1290| v1290_3(void) = ConditionalBranch : r1290_2
6647+
#-----| False -> Block 1
6648+
#-----| True -> Block 2
6649+
6650+
# 1293| Block 1
6651+
# 1293| v1293_1(void) = Unreached :
6652+
6653+
# 1291| Block 2
6654+
# 1291| r1291_1(glval<int>) = VariableAddress[#return] :
6655+
# 1291| r1291_2(glval<int>) = VariableAddress[x] :
6656+
# 1291| r1291_3(int) = Load : &:r1291_2, ~mu1289_4
6657+
# 1291| mu1291_4(int) = Store : &:r1291_1, r1291_3
6658+
# 1289| r1289_9(glval<int>) = VariableAddress[#return] :
6659+
# 1289| v1289_10(void) = ReturnValue : &:r1289_9, ~mu1289_4
6660+
# 1289| v1289_11(void) = UnmodeledUse : mu*
6661+
# 1289| v1289_12(void) = AliasedUse : ~mu1289_4
6662+
# 1289| v1289_13(void) = ExitFunction :
6663+
6664+
# 1295| void returnVoid(int, int)
6665+
# 1295| Block 0
6666+
# 1295| v1295_1(void) = EnterFunction :
6667+
# 1295| mu1295_2(unknown) = AliasedDefinition :
6668+
# 1295| mu1295_3(unknown) = InitializeNonLocal :
6669+
# 1295| mu1295_4(unknown) = UnmodeledDefinition :
6670+
# 1295| r1295_5(glval<int>) = VariableAddress[x] :
6671+
# 1295| mu1295_6(int) = InitializeParameter[x] : &:r1295_5
6672+
# 1295| r1295_7(glval<int>) = VariableAddress[y] :
6673+
# 1295| mu1295_8(int) = InitializeParameter[y] : &:r1295_7
6674+
# 1296| r1296_1(glval<unknown>) = FunctionAddress[IntegerOps] :
6675+
# 1296| r1296_2(glval<int>) = VariableAddress[x] :
6676+
# 1296| r1296_3(int) = Load : &:r1296_2, ~mu1295_4
6677+
# 1296| r1296_4(glval<int>) = VariableAddress[y] :
6678+
# 1296| r1296_5(int) = Load : &:r1296_4, ~mu1295_4
6679+
# 1296| v1296_6(void) = Call : func:r1296_1, 0:r1296_3, 1:r1296_5
6680+
# 1296| mu1296_7(unknown) = ^CallSideEffect : ~mu1295_4
6681+
# 1296| v1296_8(void) = NoOp :
6682+
# 1295| v1295_9(void) = ReturnVoid :
6683+
# 1295| v1295_10(void) = UnmodeledUse : mu*
6684+
# 1295| v1295_11(void) = AliasedUse : ~mu1295_4
6685+
# 1295| v1295_12(void) = ExitFunction :
6686+
66346687
perf-regression.cpp:
66356688
# 6| void Big::Big()
66366689
# 6| Block 0

cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
| test.cpp:97:10:97:10 | Load: x | file://:0:0:0:0 | 0 | 1 | false | CompareLT: ... < ... | test.cpp:94:7:94:11 | test.cpp:94:7:94:11 |
3636
| test.cpp:100:10:100:10 | Load: x | file://:0:0:0:0 | 0 | 1 | true | CompareLE: ... <= ... | test.cpp:99:7:99:12 | test.cpp:99:7:99:12 |
3737
| test.cpp:102:10:102:10 | Load: x | file://:0:0:0:0 | 0 | 2 | false | CompareLE: ... <= ... | test.cpp:99:7:99:12 | test.cpp:99:7:99:12 |
38-
| test.cpp:107:5:107:10 | Phi: test10 | test.cpp:114:3:114:6 | Phi: call to sink | -1 | true | CompareLT: ... < ... | test.cpp:115:18:115:22 | test.cpp:115:18:115:22 |
38+
| test.cpp:117:10:117:10 | Load: i | test.cpp:114:3:114:6 | Phi: call to sink | -1 | true | CompareLT: ... < ... | test.cpp:116:7:116:11 | test.cpp:116:7:116:11 |
3939
| test.cpp:130:10:130:10 | Load: i | file://:0:0:0:0 | 0 | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
4040
| test.cpp:140:10:140:10 | Store: i | file://:0:0:0:0 | 0 | 1 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
4141
| test.cpp:140:10:140:10 | Store: i | test.cpp:135:16:135:16 | InitializeParameter: x | 0 | false | CompareLT: ... < ... | test.cpp:139:11:139:15 | test.cpp:139:11:139:15 |

cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,17 @@ void test9(int x) {
104104
}
105105

106106
// Phi nodes as bounds
107-
int test10(int y, int z, bool use_y) {
107+
void test10(int y, int z, bool use_y) {
108108
int x;
109109
if(use_y) {
110110
x = y;
111111
} else {
112112
x = z;
113113
}
114114
sink();
115-
for(int i = 0; i < x; i++) {
116-
return i;
117-
}
115+
int i = source();
116+
if (i < x)
117+
sink(i);
118118
}
119119

120120
// Irreducible CFGs

0 commit comments

Comments
 (0)