Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Brodes/seh flow overhaul2 #17676

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
652b74d
Altering TExceptionEdge to include SEH and regular C++ exceptions, an…
bdrodes Oct 1, 2024
82cdd02
Overhaul of IR exception handling to account for SEH and C++ exceptions.
bdrodes Oct 2, 2024
a818346
Fixing bug in translated call edge generation.
bdrodes Oct 3, 2024
a2ede08
Finalizing a model for throwing and non-throwing functions.
bdrodes Oct 7, 2024
0ed72d1
Propagating the exception kind into getExceptionSuccessorInstruction
bdrodes Oct 7, 2024
e3d0015
updating getNextHandler logic.
bdrodes Oct 7, 2024
7a3ab8c
Review updates to EdgeKind.qll
bdrodes Oct 10, 2024
d0a54a3
Review updates.
bdrodes Oct 10, 2024
fcff607
PR review corrections.
bdrodes Oct 15, 2024
fdf5ac6
Formatting.
bdrodes Oct 23, 2024
a053519
Removing SEH load exceptions for var args and lambda translations
bdrodes Oct 23, 2024
e86c764
Updated comments and formatting.
bdrodes Oct 23, 2024
a40343e
Updating comments.
bdrodes Oct 23, 2024
cc4b280
Comments and changed char pred (moved up to abstract class)
bdrodes Oct 23, 2024
fb16db2
Formatting.
bdrodes Oct 23, 2024
781a9b7
Typo
bdrodes Oct 23, 2024
30810ce
Merge branch 'main' into brodes/seh_flow_overhaul2
bdrodes Oct 23, 2024
a498c87
adding change log
bdrodes Oct 24, 2024
aeeafbc
Merge branch 'brodes/seh_flow_overhaul2' of https://github.com/micros…
bdrodes Oct 24, 2024
61b1fef
Comments
bdrodes Oct 24, 2024
f85ba7a
Updating docs
bdrodes Oct 24, 2024
9dc776e
Type in comments.
bdrodes Oct 24, 2024
3f01e4d
changing isSEH to isSeh to match conventions.
bdrodes Oct 24, 2024
222c9f2
Style changes.
bdrodes Oct 24, 2024
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
21 changes: 17 additions & 4 deletions cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
*/

private import internal.EdgeKindInternal
private import codeql.util.Boolean

