diff --git a/cpp/ql/lib/change-notes/2023-12-22-unique-function.md b/cpp/ql/lib/change-notes/2023-12-22-unique-function.md new file mode 100644 index 000000000000..bd5d84132abf --- /dev/null +++ b/cpp/ql/lib/change-notes/2023-12-22-unique-function.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* Under certain circumstances a function declaration that is not also a definition could be associated with a `Function` that did not have the definition as a `FunctionDeclarationEntry`. This is now fixed when only one definition exists, and a unique `Function` will exist that has both the declaration and the definition as a `FunctionDeclarationEntry`. \ No newline at end of file diff --git a/cpp/ql/lib/semmle/code/cpp/Element.qll b/cpp/ql/lib/semmle/code/cpp/Element.qll index 79df774d80f1..98b5da60c1c5 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 db6f1c487e74..92134dca31d9 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 dfb5782238bc..84e96fa9c468 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 000000000000..95cd28068568 --- /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 c11e1457dae7..3727a1aaa00c 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) ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy