Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag
private import semmle.code.cpp.ir.internal.CppType
private import semmle.code.cpp.models.interfaces.SideEffect
private import semmle.code.cpp.models.interfaces.Throwing
private import semmle.code.cpp.models.interfaces.NonThrowing
private import InstructionTag
private import SideEffects
private import TranslatedElement
Expand Down Expand Up @@ -84,12 +85,14 @@ abstract class TranslatedCall extends TranslatedExpr {
this.getEnclosingFunction().getFunction() = instr.getEnclosingFunction()
)
else (
not this.mustThrowException() and
not this.mustThrowException(_) and
result = this.getParent().getChildSuccessor(this, kind)
or
this.mayThrowException() and
kind instanceof CppExceptionEdge and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
exists(ExceptionEdge e | this.hasExceptionBehavior(e) |
this.mayThrowException(e) and
kind = e and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
)
)
}

Expand Down Expand Up @@ -118,13 +121,42 @@ abstract class TranslatedCall extends TranslatedExpr {

/**
* Holds if the evaluation of this call may throw an exception.
* The edge `e` determines what type of exception is being considered,
* `CppExceptionEdge` or `SehExceptionEdge`.
*/
abstract predicate mayThrowException();
abstract predicate mayThrowException(ExceptionEdge e);

/**
* Holds if the evaluation of this call always throws an exception.
* The edge `e` determines what type of exception is being considered,
* `CppExceptionEdge` or `SehExceptionEdge`.
*/
abstract predicate mustThrowException(ExceptionEdge e);

/**
* Holds when the call target is known to never raise an exception.
* The edge `e` determines what type of exception is being considered,
* `CppExceptionEdge` or `SehExceptionEdge`.
*
* Note that `alwaysRaiseException`, `mayRaiseException`,
* and `neverRaiseException` may conflict (e.g., all hold for a given target).
* Conflicting results are resolved during IR generation.
*/
abstract predicate mustThrowException();
abstract predicate neverThrowException(ExceptionEdge e);

/**
* Holds if the call has any exception behavior defined for exception edge type `e`,
* i.e., if the function is known to throw or never throw an exception.
* This handles cases where a function has no annotation to specify
* if a function throws or doesn't throw.
*/
final predicate hasExceptionBehavior(ExceptionEdge e) {
this.mayThrowException(e)
or
this.mustThrowException(e)
or
this.neverThrowException(e)
}

/**
* Gets the result type of the call.
Expand Down Expand Up @@ -320,6 +352,39 @@ abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedC
final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }

final override predicate isNoReturn() { any(Options opt).exits(expr.getTarget()) }

override predicate mustThrowException(ExceptionEdge e) {
// Functions that always raise exceptions only occur with Seh exceptions
// Use the `AlwaysSehThrowingFunction` instance unless the function is known to never throw
not this.neverThrowException(e) and
expr.getTarget() instanceof AlwaysSehThrowingFunction and
e instanceof SehExceptionEdge
}

override predicate mayThrowException(ExceptionEdge e) {
// by default, all functions may throw exceptions of any kind
// unless explicitly annotated to never throw
// Only consider a call to "may" throw an Seh exception
// if inside a MicrosoftTryStmt
not this.neverThrowException(e) and
(
this.mustThrowException(e)
or
// for now assuming all calls may throw for Seh only
e instanceof SehExceptionEdge and
exists(MicrosoftTryStmt trystmt | trystmt.getAChild*() = expr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether you want this at this point, as __finally and __except blocks are also children of a MicrosoftTryStmt. Hence, you'll also get SEH exception edges for calls in those blocks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had intentionally used the getAChild to include the finally and except since if there is an exception in those, I still want to unwind.

)
}

override predicate neverThrowException(ExceptionEdge e) {
// currently, only functions can only explicitly stipulate they
// never through a C++ exception
// NOTE: we cannot simply check if not exists may and must throw.
// since an annotation exists to override any other behavior
// i.e., `NonCppThrowingFunction`.
e instanceof CppExceptionEdge and
expr.getTarget() instanceof NonCppThrowingFunction
}
}

/**
Expand All @@ -332,14 +397,16 @@ class TranslatedExprCall extends TranslatedCallExpr {
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
}

final override predicate mayThrowException() {
final override predicate mayThrowException(ExceptionEdge e) { none() }

final override predicate mustThrowException(ExceptionEdge e) { none() }

final override predicate neverThrowException(ExceptionEdge e) {
// We assume that a call to a function pointer will not throw an exception.
// This is not sound in general, but this will greatly reduce the number of
// exceptional edges.
none()
any()
}

final override predicate mustThrowException() { none() }
}

/**
Expand All @@ -361,18 +428,6 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
exists(this.getQualifier()) and
not exists(MemberFunction func | expr.getTarget() = func and func.isStatic())
}

final override predicate mayThrowException() {
expr.getTarget().(ThrowingFunction).mayThrowException(_)
or
expr.getTarget() instanceof AlwaysSehThrowingFunction
}

final override predicate mustThrowException() {
expr.getTarget().(ThrowingFunction).mayThrowException(true)
or
expr.getTarget() instanceof AlwaysSehThrowingFunction
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2375,14 +2375,16 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index).getFullyConverted())
}

final override predicate mayThrowException() {
final override predicate mayThrowException(ExceptionEdge e) { none() }

final override predicate mustThrowException(ExceptionEdge e) { none() }

final override predicate neverThrowException(ExceptionEdge e) {
// We assume that a call to `new` or `new[]` will never throw. This is not
// sound in general, but this will greatly reduce the number of exceptional
// edges.
none()
any()
}

final override predicate mustThrowException() { none() }
}

TranslatedAllocatorCall getTranslatedAllocatorCall(NewOrNewArrayExpr newExpr) {
Expand Down Expand Up @@ -2448,14 +2450,16 @@ class TranslatedDeleteOrDeleteArrayExpr extends TranslatedNonConstantExpr, Trans
result = getTranslatedExpr(expr.getExprWithReuse().getFullyConverted())
}

final override predicate mayThrowException() {
final override predicate mayThrowException(ExceptionEdge e) { none() }

final override predicate mustThrowException(ExceptionEdge e) { none() }

final override predicate neverThrowException(ExceptionEdge e) {
// We assume that a call to `delete` or `delete[]` will never throw. This is not
// sound in general, but this will greatly reduce the number of exceptional
// edges.
none()
any()
}

final override predicate mustThrowException() { none() }
}

TranslatedDeleteOrDeleteArrayExpr getTranslatedDeleteOrDeleteArray(DeleteOrDeleteArrayExpr newExpr) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction {
exists(ThrowExpr throw | throw.getEnclosingFunction() = func)
or
exists(FunctionCall call | call.getEnclosingFunction() = func |
getTranslatedExpr(call).(TranslatedCallExpr).mayThrowException()
getTranslatedExpr(call).(TranslatedCallExpr).mayThrowException(_)
)
)
or
Expand Down
8 changes: 7 additions & 1 deletion cpp/ql/lib/semmle/code/cpp/models/interfaces/Throwing.qll
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs

/**
* A function that is known to raise an exception.
*
* DEPRECATED: This was originally used to differentiate Seh and C++ exception use.
* `AlwaysSehThrowingFunction` should be used instead for Seh exceptions that are
* known to always throw and
* `NonCppThrowingFunction` in `semmle.code.cpp.models.interfaces.NonThrowing`
* should be used for C++ exceptions that are known to never throw.
*/
abstract class ThrowingFunction extends Function {
abstract deprecated class ThrowingFunction extends Function {
/**
* Holds if this function may throw an exception during evaluation.
* If `unconditional` is `true` the function always throws an exception.
Expand Down
Loading
Loading