From f7da6f56f3fbab1ca09527f939df6fc2227b034e Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 15 Nov 2022 13:21:53 +0100 Subject: [PATCH] C++: Ensure that only one `Function` exists for every function --- cpp/ql/lib/semmle/code/cpp/Element.qll | 4 ++ cpp/ql/lib/semmle/code/cpp/Function.qll | 3 + .../controlflow/internal/ConstantExprs.qll | 6 +- .../code/cpp/internal/ResolveFunction.qll | 57 +++++++++++++++++++ .../cpp/internal/ResolveGlobalVariable.qll | 2 +- 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll diff --git a/cpp/ql/lib/semmle/code/cpp/Element.qll b/cpp/ql/lib/semmle/code/cpp/Element.qll index 79df774d80f..98b5da60c1c 100644 --- a/cpp/ql/lib/semmle/code/cpp/Element.qll +++ b/cpp/ql/lib/semmle/code/cpp/Element.qll @@ -7,6 +7,7 @@ import semmle.code.cpp.Location private import semmle.code.cpp.Enclosing private import semmle.code.cpp.internal.ResolveClass private import semmle.code.cpp.internal.ResolveGlobalVariable +private import semmle.code.cpp.internal.ResolveFunction /** * Get the `Element` that represents this `@element`. @@ -30,11 +31,14 @@ pragma[inline] @element unresolveElement(Element e) { not result instanceof @usertype and not result instanceof @variable and + not result instanceof @function and result = e or e = resolveClass(result) or e = resolveGlobalVariable(result) + or + e = resolveFunction(result) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/Function.qll b/cpp/ql/lib/semmle/code/cpp/Function.qll index db6f1c487e7..92134dca31d 100644 --- a/cpp/ql/lib/semmle/code/cpp/Function.qll +++ b/cpp/ql/lib/semmle/code/cpp/Function.qll @@ -9,6 +9,7 @@ import semmle.code.cpp.exprs.Call import semmle.code.cpp.metrics.MetricFunction import semmle.code.cpp.Linkage private import semmle.code.cpp.internal.ResolveClass +private import semmle.code.cpp.internal.ResolveFunction /** * A C/C++ function [N4140 8.3.5]. Both member functions and non-member @@ -25,6 +26,8 @@ private import semmle.code.cpp.internal.ResolveClass * in more detail in `Declaration.qll`. */ class Function extends Declaration, ControlFlowNode, AccessHolder, @function { + Function() { isFunction(underlyingElement(this)) } + override string getName() { functions(underlyingElement(this), result, _) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll index dfb5782238b..84e96fa9c46 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/ConstantExprs.qll @@ -110,8 +110,8 @@ private predicate loopConditionAlwaysUponEntry(ControlFlowNode loop, Expr condit * should be in this relation. */ pragma[noinline] -private predicate isFunction(Element el) { - el instanceof Function +private predicate isFunction(@element el) { + el instanceof @function or el.(Expr).getParent() = el } @@ -122,7 +122,7 @@ private predicate isFunction(Element el) { */ pragma[noopt] private predicate callHasNoTarget(@funbindexpr fc) { - exists(Function f | + exists(@function f | funbind(fc, f) and not isFunction(f) ) diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll new file mode 100644 index 00000000000..95cd2806856 --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveFunction.qll @@ -0,0 +1,57 @@ +private predicate hasDefinition(@function f) { + exists(@fun_decl fd | fun_decls(fd, f, _, _, _) | fun_def(fd)) +} + +private predicate onlyOneCompleteFunctionExistsWithMangledName(@mangledname name) { + strictcount(@function f | hasDefinition(f) and mangled_name(f, name)) = 1 +} + +/** Holds if `f` is a unique function with a definition named `name`. */ +private predicate isFunctionWithMangledNameAndWithDefinition(@mangledname name, @function f) { + hasDefinition(f) and + mangled_name(f, name) and + onlyOneCompleteFunctionExistsWithMangledName(name) +} + +/** Holds if `f` is a function without a definition named `name`. */ +private predicate isFunctionWithMangledNameAndWithoutDefinition(@mangledname name, @function f) { + not hasDefinition(f) and + mangled_name(f, name) +} + +/** + * Holds if `incomplete` is a function without a definition, and there exists + * a unique function `complete` with the same name that does have a definition. + */ +private predicate hasTwinWithDefinition(@function incomplete, @function complete) { + not function_instantiation(incomplete, complete) and + ( + not compgenerated(incomplete) or + not compgenerated(complete) + ) and + exists(@mangledname name | + isFunctionWithMangledNameAndWithoutDefinition(name, incomplete) and + isFunctionWithMangledNameAndWithDefinition(name, complete) + ) +} + +import Cached + +cached +private module Cached { + /** + * If `f` is a function without a definition, and there exists a unique + * function with the same name that does have a definition, then the + * result is that unique function. Otherwise, the result is `f`. + */ + cached + @function resolveFunction(@function f) { + hasTwinWithDefinition(f, result) + or + not hasTwinWithDefinition(f, _) and + result = f + } + + cached + predicate isFunction(@function f) { f = resolveFunction(_) } +} diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll index c11e1457dae..3727a1aaa00 100644 --- a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll +++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll @@ -24,8 +24,8 @@ private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, * a unique global variable `complete` with the same name that does have a definition. */ private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) { + not variable_instantiation(incomplete, complete) and exists(@mangledname name | - not variable_instantiation(incomplete, complete) and isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and isGlobalWithMangledNameAndWithDefinition(name, complete) )