diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll index e5d65e3b549d..a85f3c2cb4c2 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -19,6 +19,11 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { /** Gets the enclosing callable of this element, if any. */ Callable getEnclosingCallable() { none() } + /** Gets the assembly that this element was compiled into. */ + Assembly getAssembly() { + result = this.getEnclosingCallable().getDeclaringType().getALocation() + } + /** * Gets a control flow node for this element. That is, a node in the * control flow graph that corresponds to this element. diff --git a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll index 56edfd9c4f2b..631b89633c3d 100644 --- a/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/src/semmle/code/csharp/controlflow/internal/Completion.qll @@ -185,14 +185,28 @@ private class Overflowable extends UnaryOperation { } } +private class CoreLib extends Assembly { + CoreLib() { this = any(SystemExceptionClass c).getALocation() } +} + +/** + * Holds if assembly `a` was definitely compiled with core library `core`. + */ +pragma[noinline] +private predicate assemblyCompiledWithCoreLib(Assembly a, CoreLib core) { + a.getAnAttribute().getType().getBaseClass*().(SystemAttributeClass).getALocation() = core +} + /** A control flow element that is inside a `try` block. */ private class TriedControlFlowElement extends ControlFlowElement { - TriedControlFlowElement() { this = any(TryStmt try).getATriedElement() } + TryStmt try; + + TriedControlFlowElement() { this = try.getATriedElement() } /** * Gets an exception class that is potentially thrown by this element, if any. */ - Class getAThrownException() { + private Class getAThrownException0() { this instanceof Overflowable and result instanceof SystemOverflowExceptionClass or @@ -249,6 +263,40 @@ private class TriedControlFlowElement extends ControlFlowElement { this instanceof StringLiteral and result instanceof SystemOutOfMemoryExceptionClass } + + private CoreLib getCoreLibFromACatchClause() { + exists(SpecificCatchClause scc | scc = try.getACatchClause() | + result = scc.getCaughtExceptionType().getBaseClass*().(SystemExceptionClass).getALocation() + ) + } + + private CoreLib getCoreLib() { + result = this.getCoreLibFromACatchClause() + or + not exists(this.getCoreLibFromACatchClause()) and + assemblyCompiledWithCoreLib(this.getAssembly(), result) + } + + pragma[noinline] + private Class getAThrownExceptionFromPlausibleCoreLib(string name) { + result = this.getAThrownException0() and + name = result.getQualifiedName() and + ( + not exists(this.getCoreLib()) + or + this.getCoreLib() = result.getALocation() + ) + } + + Class getAThrownException() { + exists(string name | result = this.getAThrownExceptionFromPlausibleCoreLib(name) | + result = min(Class c | + c = this.getAThrownExceptionFromPlausibleCoreLib(name) + | + c order by c.getLocation().(Assembly).getFullName() + ) + ) + } } pragma[noinline] diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll index 24dd7f971a9d..76fabd949cdb 100644 --- a/csharp/ql/src/semmle/code/csharp/frameworks/System.qll +++ b/csharp/ql/src/semmle/code/csharp/frameworks/System.qll @@ -56,6 +56,11 @@ class SystemArrayClass extends SystemClass { SystemArrayClass() { this.hasName("Array") } } +/** `System.Attribute` class. */ +class SystemAttributeClass extends SystemClass { + SystemAttributeClass() { this.hasName("Attribute") } +} + /** The `System.Boolean` structure. */ class SystemBooleanStruct extends BoolType { /** Gets the `Parse(string)` method. */