Skip to content

Commit 8624f19

Browse files
committed
Adding basic SEH edge support. SEH exceptions are only considered for calls. Load and store SEH handling will be addressed in a separate PR.
1 parent 7dd10f7 commit 8624f19

File tree

6 files changed

+136
-53
lines changed

6 files changed

+136
-53
lines changed

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
44
private import semmle.code.cpp.ir.internal.CppType
55
private import semmle.code.cpp.models.interfaces.SideEffect
66
private import semmle.code.cpp.models.interfaces.Throwing
7+
private import semmle.code.cpp.models.interfaces.NonThrowing
78
private import InstructionTag
89
private import SideEffects
910
private import TranslatedElement
@@ -84,12 +85,14 @@ abstract class TranslatedCall extends TranslatedExpr {
8485
this.getEnclosingFunction().getFunction() = instr.getEnclosingFunction()
8586
)
8687
else (
87-
not this.mustThrowException() and
88+
not this.mustThrowException(_) and
8889
result = this.getParent().getChildSuccessor(this, kind)
8990
or
90-
this.mayThrowException() and
91-
kind instanceof CppExceptionEdge and
92-
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
91+
exists(ExceptionEdge e | this.hasExceptionBehavior(e) |
92+
this.mayThrowException(e) and
93+
kind = e and
94+
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge), kind)
95+
)
9396
)
9497
}
9598

@@ -118,13 +121,42 @@ abstract class TranslatedCall extends TranslatedExpr {
118121

119122
/**
120123
* Holds if the evaluation of this call may throw an exception.
124+
* The edge `e` determines what type of exception is being considered,
125+
* `CppExceptionEdge` or `SehExceptionEdge`.
121126
*/
122-
abstract predicate mayThrowException();
127+
abstract predicate mayThrowException(ExceptionEdge e);
123128

124129
/**
125130
* Holds if the evaluation of this call always throws an exception.
131+
* The edge `e` determines what type of exception is being considered,
132+
* `CppExceptionEdge` or `SehExceptionEdge`.
133+
*/
134+
abstract predicate mustThrowException(ExceptionEdge e);
135+
136+
/**
137+
* Holds when the call target is known to never raise an exception.
138+
* The edge `e` determines what type of exception is being considered,
139+
* `CppExceptionEdge` or `SehExceptionEdge`.
140+
*
141+
* Note that `alwaysRaiseException`, `mayRaiseException`,
142+
* and `neverRaiseException` may conflict (e.g., all hold for a given target).
143+
* Conflicting results are resolved during IR generation.
144+
*/
145+
abstract predicate neverThrowException(ExceptionEdge e);
146+
147+
/**
148+
* Holds if the call has any exception behavior defined for exception edge type `e`,
149+
* i.e., if the function is known to throw or never throw an exception.
150+
* This handles cases where a function has no annotation to specify
151+
* if a function throws or doesn't throw.
126152
*/
127-
abstract predicate mustThrowException();
153+
final predicate hasExceptionBehavior(ExceptionEdge e) {
154+
this.mayThrowException(e)
155+
or
156+
this.mustThrowException(e)
157+
or
158+
this.neverThrowException(e)
159+
}
128160

129161
/**
130162
* Gets the result type of the call.
@@ -320,6 +352,32 @@ abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedC
320352
final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }
321353

322354
final override predicate isNoReturn() { any(Options opt).exits(expr.getTarget()) }
355+
356+
override predicate mustThrowException(ExceptionEdge e) {
357+
// Functions that always raise exceptions only occur with Seh exceptions
358+
// Use the `AlwaysSehThrowingFunction` instance unless the function is known to never throw
359+
not this.neverThrowException(e) and
360+
expr.getTarget() instanceof AlwaysSehThrowingFunction and
361+
e instanceof SehExceptionEdge
362+
}
363+
364+
override predicate mayThrowException(ExceptionEdge e) {
365+
// by default, all functions may throw exceptions of any kind
366+
// unless explicitly annotated to never throw
367+
not this.neverThrowException(e) and
368+
// for now assuming all calls may throw for Seh only
369+
e instanceof SehExceptionEdge
370+
}
371+
372+
override predicate neverThrowException(ExceptionEdge e) {
373+
// currently, only functions can only explicitly stipulate they
374+
// never through a C++ exception
375+
// NOTE: we cannot simply check if not exists may and must throw.
376+
// since an annotation exists to override any other behavior
377+
// i.e., `NonCppThrowingFunction`.
378+
e instanceof CppExceptionEdge and
379+
expr.getTarget() instanceof NonCppThrowingFunction
380+
}
323381
}
324382

325383
/**
@@ -332,14 +390,16 @@ class TranslatedExprCall extends TranslatedCallExpr {
332390
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
333391
}
334392

335-
final override predicate mayThrowException() {
393+
final override predicate mayThrowException(ExceptionEdge e) { none() }
394+
395+
final override predicate mustThrowException(ExceptionEdge e) { none() }
396+
397+
final override predicate neverThrowException(ExceptionEdge e) {
336398
// We assume that a call to a function pointer will not throw an exception.
337399
// This is not sound in general, but this will greatly reduce the number of
338400
// exceptional edges.
339-
none()
401+
any()
340402
}
341-
342-
final override predicate mustThrowException() { none() }
343403
}
344404

345405
/**
@@ -361,18 +421,6 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
361421
exists(this.getQualifier()) and
362422
not exists(MemberFunction func | expr.getTarget() = func and func.isStatic())
363423
}
364-
365-
final override predicate mayThrowException() {
366-
expr.getTarget().(ThrowingFunction).mayThrowException(_)
367-
or
368-
expr.getTarget() instanceof AlwaysSehThrowingFunction
369-
}
370-
371-
final override predicate mustThrowException() {
372-
expr.getTarget().(ThrowingFunction).mayThrowException(true)
373-
or
374-
expr.getTarget() instanceof AlwaysSehThrowingFunction
375-
}
376424
}
377425

378426
/**

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,8 +1107,8 @@ abstract class TranslatedElement extends TTranslatedElement {
11071107
* nearest enclosing `try`, or the `Unwind` instruction for the function if
11081108
* there is no enclosing `try`. The successor edge kind is specified by `kind`.
11091109
*/
1110-
Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
1111-
result = this.getParent().getExceptionSuccessorInstruction(kind)
1110+
Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
1111+
result = this.getParent().getExceptionSuccessorInstruction(kind, exception)
11121112
}
11131113