private newtype TEdgeKind =
TGotoEdge() or // Single successor (including fall-through)
TTrueEdge() or // 'true' edge of conditional branch
TFalseEdge() or // 'false' edge of conditional branch
TExceptionEdge() or // Thrown exception
TExceptionEdge(Boolean isSEH) or // Thrown exception, true for SEH exceptions, false otherwise

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
TDefaultEdge() or // 'default' label of switch
TCaseEdge(string minValue, string maxValue) {
// Case label of switch
Expand Down Expand Up @@ -54,7 +55,18 @@
* instruction's evaluation throws an exception.
*/
class ExceptionEdge extends EdgeKind, TExceptionEdge {
final override string toString() { result = "Exception" }
boolean isSEH; //true for Structured Exception Handling, false for C++ exceptions

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

ExceptionEdge() { this = TExceptionEdge(isSEH) }

/**
* Holds if the exception is a Structured Exception Handling (SEH) exception.
*/
final predicate isSEH() { isSEH = true }

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

final override string toString() {
if isSEH = true then result = "SEH Exception" else result = "C++ Exception"
}
}

/**
Expand Down Expand Up @@ -121,9 +133,10 @@
FalseEdge falseEdge() { result = TFalseEdge() }

/**
* Gets the single instance of the `ExceptionEdge` class.
* Gets the instance of the `ExceptionEdge` class.
* `isSEH` is true if the exception is an SEH exception, and false for a C++ edge.
*/
ExceptionEdge exceptionEdge() { result = TExceptionEdge() }
ExceptionEdge exceptionEdge(boolean isSEH) { result = TExceptionEdge(isSEH) }

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

/**
* Gets the single instance of the `DefaultEdge` class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
private import TranslatedExpr
private import TranslatedFunction
private import DefaultOptions as DefaultOptions
private import EdgeKind

/**
* Gets the `CallInstruction` from the `TranslatedCallExpr` for the specified expression.
Expand Down Expand Up @@ -83,14 +84,36 @@
any(UnreachedInstruction instr |
this.getEnclosingFunction().getFunction() = instr.getEnclosingFunction()
)
else (
not this.mustThrowException() and
result = this.getParent().getChildSuccessor(this, kind)
or
this.mayThrowException() and
kind instanceof ExceptionEdge and
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge))
)
else
exists(boolean isSEH | kind = exceptionEdge(isSEH) |

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
// Call throw behavior is resolved from most restricted to least restricted in order
// if there are conflicting throwing specificaitons for a function.
// Enumerating all scenarios to be explicit.
(
// If the call is known to never throw, regardless of other defined throwing behavior,
// do not generate any exception edges, only an ordinary successor
if this.(TranslatedCallExpr).neverRaiseException(isSEH)
then result = this.getParent().getChildSuccessor(this, any(GotoEdge edge))
else
// If the call is known to always throw, regardless of other defined throwing behavior,
// only generate an exception edge.
if this.(TranslatedCallExpr).alwaysRaiseException(isSEH)
then
result = this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge), isSEH)
else
if this.(TranslatedCallExpr).mayRaiseException(isSEH)
then (
// if the call is known to conditionally throw, generate both an exception edge and an
// ordinary successor
result =
this.getParent().getExceptionSuccessorInstruction(any(GotoEdge edge), isSEH)
or
result = this.getParent().getChildSuccessor(this, kind)
) else
// fallthrough case, no exceptions, just get the ordinary successor
result = this.getParent().getChildSuccessor(this, kind)
)
)
}

override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
Expand All @@ -117,14 +140,28 @@
final override Instruction getResult() { result = this.getInstruction(CallTag()) }

/**
* Holds if the evaluation of this call may throw an exception.
* The call target is known to always raise an exception.
* 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 mayThrowException();
abstract predicate alwaysRaiseException(boolean isSEH);

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

/**
* Holds if the evaluation of this call always throws an exception.
* The call target is known to conditionally raise an exception.
* 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 mayRaiseException(boolean isSEH);

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

/**
* The call target is known to never raise an exception.
* Note that `alwaysRaiseException`, `mayRaiseException`,
* and `neverRaiseException` may conflict (e.g., all hold for a given target).
* Conflicting results are resolved during IR generation.
*/
Fixed Show fixed Hide fixed
abstract predicate neverRaiseException(boolean isSEH);

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.

/**
* Gets the result type of the call.
Expand Down Expand Up @@ -320,6 +357,34 @@
final override int getNumberOfArguments() { result = expr.getNumberOfArguments() }

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

override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getTarget()
}

override predicate alwaysRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
exists(ThrowingFunction f | f = expr.getTarget() |
f.alwaysRaisesException() and f.isSEH() and isSEH = true
or
f.alwaysRaisesException() and f.isCxx() and isSEH = false
)
}

override predicate mayRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
exists(ThrowingFunction f | f = expr.getTarget() |
f.mayRaiseException() and f.isSEH() and isSEH = true
or
f.mayRaiseException() and f.isCxx() and isSEH = false
)
}

override predicate neverRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
exists(NonThrowingFunction f | f = expr.getTarget() |
f.isSEH() and isSEH = true
or
f.isCxx() and isSEH = false
)
}
}

/**
Expand All @@ -332,14 +397,42 @@
result = getTranslatedExpr(expr.getExpr().getFullyConverted())
}

final override predicate mayThrowException() {
// We assume that a call to a function pointer will not throw an exception.
override predicate alwaysRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
// We assume that a call to a function pointer will not throw a CXX exception.
// This is not sound in general, but this will greatly reduce the number of
// exceptional edges.
none()
// For SEH exceptions, use the defined ThrowingFunction behavior and
// if no throwing function is found, assume a conditional SEH exception
// see `mayRaiseException`
exists(ThrowingFunction f | f = expr.getTarget() |
f.alwaysRaisesException() and f.isSEH() and isSEH = true
)
}

override predicate mayRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
// We assume that a call to a function pointer will not throw a CXX exception.
// This is not sound in general, but this will greatly reduce the number of
// exceptional edges.
// For SEH exceptions, use the defined ThrowingFunction behavior.
// ASSUMPTION: if no ThrowingFunction is found for the given call, assume a conditional SEH exception
// on the call.
exists(ThrowingFunction f | f = expr.getTarget() |
f.mayRaiseException() and f.isSEH() and isSEH = true
)
or
not exists(ThrowingFunction f | f = expr.getTarget() and f.isSEH()) and
isSEH = true
}

final override predicate mustThrowException() { none() }
override predicate neverRaiseException(boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
// We assume that a call to a function pointer will not throw a CXX exception.
// This is not sound in general, but this will greatly reduce the number of
// exceptional edges.
// For SEH exceptions, use the defined ThrowingFunction behavior and
// if no throwing function is found, assume a conditional SEH exception
// see `mayRaiseException`
exists(NonThrowingFunction f | f = expr.getTarget() | f.isSEH() and isSEH = true)
}
}

/**
Expand All @@ -348,10 +441,6 @@
class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
override FunctionCall expr;

override Function getInstructionFunction(InstructionTag tag) {
tag = CallTargetTag() and result = expr.getTarget()
}

override Instruction getQualifierResult() {
this.hasQualifier() and
result = this.getQualifier().getResult()
Expand All @@ -361,14 +450,6 @@
exists(this.getQualifier()) and
not exists(MemberFunction func | expr.getTarget() = func and func.isStatic())
}

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

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

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1104,9 +1104,13 @@
* within this element. This will generally return first `catch` block of the
* nearest enclosing `try`, or the `Unwind` instruction for the function if
* there is no enclosing `try`. The successor edge kind is specified by `kind`.
*
* `isSEH` is true if the exception is a structured exception, false if otherwise.
* The boolean value can be extracted from `ExceptionEdge` from the exception
* edge triggering the call to this function.
*/
Instruction getExceptionSuccessorInstruction(EdgeKind kind) {
result = this.getParent().getExceptionSuccessorInstruction(kind)
Instruction getExceptionSuccessorInstruction(EdgeKind kind, boolean isSEH) {

Check warning

Code scanning / CodeQL

Acronyms should be PascalCase/camelCase. Warning

Acronyms in isSEH should be PascalCase/camelCase.
result = this.getParent().getExceptionSuccessorInstruction(kind, isSEH)
}

/**
Expand Down
Loading
Loading