11141114
/**

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ private import TranslatedInitialization
1515
private import TranslatedStmt
1616
private import TranslatedGlobalVar
1717
private import IRConstruction
18+
private import EdgeKind
1819
import TranslatedCall
1920

2021
/**
@@ -2375,14 +2376,16 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
23752376
result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index).getFullyConverted())
23762377
}
23772378

2378-
final override predicate mayThrowException() {
2379+
final override predicate mayThrowException(ExceptionEdge e) { none() }
2380+
2381+
final override predicate mustThrowException(ExceptionEdge e) { none() }
2382+
2383+
final override predicate neverThrowException(ExceptionEdge e) {
23792384
// We assume that a call to `new` or `new[]` will never throw. This is not
23802385
// sound in general, but this will greatly reduce the number of exceptional
23812386
// edges.
2382-
none()
2387+
any()
23832388
}
2384-
2385-
final override predicate mustThrowException() { none() }
23862389
}
23872390

23882391
TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
@@ -2448,14 +2451,16 @@ class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, Trans
24482451
result = getTranslatedExpr(expr.getExprWithReuse().getFullyConverted())
24492452
}
24502453

2451-
final override predicate mayThrowException() {
2454+
final override predicate mayThrowException(ExceptionEdge e) { none() }
2455+
2456+
final override predicate mustThrowException(ExceptionEdge e) { none() }
2457+
2458+
final override predicate neverThrowException(ExceptionEdge e) {
24522459
// We assume that a call to `delete` or `delete[]` will never throw. This is not
24532460
// sound in general, but this will greatly reduce the number of exceptional
24542461
// edges.
2455-
none()
2462+
any()
24562463
}
2457-
2458-
final override predicate mustThrowException() { none() }
24592464
}
24602465

24612466
TranslatedDeleteOrDeleteArrayExpr getTranslatedDeleteOrDeleteArray(DeleteOrDeleteArrayExpr newExpr) {
@@ -3040,7 +3045,7 @@ class TranslatedDestructorsAfterThrow extends TranslatedElement, TTranslatedDest
30403045
// And otherwise, exit this element with an exceptional edge
30413046
not exists(this.getChild(id + 1)) and
30423047
kind instanceof CppExceptionEdge and
3043-
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
3048+
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge), kind)
30443049
)
30453050
}
30463051

@@ -3079,7 +3084,7 @@ abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr {
30793084
or
30803085
not exists(this.getDestructors()) and
30813086
kind instanceof CppExceptionEdge and
3082-
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
3087+
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge), kind)
30833088
)
30843089
}
30853090

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,12 +209,14 @@ class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction {
209209
(
210210
// Only generate the `Unwind` instruction if there is any exception
211211
// handling present in the function.
212-
exists(TryOrMicrosoftTryStmt try | try.getEnclosingFunction() = func)
212+
// Do not unwind for MicrosoftTryStmt (SEH), as an optimization (SEH exception
213+
// will occur at any store/load, so unwind would appear everywhere as a result)
214+
exists(TryStmt try | try.getEnclosingFunction() = func)
213215
or
214216
exists(ThrowExpr throw | throw.getEnclosingFunction() = func)
215217
or
216-
exists(FunctionCall call | call.getEnclosingFunction() = func |
217-
getTranslatedExpr(call).(TranslatedCallExpr).mayThrowException()
218+
exists(FunctionCall call, CppExceptionEdge exception | call.getEnclosingFunction() = func |
219+
getTranslatedExpr(call).(TranslatedCallExpr).mayThrowException(exception)
218220
)
219221
)
220222
or
@@ -228,7 +230,10 @@ class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction {
228230
)
229231
}
230232

231-
final override Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
233+
final override Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
234+
// only unwind for C++ exceptions since SEH exceptions are too verbose
235+
// and would generate unwind for all functions.
236+
exception instanceof CppExceptionEdge and
232237
result = this.getInstruction(UnwindTag()) and
233238
kind instanceof GotoEdge
234239
}

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

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import TranslatedElement
1010
private import TranslatedExpr
1111
private import TranslatedFunction
1212
private import TranslatedInitialization
13+
private import EdgeKind
1314

1415
TranslatedStmt getTranslatedStmt(Stmt stmt) { result.getAst() = stmt }
1516

@@ -151,7 +152,7 @@ class TranslatedMicrosoftTryExceptHandler extends TranslatedElement,
151152
// TODO: This is not really correct. The semantics of `EXCEPTION_CONTINUE_EXECUTION` is that
152153
// we should continue execution at the point where the exception occurred. But we don't have
153154
// any instruction to model this behavior.
154-
result = this.getExceptionSuccessorInstruction(any(GotoEdge edge))
155+
result = this.getExceptionSuccessorInstruction(any(GotoEdge edge), sehExceptionEdge())
155156
or
156157
kind instanceof FalseEdge and
157158
result = this.getInstruction(TryExceptGenerateZero())
@@ -171,7 +172,7 @@ class TranslatedMicrosoftTryExceptHandler extends TranslatedElement,
171172
tag = TryExceptCompareZeroBranch() and
172173
(
173174
kind instanceof TrueEdge and
174-
result = this.getExceptionSuccessorInstruction(any(GotoEdge edge))
175+
result = this.getExceptionSuccessorInstruction(any(GotoEdge edge), sehExceptionEdge())
175176
or
176177
kind instanceof FalseEdge and
177178
result = this.getInstruction(TryExceptGenerateOne())
@@ -226,10 +227,10 @@ class TranslatedMicrosoftTryExceptHandler extends TranslatedElement,
226227

227228
final override Function getFunction() { result = tryExcept.getEnclosingFunction() }
228229

229-
override Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
230+
override Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
230231
// A throw from within a `__except` block flows to the handler for the parent of
231232
// the `__try`.
232-
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind)
233+
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind, exception)
233234
}
234235
}
235236

@@ -282,10 +283,10 @@ class TranslatedMicrosoftTryFinallyHandler extends TranslatedElement,
282283
result = getTranslatedStmt(tryFinally.getFinally())
283284
}
284285

285-
override Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
286+
override Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
286287
// A throw from within a `__finally` block flows to the handler for the parent of
287288
// the `__try`.
288-
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind)
289+
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind, exception)
289290
}
290291
}
291292

@@ -734,14 +735,32 @@ class TranslatedTryStmt extends TranslatedStmt {
734735
// of the `try`, because the exception successor of the `try` itself is
735736
// the first catch clause.
736737
handler = this.getHandler(stmt.getNumberOfCatchClauses() - 1) and
737-
result = this.getParent().getExceptionSuccessorInstruction(kind)
738+
exists(ExceptionEdge exception |
739+
stmt instanceof MicrosoftTryStmt and exception instanceof SehExceptionEdge
740+
or
741+
stmt instanceof TryStmt and exception instanceof CppExceptionEdge
742+
|
743+
result = this.getParent().getExceptionSuccessorInstruction(kind, exception)
744+
)
738745
}
739746

740-
final override Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
741-
result = this.getHandler(0).getFirstInstruction(kind)
742-
or
743-
not exists(this.getHandler(_)) and
744-
result = this.getFinally().getFirstInstruction(kind)
747+
final override Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
748+
// Seh exceptions are only handled for Seh try statements and
749+
// C++ exceptions for C++ try statements.
750+
// I.e., we are assuming there isn't a mix and match between Seh and C++ exceptions.
751+
// They are either all Seh or all C++ within a single try block depending on the
752+
// try type (TryStmt vs MicrosoftTryStmt).
753+
(
754+
stmt instanceof TryStmt and exception instanceof CppExceptionEdge
755+
or
756+
stmt instanceof MicrosoftTryStmt and exception instanceof SehExceptionEdge
757+
) and
758+
(
759+
result = this.getHandler(0).getFirstInstruction(kind)
760+
or
761+
not exists(this.getHandler(_)) and
762+
result = this.getFinally().getFirstInstruction(kind)
763+
)
745764
}
746765

747766
private TranslatedElement getHandler(int index) { result = stmt.getTranslatedHandler(index) }
@@ -821,10 +840,10 @@ abstract class TranslatedHandler extends TranslatedStmt {
821840
child = this.getBlock() and result = this.getParent().getChildSuccessor(this, kind)
822841
}
823842

824-
override Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
843+
override Instruction getExceptionSuccessorInstruction(EdgeKind kind, ExceptionEdge exception) {
825844
// A throw from within a `catch` block flows to the handler for the parent of
826845
// the `try`.
827-
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind)
846+
result = this.getParent().getParent().getExceptionSuccessorInstruction(kind, exception)
828847
}
829848

830849
TranslatedStmt getBlock() { result = getTranslatedStmt(stmt.getBlock()) }

cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
1212

1313
/**
1414
* A function that is known to raise an exception.
15+
*
16+
* DEPRECATED: This was originally used to differentiate Seh and C++ exception use.
17+
* `AlwaysSehThrowingFunction` should be used instead for Seh exceptions that are
18+
* known to always throw and
19+
* `NonCppThrowingFunction` in `semmle.code.cpp.models.interfaces.NonThrowing`
20+
* should be used for C++ exceptions that are known to never throw.
1521
*/
16-
abstract class ThrowingFunction extends Function {
22+
abstract deprecated class ThrowingFunction extends Function {
1723
/**
1824
* Holds if this function may throw an exception during evaluation.
1925
* If `unconditional` is `true` the function always throws an exception.

0 commit comments

Comments
 (0)