diff --git a/repo-tests/codeql.txt b/repo-tests/codeql.txt new file mode 100644 index 00000000000..869ad93732c --- /dev/null +++ b/repo-tests/codeql.txt @@ -0,0 +1 @@ +a2371370ff8260e789342e0ac759bc67ed401702 diff --git a/repo-tests/codeql/cpp/ql/examples/qlpack.yml b/repo-tests/codeql/cpp/ql/examples/qlpack.yml new file mode 100644 index 00000000000..e7b5d02295d --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/cpp-examples +version: 0.0.2 +dependencies: + codeql/cpp-all: "*" diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/addressof.ql b/repo-tests/codeql/cpp/ql/examples/snippets/addressof.ql new file mode 100644 index 00000000000..d6fdd4ad346 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/addressof.ql @@ -0,0 +1,16 @@ +/** + * @id cpp/examples/addressof + * @name Address of reference variable + * @description Finds address-of expressions (`&`) that take the address + * of a reference variable + * @tags addressof + * reference + */ + +import cpp + +from AddressOfExpr addr, VariableAccess access +where + access = addr.getOperand() and + access.getTarget().getType() instanceof ReferenceType +select addr diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/arrayaccess.ql b/repo-tests/codeql/cpp/ql/examples/snippets/arrayaccess.ql new file mode 100644 index 00000000000..6a7f600571d --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/arrayaccess.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/arrayaccess + * @name Array access + * @description Finds array access expressions with an index expression + * consisting of a postfix increment (`++`) expression. + * @tags array + * access + * index + * postfix + * increment + */ + +import cpp + +from ArrayExpr a +where a.getArrayOffset() instanceof PostfixIncrExpr +select a diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/castexpr.ql b/repo-tests/codeql/cpp/ql/examples/snippets/castexpr.ql new file mode 100644 index 00000000000..46fe3e12d04 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/castexpr.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/castexpr + * @name Cast expressions + * @description Finds casts from a floating point type to an integer type + * @tags cast + * integer + * float + * type + */ + +import cpp + +from Cast c +where + c.getExpr().getType() instanceof FloatingPointType and + c.getType() instanceof IntegralType +select c diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/catch_exception.ql b/repo-tests/codeql/cpp/ql/examples/snippets/catch_exception.ql new file mode 100644 index 00000000000..3cc29127218 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/catch_exception.ql @@ -0,0 +1,15 @@ +/** + * @id cpp/examples/catch-exception + * @name Catch exception + * @description Finds places where we catch exceptions of type `parse_error` + * @tags catch + * try + * exception + */ + +import cpp + +from CatchBlock catch +// `stripType` converts `const parse_error &` to `parse_error`. +where catch.getParameter().getType().stripType().hasName("parse_error") +select catch diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/constructor_call.ql b/repo-tests/codeql/cpp/ql/examples/snippets/constructor_call.ql new file mode 100644 index 00000000000..220160a7eec --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/constructor_call.ql @@ -0,0 +1,16 @@ +/** + * @id cpp/examples/constructor-call + * @name Call to constructor + * @description Finds places where we call `new MyClass(...)` + * @tags call + * constructor + * new + */ + +import cpp + +from NewExpr new, Constructor c +where + c = new.getInitializer().(ConstructorCall).getTarget() and + c.getName() = "MyClass" +select new diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/derives_from_class.ql b/repo-tests/codeql/cpp/ql/examples/snippets/derives_from_class.ql new file mode 100644 index 00000000000..8cb60b26df3 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/derives_from_class.ql @@ -0,0 +1,20 @@ +/** + * @id cpp/examples/derives-from-class + * @name Class derives from + * @description Finds classes that derive from `std::exception` + * @tags base + * class + * derive + * inherit + * override + * subtype + * supertype + */ + +import cpp + +from Class type +where + type.getABaseClass+().hasName("exception") and + type.getNamespace().getName() = "std" +select type diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/emptyblock.ql b/repo-tests/codeql/cpp/ql/examples/snippets/emptyblock.ql new file mode 100644 index 00000000000..46ee35a6864 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/emptyblock.ql @@ -0,0 +1,14 @@ +/** + * @id cpp/examples/emptyblock + * @name Empty blocks + * @description Finds empty block statements + * @tags empty + * block + * statement + */ + +import cpp + +from BlockStmt blk +where blk.getNumStmt() = 0 +select blk diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/emptythen.ql b/repo-tests/codeql/cpp/ql/examples/snippets/emptythen.ql new file mode 100644 index 00000000000..7e31ac7e4e1 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/emptythen.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/emptythen + * @name If statements with empty then branch + * @description Finds `if` statements where the `then` branch is + * an empty block statement + * @tags if + * then + * empty + * conditional + * branch + */ + +import cpp + +from IfStmt i +where i.getThen().(BlockStmt).getNumStmt() = 0 +select i diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/eq_true.ql b/repo-tests/codeql/cpp/ql/examples/snippets/eq_true.ql new file mode 100644 index 00000000000..661f27d650f --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/eq_true.ql @@ -0,0 +1,18 @@ +/** + * @id cpp/examples/eq-true + * @name Equality test on boolean + * @description Finds tests like `==true`, `!=true` + * @tags equal + * comparison + * test + * boolean + */ + +import cpp + +from EqualityOperation eq, Expr trueExpr +where + trueExpr = eq.getAnOperand() and + trueExpr.getType() instanceof BoolType and + trueExpr.getValue().toInt() = 1 +select eq diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/field_access.ql b/repo-tests/codeql/cpp/ql/examples/snippets/field_access.ql new file mode 100644 index 00000000000..4947cfd832f --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/field_access.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/field-access + * @name Access of field + * @description Finds reads of `aDate` (defined on class `Order`) + * @tags access + * field + * read + */ + +import cpp + +from Field f, FieldAccess access +where + f.hasName("aDate") and + f.getDeclaringType().hasName("Order") and + f = access.getTarget() +select access diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/function_call.ql b/repo-tests/codeql/cpp/ql/examples/snippets/function_call.ql new file mode 100644 index 00000000000..81c5d6246ae --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/function_call.ql @@ -0,0 +1,18 @@ +/** + * @id cpp/examples/function-call + * @name Call to function + * @description Finds calls to `std::map<...>::find()` + * @tags call + * function + * method + */ + +import cpp + +from FunctionCall call, Function fcn +where + call.getTarget() = fcn and + fcn.getDeclaringType().getSimpleName() = "map" and + fcn.getDeclaringType().getNamespace().getName() = "std" and + fcn.hasName("find") +select call diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/integer_literal.ql b/repo-tests/codeql/cpp/ql/examples/snippets/integer_literal.ql new file mode 100644 index 00000000000..008902db1ee --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/integer_literal.ql @@ -0,0 +1,15 @@ +/** + * @id cpp/examples/integer-literal + * @name Integer literal + * @description Finds places where we use the integer literal `2` + * @tags integer + * literal + */ + +import cpp + +from Literal literal +where + literal.getType() instanceof IntType and + literal.getValue().toInt() = 2 +select literal diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/mutualrecursion.ql b/repo-tests/codeql/cpp/ql/examples/snippets/mutualrecursion.ql new file mode 100644 index 00000000000..744c4de5832 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/mutualrecursion.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/mutualrecursion + * @name Mutual recursion + * @description Finds pairs of functions that call each other + * @tags function + * method + * recursion + */ + +import cpp + +from Function m, Function n +where + exists(FunctionCall c | c.getEnclosingFunction() = m and c.getTarget() = n) and + exists(FunctionCall c | c.getEnclosingFunction() = n and c.getTarget() = m) and + m != n +select m, n diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/override_method.ql b/repo-tests/codeql/cpp/ql/examples/snippets/override_method.ql new file mode 100644 index 00000000000..4917437b666 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/override_method.ql @@ -0,0 +1,18 @@ +/** + * @id cpp/examples/override-method + * @name Override of method + * @description Finds methods that override `std::exception::what()` + * @tags function + * method + * override + */ + +import cpp + +from MemberFunction override, MemberFunction base +where + base.getName() = "what" and + base.getDeclaringType().getName() = "exception" and + base.getDeclaringType().getNamespace().getName() = "std" and + override.overrides+(base) +select override diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/returnstatement.ql b/repo-tests/codeql/cpp/ql/examples/snippets/returnstatement.ql new file mode 100644 index 00000000000..bf52adc674a --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/returnstatement.ql @@ -0,0 +1,14 @@ +/** + * @id cpp/examples/returnstatement + * @name Return statements + * @description Finds return statements that return `0` + * @tags return + * statement + * literal + */ + +import cpp + +from ReturnStmt r +where r.getExpr().(Literal).getValue().toInt() = 0 +select r diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/singletonblock.ql b/repo-tests/codeql/cpp/ql/examples/snippets/singletonblock.ql new file mode 100644 index 00000000000..265bc85927c --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/singletonblock.ql @@ -0,0 +1,13 @@ +/** + * @id cpp/examples/singletonblock + * @name Singleton blocks + * @description Finds block statements containing a single statement + * @tags block + * statement + */ + +import cpp + +from BlockStmt b +where b.getNumStmt() = 1 +select b diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/switchcase.ql b/repo-tests/codeql/cpp/ql/examples/snippets/switchcase.ql new file mode 100644 index 00000000000..576d7386c80 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/switchcase.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/examples/switchcase + * @name Switch statement case missing + * @description Finds switch statements with a missing enum constant case + * and no default case + * @tags switch + * case + * enum + */ + +import cpp + +from EnumSwitch es, EnumConstant ec +where + ec = es.getAMissingCase() and + not es.hasDefaultCase() +select es, ec diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/ternaryconditional.ql b/repo-tests/codeql/cpp/ql/examples/snippets/ternaryconditional.ql new file mode 100644 index 00000000000..74b3bc7cdd5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/ternaryconditional.ql @@ -0,0 +1,15 @@ +/** + * @id cpp/examples/ternaryconditional + * @name Conditional expressions + * @description Finds conditional expressions of the form `... ? ... : ...` + * where the types of the resulting expressions differ + * @tags conditional + * ternary + * type + */ + +import cpp + +from ConditionalExpr e +where e.getThen().getType() != e.getElse().getType() +select e diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/throw_exception.ql b/repo-tests/codeql/cpp/ql/examples/snippets/throw_exception.ql new file mode 100644 index 00000000000..c109c87ae95 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/throw_exception.ql @@ -0,0 +1,15 @@ +/** + * @id cpp/examples/throw-exception + * @name Throw exception of type + * @description Finds places where we throw `parse_error` or one of its sub-types + * @tags base + * class + * throw + * exception + */ + +import cpp + +from ThrowExpr throw +where throw.getType().(Class).getABaseClass*().getName() = "parse_error" +select throw diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/todocomment.ql b/repo-tests/codeql/cpp/ql/examples/snippets/todocomment.ql new file mode 100644 index 00000000000..d11782bada0 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/todocomment.ql @@ -0,0 +1,14 @@ +/** + * @id cpp/examples/todocomment + * @name TODO comments + * @description Finds comments containing the word "TODO" + * @tags comment + * matches + * TODO + */ + +import cpp + +from Comment c +where c.getContents().matches("%TODO%") +select c diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/toomanyparams.ql b/repo-tests/codeql/cpp/ql/examples/snippets/toomanyparams.ql new file mode 100644 index 00000000000..318550fe463 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/toomanyparams.ql @@ -0,0 +1,15 @@ +/** + * @id cpp/examples/toomanyparams + * @name Functions with many parameters + * @description Finds functions or methods with more than 10 parameters + * @tags function + * method + * parameter + * argument + */ + +import cpp + +from Function fcn +where fcn.getNumberOfParameters() > 10 +select fcn diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/unusedlocalvar.ql b/repo-tests/codeql/cpp/ql/examples/snippets/unusedlocalvar.ql new file mode 100644 index 00000000000..a6fdbb8ec42 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/unusedlocalvar.ql @@ -0,0 +1,16 @@ +/** + * @id cpp/examples/unusedlocalvar + * @name Unused local variable + * @description Finds local variables that are not accessed + * @tags variable + * local + * access + */ + +import cpp + +from LocalScopeVariable v +where + not v instanceof Parameter and + not exists(v.getAnAccess()) +select v diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/unusedmethod.ql b/repo-tests/codeql/cpp/ql/examples/snippets/unusedmethod.ql new file mode 100644 index 00000000000..7544b24e347 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/unusedmethod.ql @@ -0,0 +1,18 @@ +/** + * @id cpp/examples/unusedmethod + * @name Unused private method + * @description Finds private non-virtual methods that are not accessed + * @tags method + * access + * private + * virtual + */ + +import cpp + +from MemberFunction fcn +where + fcn.isPrivate() and + not fcn.isVirtual() and + not exists(FunctionCall call | fcn = call.getTarget()) +select fcn.getDefinition() diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/unusedparam.ql b/repo-tests/codeql/cpp/ql/examples/snippets/unusedparam.ql new file mode 100644 index 00000000000..07a3fc46ffe --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/unusedparam.ql @@ -0,0 +1,13 @@ +/** + * @id cpp/examples/unusedparam + * @name Unused parameter + * @description Finds parameters that are not accessed + * @tags parameter + * access + */ + +import cpp + +from Parameter p +where p.isNamed() and not exists(p.getAnAccess()) +select p diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/voidreturntype.ql b/repo-tests/codeql/cpp/ql/examples/snippets/voidreturntype.ql new file mode 100644 index 00000000000..dee01207af7 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/voidreturntype.ql @@ -0,0 +1,21 @@ +/** + * @id cpp/examples/voidreturntype + * @name Const method without return type + * @description Finds const methods whose return type is `void` + * @tags const + * function + * method + * modifier + * specifier + * return + * type + * void + */ + +import cpp + +from MemberFunction m +where + m.hasSpecifier("const") and + m.getType() instanceof VoidType +select m diff --git a/repo-tests/codeql/cpp/ql/examples/snippets/volatilevariable.ql b/repo-tests/codeql/cpp/ql/examples/snippets/volatilevariable.ql new file mode 100644 index 00000000000..8d97d1619da --- /dev/null +++ b/repo-tests/codeql/cpp/ql/examples/snippets/volatilevariable.ql @@ -0,0 +1,13 @@ +/** + * @id cpp/examples/volatilevariable + * @name Variable declared volatile + * @description Finds variables with a `volatile` modifier + * @tags variable + * volatile + */ + +import cpp + +from Variable f +where f.isVolatile() +select f diff --git a/repo-tests/codeql/cpp/ql/lib/DefaultOptions.qll b/repo-tests/codeql/cpp/ql/lib/DefaultOptions.qll new file mode 100644 index 00000000000..9ed99054098 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/DefaultOptions.qll @@ -0,0 +1,112 @@ +/** + * Provides default predicates that specify information about + * the behavior of the program being analyzed. + * + * This can be overridden for particular code bases in `Options.qll`. + */ + +import cpp +import semmle.code.cpp.controlflow.Dataflow +import semmle.code.cpp.commons.Synchronization +private import semmle.code.cpp.controlflow.internal.ConstantExprs +private import Options as CustomOptions + +/** + * Default predicates that specify information about the behavior of + * the program being analyzed. + */ +class Options extends string { + Options() { this = "Options" } + + /** + * Holds if we wish to override the "may return NULL" inference for this + * call. If this holds, then rather than trying to infer whether this + * call might return NULL, we will instead test whether `returnsNull` + * holds for it. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + predicate overrideReturnsNull(Call call) { + // Used in CVS: + call.(FunctionCall).getTarget().hasGlobalName("Xstrdup") + or + CustomOptions::overrideReturnsNull(call) // old Options.qll + } + + /** + * Holds if this call may return NULL. This is only used if + * `overrideReturnsNull` holds for the call. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + predicate returnsNull(Call call) { + // Used in CVS: + call.(FunctionCall).getTarget().hasGlobalName("Xstrdup") and + nullValue(call.getArgument(0)) + or + CustomOptions::returnsNull(call) // old Options.qll + } + + /** + * Holds if a call to this function will never return. + * + * By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`, + * `longjmp`, `__builtin_unreachable` and any function with a + * `noreturn` attribute. + */ + predicate exits(Function f) { + f.getAnAttribute().hasName("noreturn") + or + f.hasGlobalOrStdName([ + "exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable" + ]) + or + CustomOptions::exits(f) // old Options.qll + } + + /** + * Holds if evaluating expression `e` will never return, or can be assumed + * to never return. For example: + * ``` + * __assume(0); + * ``` + * (note that in this case if the hint is wrong and the expression is reached at + * runtime, the program's behaviour is undefined) + */ + predicate exprExits(Expr e) { + e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or + CustomOptions::exprExits(e) // old Options.qll + } + + /** + * Holds if function `f` should always have its return value checked. + * + * By default holds only for `fgets`. + */ + predicate alwaysCheckReturnValue(Function f) { + f.hasGlobalOrStdName("fgets") or + CustomOptions::alwaysCheckReturnValue(f) // old Options.qll + } + + /** + * Holds if it is reasonable to ignore the return value of function + * call `fc`. + * + * By default holds for calls to `select` where the first argument is + * `0` (commonly used as a way of sleeping), and any call inside a + * macro expansion. + */ + predicate okToIgnoreReturnValue(FunctionCall fc) { + fc.isInMacroExpansion() + or + // common way of sleeping using select: + fc.getTarget().hasGlobalName("select") and + fc.getArgument(0).getValue() = "0" + or + CustomOptions::okToIgnoreReturnValue(fc) // old Options.qll + } +} + +Options getOptions() { any() } diff --git a/repo-tests/codeql/cpp/ql/lib/Options.qll b/repo-tests/codeql/cpp/ql/lib/Options.qll new file mode 100644 index 00000000000..3c7e320dff6 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/Options.qll @@ -0,0 +1,154 @@ +/** + * Provides custom predicates that specify information about + * the behavior of the program being analyzed. + * + * By default they fall back to the reasonable defaults provided in + * `DefaultOptions.qll`, but by modifying this file, you can customize + * the standard analyses to give better results for your project. + */ + +import cpp + +/** + * Customizable predicates that specify information about + * the behavior of the program being analyzed. + */ +class CustomOptions extends Options { + /** + * Holds if we wish to override the "may return NULL" inference for this + * call. If this holds, then rather than trying to infer whether this + * call might return NULL, we will instead test whether `returnsNull` + * holds for it. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + override predicate overrideReturnsNull(Call call) { Options.super.overrideReturnsNull(call) } + + /** + * Holds if this call may return NULL. This is only used if + * `overrideReturnsNull` holds for the call. + * + * By default, this handles the `Xstrdup` function used by the CVS + * project. + */ + override predicate returnsNull(Call call) { Options.super.returnsNull(call) } + + /** + * Holds if a call to this function will never return. + * + * By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`, + * `longjmp`, `error`, `__builtin_unreachable` and any function with a + * `noreturn` attribute. + */ + override predicate exits(Function f) { Options.super.exits(f) } + + /** + * Holds if evaluating expression `e` will never return, or can be assumed + * to never return. For example: + * ``` + * __assume(0); + * ``` + * (note that in this case if the hint is wrong and the expression is reached at + * runtime, the program's behaviour is undefined) + */ + override predicate exprExits(Expr e) { Options.super.exprExits(e) } + + /** + * Holds if function `f` should always have its return value checked. + * + * By default holds only for `fgets`. + */ + override predicate alwaysCheckReturnValue(Function f) { Options.super.alwaysCheckReturnValue(f) } + + /** + * Holds if it is reasonable to ignore the return value of function + * call `fc`. + * + * By default holds for calls to `select` where the first argument is + * `0` (commonly used as a way of sleeping), and any call inside a + * macro expansion. + */ + override predicate okToIgnoreReturnValue(FunctionCall fc) { + Options.super.okToIgnoreReturnValue(fc) + } +} + +/** + * A type that acts as a mutex. + */ +class CustomMutexType extends MutexType { + CustomMutexType() { none() } + + /** + * Holds if `fc` is a call that always locks mutex `arg` + * of this type. + */ + override predicate mustlockAccess(FunctionCall fc, Expr arg) { none() } + + /** + * Holds if `fc` is a call that tries to lock mutex `arg` + * of this type, but may return without success. + */ + override predicate trylockAccess(FunctionCall fc, Expr arg) { none() } + + /** + * Holds if `fc` is a call that unlocks mutex `arg` + * of this type. + */ + override predicate unlockAccess(FunctionCall fc, Expr arg) { none() } +} + +/** + * DEPRECATED: customize `CustomOptions.overrideReturnsNull` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate overrideReturnsNull(Call call) { none() } + +/** + * DEPRECATED: customize `CustomOptions.returnsNull` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate returnsNull(Call call) { none() } + +/** + * DEPRECATED: customize `CustomOptions.exits` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate exits(Function f) { none() } + +/** + * DEPRECATED: customize `CustomOptions.exprExits` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate exprExits(Expr e) { none() } + +/** + * DEPRECATED: customize `CustomOptions.alwaysCheckReturnValue` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate alwaysCheckReturnValue(Function f) { none() } + +/** + * DEPRECATED: customize `CustomOptions.okToIgnoreReturnValue` instead. + * + * This predicate is required to support backwards compatibility for + * older `Options.qll` files. It should not be removed or modified by + * end users. + */ +predicate okToIgnoreReturnValue(FunctionCall fc) { none() } diff --git a/repo-tests/codeql/cpp/ql/lib/cpp.qll b/repo-tests/codeql/cpp/ql/lib/cpp.qll new file mode 100644 index 00000000000..a989c9a6c9d --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/cpp.qll @@ -0,0 +1,74 @@ +/** + * Provides classes and predicates for working with C/C++ code. + * + * Where the documentation refers to the standards, it gives + * references to the freely-available drafts. + * + * For C++11, these references are of the form [N3337 5.3.2/1], and the + * corresponding draft of the standard can be downloaded from + * https://github.com/cplusplus/draft/raw/master/papers/n3337.pdf + * + * For C++14, these references are of the form [N4140 5.3.2/1], and the + * corresponding draft of the standard can be downloaded from + * https://github.com/cplusplus/draft/raw/master/papers/n4140.pdf + */ + +import semmle.code.cpp.File +import semmle.code.cpp.Linkage +import semmle.code.cpp.Location +import semmle.code.cpp.Compilation +import semmle.code.cpp.Element +import semmle.code.cpp.Namespace +import semmle.code.cpp.Specifier +import semmle.code.cpp.Declaration +import semmle.code.cpp.Include +import semmle.code.cpp.Macro +import semmle.code.cpp.Type +import semmle.code.cpp.TypedefType +import semmle.code.cpp.Class +import semmle.code.cpp.Struct +import semmle.code.cpp.Union +import semmle.code.cpp.Enum +import semmle.code.cpp.Member +import semmle.code.cpp.Field +import semmle.code.cpp.Function +import semmle.code.cpp.MemberFunction +import semmle.code.cpp.Parameter +import semmle.code.cpp.Variable +import semmle.code.cpp.Initializer +import semmle.code.cpp.FriendDecl +import semmle.code.cpp.exprs.Expr +import semmle.code.cpp.exprs.ArithmeticOperation +import semmle.code.cpp.exprs.BitwiseOperation +import semmle.code.cpp.exprs.LogicalOperation +import semmle.code.cpp.exprs.ComparisonOperation +import semmle.code.cpp.exprs.Assignment +import semmle.code.cpp.exprs.Cast +import semmle.code.cpp.exprs.Access +import semmle.code.cpp.exprs.Call +import semmle.code.cpp.exprs.Lambda +import semmle.code.cpp.exprs.Literal +import semmle.code.cpp.exprs.BuiltInOperations +import semmle.code.cpp.stmts.Stmt +import semmle.code.cpp.stmts.Block +import semmle.code.cpp.metrics.MetricNamespace +import semmle.code.cpp.metrics.MetricClass +import semmle.code.cpp.metrics.MetricFile +import semmle.code.cpp.metrics.MetricFunction +import semmle.code.cpp.commons.CommonType +import semmle.code.cpp.commons.Printf +import semmle.code.cpp.commons.VoidContext +import semmle.code.cpp.commons.NULL +import semmle.code.cpp.commons.PolymorphicClass +import semmle.code.cpp.commons.Alloc +import semmle.code.cpp.commons.StructLikeClass +import semmle.code.cpp.controlflow.ControlFlowGraph +import semmle.code.cpp.XML +import semmle.code.cpp.Diagnostics +import semmle.code.cpp.Comments +import semmle.code.cpp.Preprocessor +import semmle.code.cpp.Iteration +import semmle.code.cpp.NameQualifiers +import semmle.code.cpp.ObjectiveC +import semmle.code.cpp.exprs.ObjectiveC +import DefaultOptions diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll new file mode 100644 index 00000000000..0cf570a72b4 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisDefinition.qll @@ -0,0 +1,65 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `RangeSsaDefinition`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An SSA definition for which a range can be deduced. As with + * `RangeSsaDefinition` and `SsaDefinition`, instances of this class + * correspond to points in the program where one or more variables are defined + * or have their value constrained in some way. + * + * Extend this class to add functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition { + /** + * Holds if this `SimpleRangeAnalysisDefinition` adds range information for + * `v`. Because a `SimpleRangeAnalysisDefinition` is just a point in the + * program, it's possible that more than one variable might be defined at + * this point. This predicate clarifies which variable(s) should get range + * information from `this`. + * + * This predicate **must be overridden** to hold for any `v` that can show + * up in the other members of `SimpleRangeAnalysisDefinition`. Conversely, + * the other members **must be accurate** for any `v` in this predicate. + */ + abstract predicate hasRangeInformationFor(StackVariable v); + + /** + * Holds if `(this, v)` depends on the range of the unconverted expression + * `e`. This information is used to inform the range analysis about cyclic + * dependencies. Without this information, range analysis might work for + * simple cases but will go into infinite loops on complex code. + * + * For example, when modelling the definition by reference in a call to an + * overloaded `operator=`, written as `v = e`, the definition of `(this, v)` + * depends on `e`. + */ + abstract predicate dependsOnExpr(StackVariable v, Expr e); + + /** + * Gets the lower bound of the variable `v` defined by this definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their dependencies. + */ + abstract float getLowerBounds(StackVariable v); + + /** + * Gets the upper bound of the variable `v` defined by this definition. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their dependencies. + */ + abstract float getUpperBounds(StackVariable v); +} + +import SimpleRangeAnalysisInternal diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll new file mode 100644 index 00000000000..c8c1110b3af --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/models/interfaces/SimpleRangeAnalysisExpr.qll @@ -0,0 +1,78 @@ +/** + * EXPERIMENTAL: The API of this module may change without notice. + * + * Provides a class for modeling `Expr`s with a restricted range. + */ + +import cpp +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis + +/** + * EXPERIMENTAL: The API of this class may change without notice. + * + * An expression for which a range can be deduced. Extend this class to add + * functionality to the range analysis library. + */ +abstract class SimpleRangeAnalysisExpr extends Expr { + /** + * Gets the lower bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getLowerBounds(); + + /** + * Gets the upper bound of the expression. + * + * Implementations of this predicate should use + * `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for + * recursive calls to get the bounds of their children. + */ + abstract float getUpperBounds(); + + /** + * Holds if the range this expression depends on the definition `srcDef` for + * StackVariable `srcVar`. + * + * Because this predicate cannot be recursive, most implementations should + * override `dependsOnChild` instead. + */ + predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() } + + /** + * Holds if this expression depends on the range of its unconverted + * subexpression `child`. This information is used to inform the range + * analysis about cyclic dependencies. Without this information, range + * analysis might work for simple cases but will go into infinite loops on + * complex code. + * + * For example, when modeling a function call whose return value depends on + * all of its arguments, implement this predicate as + * `child = this.getAnArgument()`. + */ + abstract predicate dependsOnChild(Expr child); +} + +import SimpleRangeAnalysisInternal + +/** + * This class exists to prevent the QL front end from emitting compile errors + * inside `SimpleRangeAnalysis.qll` about certain conjuncts being empty + * because the overrides of `SimpleRangeAnalysisExpr` that happen to be in + * scope do not make use of every feature it offers. + */ +private class Empty extends SimpleRangeAnalysisExpr { + Empty() { + // This predicate is complicated enough that the QL type checker doesn't + // see it as empty but simple enough that the optimizer should. + this = this and none() + } + + override float getLowerBounds() { none() } + + override float getUpperBounds() { none() } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll new file mode 100644 index 00000000000..39db446e3d3 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ArrayLengthAnalysis.qll @@ -0,0 +1,282 @@ +/** + * Provides precise tracking of how big the memory pointed to by pointers is. + * For each pointer, we start tracking (starting from the allocation or an array declaration) + * 1) how long is the chunk of memory allocated + * 2) where the current pointer is in this chunk of memory + * As computing this information is obviously not possible for all pointers, + * we do not guarantee the existence of length/offset information for all pointers. + * However, when it exists it is guaranteed to be accurate. + * + * The length and offset are tracked in a similar way to the Rangeanalysis. + * Each length is a `ValueNumber + delta`, and each Offset is an `Operand + delta`. + * We choose to track a `ValueNumber` for length, because the Rangeanalysis offers + * integer bounds on instructions and operands in terms of `ValueNumber`s, + * and `Operand` for offset because integer bounds on `Operand`s are + * tighter than bounds on `Instruction`s. + */ + +import cpp +import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.internal.CppType +private import semmle.code.cpp.models.interfaces.Allocation +private import experimental.semmle.code.cpp.rangeanalysis.RangeUtils + +private newtype TLength = + TZeroLength() or + TVNLength(ValueNumber vn) { + not vn.getAnInstruction() instanceof ConstantInstruction and + exists(Instruction i | + vn.getAnInstruction() = i and + ( + i.getResultIRType() instanceof IRSignedIntegerType or + i.getResultIRType() instanceof IRUnsignedIntegerType + ) + | + i instanceof PhiInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof CallInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction + or + i.getAUse() instanceof ArgumentOperand + ) + } + +/** + * Array lengths are represented in a ValueNumber | Zero + delta format. + * This class keeps track of the ValueNumber or Zero. + * The delta is tracked in the predicate `knownArrayLength`. + */ +class Length extends TLength { + string toString() { none() } // overridden in subclasses +} + +/** + * This length class corresponds to an array having a constant length + * that is tracked by the delta value. + */ +class ZeroLength extends Length, TZeroLength { + override string toString() { result = "ZeroLength" } +} + +/** + * This length class corresponds to an array having variable length, i.e. the + * length is tracked by a value number. One example is an array having length + * `count` for an integer variable `count` in the program. + */ +class VNLength extends Length, TVNLength { + ValueNumber vn; + + VNLength() { this = TVNLength(vn) } + + /** Gets an instruction with this value number bound. */ + Instruction getInstruction() { this = TVNLength(valueNumber(result)) } + + ValueNumber getValueNumber() { result = vn } + + override string toString() { result = "VNLength(" + vn.getExampleInstruction().toString() + ")" } +} + +private newtype TOffset = + TZeroOffset() or + TOpOffset(Operand op) { + op.getAnyDef().getResultIRType() instanceof IRSignedIntegerType or + op.getAnyDef().getResultIRType() instanceof IRUnsignedIntegerType + } + +/** + * This class describes the offset of a pointer in a chunk of memory. + * It is either an `Operand` or zero, an additional integer delta is added later. + */ +class Offset extends TOffset { + string toString() { none() } // overridden in subclasses +} + +/** + * This class represents a fixed offset, only specified by a delta. + */ +class ZeroOffset extends Offset, TZeroOffset { + override string toString() { result = "ZeroOffset" } +} + +/** + * This class represents an offset of an operand. + */ +class OpOffset extends Offset, TOpOffset { + Operand op; + + OpOffset() { this = TOpOffset(op) } + + Operand getOperand() { result = op } + + override string toString() { result = "OpOffset(" + op.getDef().toString() + ")" } +} + +private int getBaseSizeForPointerType(PointerType type) { result = type.getBaseType().getSize() } + +/** + * Holds if pointer `prev` that points at offset `prevOffset + prevOffsetDelta` + * steps to `array` that points to `offset + offsetDelta` in one step. + * This predicate does not contain any recursive steps. + */ +bindingset[prevOffset, prevOffsetDelta] +predicate simpleArrayLengthStep( + Instruction array, Offset offset, int offsetDelta, Instruction prev, Offset prevOffset, + int prevOffsetDelta +) { + // array assign + array.(CopyInstruction).getSourceValue() = prev and + offset = prevOffset and + offsetDelta = prevOffsetDelta + or + // pointer add with constant + array.(PointerAddInstruction).getLeft() = prev and + offset = prevOffset and + offsetDelta = prevOffsetDelta + getConstantValue(array.(PointerAddInstruction).getRight()) + or + // pointer add with variable + array.(PointerAddInstruction).getLeft() = prev and + prevOffset instanceof ZeroOffset and + offset.(OpOffset).getOperand() = array.(PointerAddInstruction).getRightOperand() and + offsetDelta = prevOffsetDelta and + not exists(getConstantValue(array.(PointerAddInstruction).getRight())) + or + // pointer sub with constant + array.(PointerSubInstruction).getLeft() = prev and + offset = prevOffset and + offsetDelta = prevOffsetDelta - getConstantValue(array.(PointerSubInstruction).getRight()) + or + // array to pointer decay + array.(ConvertInstruction).getUnary() = prev and + array.getConvertedResultExpression() instanceof ArrayToPointerConversion and + offset = prevOffset and + offsetDelta = prevOffsetDelta + or + // cast of pointer to pointer with the same element size + exists(PointerType fromTyp, PointerType toTyp | + array.(PtrToPtrCastInstruction).getUnary() = prev and + prev.getResultLanguageType().hasType(fromTyp, false) and + array.getResultLanguageType().hasType(toTyp, false) and + offset = prevOffset and + offsetDelta = prevOffsetDelta and + if fromTyp instanceof VoidPointerType + then getBaseSizeForPointerType(toTyp) = 1 + else ( + if toTyp instanceof VoidPointerType + then getBaseSizeForPointerType(fromTyp) = 1 + else getBaseSizeForPointerType(toTyp) = getBaseSizeForPointerType(fromTyp) + ) + ) +} + +/** + * Parses a `sizeExpr` of malloc into a variable part (`lengthExpr`) and an integer offset (`delta`). + */ +private predicate deconstructMallocSizeExpr(Expr sizeExpr, Expr lengthExpr, int delta) { + sizeExpr instanceof AddExpr and + exists(Expr constantExpr | + lengthExpr = sizeExpr.(AddExpr).getAnOperand() and + constantExpr = sizeExpr.(AddExpr).getAnOperand() and + lengthExpr != constantExpr and + delta = constantExpr.getValue().toInt() + ) + or + sizeExpr instanceof SubExpr and + exists(Expr constantExpr | + lengthExpr = sizeExpr.(SubExpr).getLeftOperand() and + constantExpr = sizeExpr.(SubExpr).getRightOperand() and + delta = -constantExpr.getValue().toInt() + ) +} + +/** + * Holds if the instruction `array` is a dynamic memory allocation of `length`+`delta` elements. + */ +private predicate allocation(Instruction array, Length length, int delta) { + exists(AllocationExpr alloc, Type ptrTyp | + array.getUnconvertedResultExpression() = alloc and + array.getResultLanguageType().hasType(ptrTyp, false) and + // ensure that we have the same type of the allocation and the pointer + ptrTyp.stripTopLevelSpecifiers().(PointerType).getBaseType().getUnspecifiedType() = + alloc.getAllocatedElementType().getUnspecifiedType() and + // ensure that the size multiplier of the allocation is the same as the + // size of the type we are allocating + alloc.getSizeMult() = getBaseSizeForPointerType(ptrTyp) and + ( + length instanceof ZeroLength and + delta = alloc.getSizeExpr().getValue().toInt() + or + not exists(alloc.getSizeExpr().getValue().toInt()) and + ( + exists(Expr lengthExpr | + deconstructMallocSizeExpr(alloc.getSizeExpr(), lengthExpr, delta) and + length.(VNLength).getInstruction().getConvertedResultExpression() = lengthExpr + ) + or + not exists(int d | deconstructMallocSizeExpr(alloc.getSizeExpr(), _, d)) and + length.(VNLength).getInstruction().getConvertedResultExpression() = alloc.getSizeExpr() and + delta = 0 + ) + ) + ) +} + +/** + * Holds if `array` is declared as an array with length `length + lengthDelta` + */ +private predicate arrayDeclaration(Instruction array, Length length, int lengthDelta) { + ( + array instanceof VariableAddressInstruction or + array instanceof FieldAddressInstruction + ) and + exists(ArrayType type | array.getResultLanguageType().hasType(type, _) | + length instanceof ZeroLength and + lengthDelta = type.getArraySize() + ) +} + +/** + * Holds if `array` is declared as an array or allocated + * with length `length + lengthDelta` + */ +predicate arrayAllocationOrDeclaration(Instruction array, Length length, int lengthDelta) { + allocation(array, length, lengthDelta) + or + // declaration of variable of array type + arrayDeclaration(array, length, lengthDelta) +} + +/** + * Holds if the instruction `array` represents a pointer to a chunk of memory that holds + * `length + lengthDelta` elements, using only local analysis. + * `array` points at `offset + offsetDelta` in the chunk of memory. + * The pointer is in-bounds if `offset + offsetDelta < length + lengthDelta` and + * `offset + offsetDelta >= 0` holds. + * The pointer is out-of-bounds if `offset + offsetDelta >= length + lengthDelta` + * or `offset + offsetDelta < 0` holds. + * All pointers in this predicate are guaranteed to be non-null, + * but are not guaranteed to be live. + */ +predicate knownArrayLength( + Instruction array, Length length, int lengthDelta, Offset offset, int offsetDelta +) { + arrayAllocationOrDeclaration(array, length, lengthDelta) and + offset instanceof ZeroOffset and + offsetDelta = 0 + or + // simple step (no phi nodes) + exists(Instruction prev, Offset prevOffset, int prevOffsetDelta | + knownArrayLength(prev, length, lengthDelta, prevOffset, prevOffsetDelta) and + simpleArrayLengthStep(array, offset, offsetDelta, prev, prevOffset, prevOffsetDelta) + ) + or + // merge control flow after phi node - but only if all the bounds agree + forex(Instruction input | array.(PhiInstruction).getAnInput() = input | + knownArrayLength(input, length, lengthDelta, offset, offsetDelta) + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/Bound.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/Bound.qll new file mode 100644 index 00000000000..bdd0e39f9b7 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/Bound.qll @@ -0,0 +1,82 @@ +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.ValueNumbering + +private newtype TBound = + TBoundZero() or + TBoundValueNumber(ValueNumber vn) { + exists(Instruction i | + vn.getAnInstruction() = i and + ( + i.getResultIRType() instanceof IRIntegerType or + i.getResultIRType() instanceof IRAddressType + ) and + not vn.getAnInstruction() instanceof ConstantInstruction + | + i instanceof PhiInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof CallInstruction + or + i instanceof VariableAddressInstruction + or + i instanceof FieldAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction + or + i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction + or + i.getAUse() instanceof ArgumentOperand + ) + } + +/** + * A bound that may be inferred for an expression plus/minus an integer delta. + */ +abstract class Bound extends TBound { + abstract string toString(); + + /** Gets an expression that equals this bound plus `delta`. */ + abstract Instruction getInstruction(int delta); + + /** Gets an expression that equals this bound. */ + Instruction getInstruction() { result = getInstruction(0) } + + abstract Location getLocation(); +} + +/** + * The bound that corresponds to the integer 0. This is used to represent all + * integer bounds as bounds are always accompanied by an added integer delta. + */ +class ZeroBound extends Bound, TBoundZero { + override string toString() { result = "0" } + + override Instruction getInstruction(int delta) { + result.(ConstantValueInstruction).getValue().toInt() = delta + } + + override Location getLocation() { result instanceof UnknownDefaultLocation } +} + +/** + * A bound corresponding to the value of an `Instruction`. + */ +class ValueNumberBound extends Bound, TBoundValueNumber { + ValueNumber vn; + + ValueNumberBound() { this = TBoundValueNumber(vn) } + + /** Gets an `Instruction` that equals this bound. */ + override Instruction getInstruction(int delta) { + this = TBoundValueNumber(valueNumber(result)) and delta = 0 + } + + override string toString() { result = vn.getExampleInstruction().toString() } + + override Location getLocation() { result = vn.getLocation() } + + /** Gets the value number that equals this bound. */ + ValueNumber getValueNumber() { result = vn } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll new file mode 100644 index 00000000000..bc63d740c32 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/ExtendedRangeAnalysis.qll @@ -0,0 +1,5 @@ +import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis +// +// Import each extension we want to enable +import extensions.SubtractSelf +import extensions.ConstantBitwiseAndExprRange diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll new file mode 100644 index 00000000000..a7375f66b66 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/InBoundsPointerDeref.qll @@ -0,0 +1,105 @@ +/** + * This library proves that a subset of pointer dereferences in a program are + * safe, i.e. in-bounds. + * It does so by first defining what a pointer dereference is (on the IR + * `Instruction` level), and then using the array length analysis and the range + * analysis together to prove that some of these pointer dereferences are safe. + * + * The analysis is soundy, i.e. it is sound if no undefined behaviour is present + * in the program. + * Furthermore, it crucially depends on the soundiness of the range analysis and + * the array length analysis. + */ + +import cpp +private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis +private import experimental.semmle.code.cpp.rangeanalysis.RangeAnalysis + +/** + * Gets the instruction that computes the address of memory that `i` accesses. + * Only holds if `i` dereferences a pointer, not when the computation of the + * memory address is constant, or if the address of a local variable is loaded/stored to. + */ +private Instruction getMemoryAddressInstruction(Instruction i) { + ( + result = i.(FieldAddressInstruction).getObjectAddress() or + result = i.(LoadInstruction).getSourceAddress() or + result = i.(StoreInstruction).getDestinationAddress() + ) and + not result instanceof FieldAddressInstruction and + not result instanceof VariableAddressInstruction and + not result instanceof ConstantValueInstruction +} + +/** + * All instructions that dereference a pointer. + */ +class PointerDereferenceInstruction extends Instruction { + PointerDereferenceInstruction() { exists(getMemoryAddressInstruction(this)) } + + Instruction getAddress() { result = getMemoryAddressInstruction(this) } +} + +/** + * Holds if `ptrDeref` can be proven to always access allocated memory. + */ +predicate inBounds(PointerDereferenceInstruction ptrDeref) { + exists(Length length, int lengthDelta, Offset offset, int offsetDelta | + knownArrayLength(ptrDeref.getAddress(), length, lengthDelta, offset, offsetDelta) and + // lower bound - note that we treat a pointer that accesses an array of + // length 0 as on upper-bound violation, but not as a lower-bound violation + ( + offset instanceof ZeroOffset and + offsetDelta >= 0 + or + offset instanceof OpOffset and + exists(int lowerBoundDelta | + boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), lowerBoundDelta, + /*upper*/ false, _) and + lowerBoundDelta + offsetDelta >= 0 + ) + ) and + // upper bound + ( + // both offset and length are only integers + length instanceof ZeroLength and + offset instanceof ZeroOffset and + offsetDelta < lengthDelta + or + exists(int lengthBound | + // array length is variable+integer, and there's a fixed (integer-only) + // lower bound on the variable, so we can guarantee this access is always in-bounds + length instanceof VNLength and + offset instanceof ZeroOffset and + boundedInstruction(length.(VNLength).getInstruction(), any(ZeroBound b), lengthBound, + /* upper*/ false, _) and + offsetDelta < lengthBound + lengthDelta + ) + or + exists(int offsetBoundDelta | + length instanceof ZeroLength and + offset instanceof OpOffset and + boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), offsetBoundDelta, + /* upper */ true, _) and + // offset <= offsetBoundDelta, so offset + offsetDelta <= offsetDelta + offsetBoundDelta + // Thus, in-bounds if offsetDelta + offsetBoundDelta < lengthDelta + // as we have length instanceof ZeroLength + offsetDelta + offsetBoundDelta < lengthDelta + ) + or + exists(ValueNumberBound b, int offsetBoundDelta | + length instanceof VNLength and + offset instanceof OpOffset and + b.getValueNumber() = length.(VNLength).getValueNumber() and + // It holds that offset <= length + offsetBoundDelta + boundedOperand(offset.(OpOffset).getOperand(), b, offsetBoundDelta, /*upper*/ true, _) and + // it also holds that + offsetDelta < lengthDelta - offsetBoundDelta + // taking both inequalities together we get + // offset <= length + offsetBoundDelta + // => offset + offsetDelta <= length + offsetBoundDelta + offsetDelta < length + offsetBoundDelta + lengthDelta - offsetBoundDelta + // as required + ) + ) + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll new file mode 100644 index 00000000000..4a5bef36b20 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll @@ -0,0 +1,624 @@ +/** + * Provides classes and predicates for range analysis. + * + * An inferred bound can either be a specific integer or a `ValueNumber` + * representing the abstract value of a set of `Instruction`s. + * + * If an inferred bound relies directly on a condition, then this condition is + * reported as the reason for the bound. + */ + +/* + * This library tackles range analysis as a flow problem. Consider e.g.: + * ``` + * len = arr.length; + * if (x < len) { ... y = x-1; ... y ... } + * ``` + * In this case we would like to infer `y <= arr.length - 2`, and this is + * accomplished by tracking the bound through a sequence of steps: + * ``` + * arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y + * ``` + * + * In its simplest form the step relation `I1 --> I2` relates two `Instruction`s + * such that `I1 <= B` implies `I2 <= B` for any `B` (with a second separate + * step relation handling lower bounds). Examples of such steps include + * assignments `I2 = I1` and conditions `x <= I1` where `I2` is a use of `x` + * guarded by the condition. + * + * In order to handle subtractions and additions with constants, and strict + * comparisons, the step relation is augmented with an integer delta. With this + * generalization `I1 --(delta)--> I2` relates two `Instruction`s and an integer + * such that `I1 <= B` implies `I2 <= B + delta` for any `B`. This corresponds + * to the predicate `boundFlowStep`. + * + * The complete range analysis is then implemented as the transitive closure of + * the step relation summing the deltas along the way. If `I1` transitively + * steps to `I2`, `delta` is the sum of deltas along the path, and `B` is an + * interesting bound equal to the value of `I1` then `I2 <= B + delta`. This + * corresponds to the predicate `boundedInstruction`. + * + * Bounds come in two forms: either they are relative to zero (and thus provide + * a constant bound), or they are relative to some program value. This value is + * represented by the `ValueNumber` class, each instance of which represents a + * set of `Instructions` that must have the same value. + * + * Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`. + * There are essentially two cases: + * - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`. + * - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`. + * The first case is for whenever a bound can be proven without taking looping + * into account. The second case is relevant when `x2` comes from a back-edge + * where we can prove that the variable has been non-increasing through the + * loop-iteration as this means that any upper bound that holds prior to the + * loop also holds for the variable during the loop. + * This generalizes to a phi node with `n` inputs, so if + * `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we + * also have `x0 <= B + delta` if we can prove either: + * - `xj <= B + d` with `d <= delta` or + * - `xj <= x0 + d` with `d <= 0` + * for each input `xj`. + * + * As all inferred bounds can be related directly to a path in the source code + * the only source of non-termination is if successive redundant (and thereby + * increasingly worse) bounds are calculated along a loop in the source code. + * We prevent this by weakening the bound to a small finite set of bounds when + * a path follows a second back-edge (we postpone weakening till the second + * back-edge as a precise bound might require traversing a loop once). + */ + +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.ir.ValueNumbering +private import RangeUtils +private import SignAnalysis +import Bound + +cached +private module RangeAnalysisCache { + cached + module RangeAnalysisPublic { + /** + * Holds if `b + delta` is a valid bound for `i` and this is the best such delta. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { + boundedInstruction(i, b, delta, upper, _, _, reason) and + bestInstructionBound(i, b, delta, upper) + } + + /** + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ + cached + predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedOperandCand(op, b, delta, upper, reason) and + bestOperandBound(op, b, delta, upper) + } + } + + /** + * Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`. + */ + cached + predicate possibleReason(IRGuardCondition guard) { + guard = boundFlowCond(_, _, _, _, _) + or + guard = eqFlowCond(_, _, _, _, _) + } +} + +private import RangeAnalysisCache +import RangeAnalysisPublic + +/** + * Holds if `b + delta` is a valid bound for `e` and this is the best such delta. + * - `upper = true` : `e <= b + delta` + * - `upper = false` : `e >= b + delta` + */ +private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) { + delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true + or + delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false +} + +/** + * Holds if `b + delta` is a valid bound for `op`. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ +private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedNonPhiOperand(op, b, delta, upper, _, _, reason) + or + boundedPhiOperand(op, b, delta, upper, _, _, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + */ +private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) { + delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true + or + delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false +} + +/** + * Gets a condition that tests whether `vn` equals `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `isEq = true` : `vn == bound + delta` + * - `isEq = false` : `vn != bound + delta` + */ +private IRGuardCondition eqFlowCond( + ValueNumber vn, Operand bound, int delta, boolean isEq, boolean testIsTrue +) { + result.comparesEq(vn.getAUse(), bound, delta, isEq, testIsTrue) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepSsa( + NonPhiOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) + or + exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast | + valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and + guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Gets a condition that tests whether `vn` is bounded by `bound + delta`. + * + * If the condition evaluates to `testIsTrue`: + * - `upper = true` : `vn <= bound + delta` + * - `upper = false` : `vn >= bound + delta` + */ +private IRGuardCondition boundFlowCond( + ValueNumber vn, NonPhiOperand bound, int delta, boolean upper, boolean testIsTrue +) { + exists(int d | + result.comparesLt(vn.getAUse(), bound, d, upper, testIsTrue) and + // `comparesLt` provides bounds of the form `x < y + k` or `x >= y + k`, but we need + // `x <= y + k` so we strengthen here. `testIsTrue` has the same semantics in `comparesLt` as + // it does here, so we don't need to account for it. + if upper = true then delta = d - 1 else delta = d + ) + or + result = eqFlowCond(vn, bound, delta, true, testIsTrue) and + (upper = true or upper = false) +} + +private newtype TReason = + TNoReason() or + TCondReason(IRGuardCondition guard) { possibleReason(guard) } + +/** + * A reason for an inferred bound. This can either be `CondReason` if the bound + * is due to a specific condition, or `NoReason` if the bound is inferred + * without going through a bounding condition. + */ +abstract class Reason extends TReason { + abstract string toString(); +} + +class NoReason extends Reason, TNoReason { + override string toString() { result = "NoReason" } +} + +class CondReason extends Reason, TCondReason { + IRGuardCondition getCond() { this = TCondReason(result) } + + override string toString() { result = getCond().toString() } +} + +/** + * Holds if `typ` is a small integral type with the given lower and upper bounds. + */ +private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) { + typ.isSigned() and typ.getByteSize() = 1 and lowerbound = -128 and upperbound = 127 + or + typ.isUnsigned() and typ.getByteSize() = 1 and lowerbound = 0 and upperbound = 255 + or + typ.isSigned() and typ.getByteSize() = 2 and lowerbound = -32768 and upperbound = 32767 + or + typ.isUnsigned() and typ.getByteSize() = 2 and lowerbound = 0 and upperbound = 65535 +} + +/** + * A cast to a small integral type that may overflow or underflow. + */ +private class NarrowingCastInstruction extends ConvertInstruction { + NarrowingCastInstruction() { + not this instanceof SafeCastInstruction and + typeBound(getResultIRType(), _, _) + } + + /** Gets the lower bound of the resulting type. */ + int getLowerBound() { typeBound(getResultIRType(), result, _) } + + /** Gets the upper bound of the resulting type. */ + int getUpperBound() { typeBound(getResultIRType(), _, result) } +} + +/** + * Holds if `op + delta` is a valid bound for `i`. + * - `upper = true` : `i <= op + delta` + * - `upper = false` : `i >= op + delta` + */ +private predicate boundFlowStep(Instruction i, NonPhiOperand op, int delta, boolean upper) { + valueFlowStep(i, op, delta) and + (upper = true or upper = false) + or + i.(SafeCastInstruction).getAnOperand() = op and + delta = 0 and + (upper = true or upper = false) + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + not exists(getValue(getConstantValue(op.getUse()))) and + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = false and delta = 1 + else + if positive(x) + then upper = false and delta = 0 + else + if strictlyNegative(x) + then upper = true and delta = -1 + else + if negative(x) + then upper = true and delta = 0 + else none() + ) + or + exists(Operand x | + exists(SubInstruction sub | + i = sub and + sub.getLeftOperand() = op and + sub.getRightOperand() = x + ) + | + // `x` with constant value is covered by valueFlowStep + not exists(getValue(getConstantValue(x.getUse()))) and + if strictlyPositive(x) + then upper = true and delta = -1 + else + if positive(x) + then upper = true and delta = 0 + else + if strictlyNegative(x) + then upper = false and delta = 1 + else + if negative(x) + then upper = false and delta = 0 + else none() + ) + or + i.(RemInstruction).getRightOperand() = op and positive(op) and delta = -1 and upper = true + or + i.(RemInstruction).getLeftOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitAndInstruction).getAnOperand() = op and positive(op) and delta = 0 and upper = true + or + i.(BitOrInstruction).getAnOperand() = op and + positiveInstruction(i) and + delta = 0 and + upper = false + // TODO: min, max, rand +} + +private predicate boundFlowStepMul(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + i1.(MulInstruction).hasOperands(op, c.getAUse()) and factor = k + or + exists(ShiftLeftInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRightOperand() = c.getAUse() and factor = 2.pow(k) + ) + ) +} + +private predicate boundFlowStepDiv(Instruction i1, Operand op, int factor) { + exists(Instruction c, int k | k = getValue(getConstantValue(c)) and k > 0 | + exists(DivInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = k + ) + or + exists(ShiftRightInstruction i | + i = i1 and i.getLeftOperand() = op and i.getRight() = c and factor = 2.pow(k) + ) + ) +} + +/** + * Holds if `b` is a valid bound for `op` + */ +pragma[noinline] +private predicate boundedNonPhiOperand( + NonPhiOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2 | + boundFlowStepSsa(op, op2, d1, upper, reason) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, _) and + delta = d1 + d2 + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedNonPhiOperand(op, b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** + * Holds if `op1 + delta` is a valid bound for `op2`. + * - `upper = true` : `op2 <= op1 + delta` + * - `upper = false` : `op2 >= op1 + delta` + */ +private predicate boundFlowStepPhi( + PhiInputOperand op2, Operand op1, int delta, boolean upper, Reason reason +) { + op2.getDef().(CopyInstruction).getSourceValueOperand() = op1 and + (upper = true or upper = false) and + reason = TNoReason() and + delta = 0 + or + exists(IRGuardCondition guard, boolean testIsTrue | + guard = boundFlowCond(valueNumberOfOperand(op2), op1, delta, upper, testIsTrue) and + guard.controlsEdge(op2.getPredecessorBlock(), op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +private predicate boundedPhiOperand( + PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(NonPhiOperand op2, int d1, int d2, Reason r1, Reason r2 | + boundFlowStepPhi(op, op2, d1, upper, r1) and + boundedNonPhiOperand(op2, b, d2, upper, fromBackEdge, origdelta, r2) and + delta = d1 + d2 and + (if r1 instanceof NoReason then reason = r2 else reason = r1) + ) + or + boundedInstruction(op.getDef(), b, delta, upper, fromBackEdge, origdelta, reason) + or + exists(int d, Reason r1, Reason r2 | + boundedInstruction(op.getDef(), b, d, upper, fromBackEdge, origdelta, r2) + | + unequalOperand(op, b, d, r1) and + ( + upper = true and delta = d - 1 + or + upper = false and delta = d + 1 + ) and + ( + reason = r1 + or + reason = r2 and not r2 instanceof NoReason + ) + ) +} + +/** Holds if `op2 != op1 + delta` at `pos`. */ +private predicate unequalFlowStep(Operand op2, Operand op1, int delta, Reason reason) { + exists(IRGuardCondition guard, boolean testIsTrue | + guard = eqFlowCond(valueNumberOfOperand(op2), op1, delta, false, testIsTrue) and + guard.controls(op2.getUse().getBlock(), testIsTrue) and + reason = TCondReason(guard) + ) +} + +/** + * Holds if `op != b + delta` at `pos`. + */ +private predicate unequalOperand(Operand op, Bound b, int delta, Reason reason) { + exists(Operand op2, int d1, int d2 | + unequalFlowStep(op, op2, d1, reason) and + boundedNonPhiOperand(op2, b, d2, true, _, _, _) and + boundedNonPhiOperand(op2, b, d2, false, _, _, _) and + delta = d1 + d2 + ) +} + +private predicate boundedPhiCandValidForEdge( + PhiInstruction phi, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason, PhiInputOperand op +) { + boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and + ( + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = true and d <= delta) + or + exists(int d | boundedPhiInp1(phi, op, b, d, upper) | upper = false and d >= delta) + or + selfBoundedPhiInp(phi, op, upper) + ) +} + +/** Weakens a delta to lie in the range `[-1..1]`. */ +bindingset[delta, upper] +private int weakenDelta(boolean upper, int delta) { + delta in [-1 .. 1] and result = delta + or + upper = true and result = -1 and delta < -1 + or + upper = false and result = 1 and delta > 1 +} + +private predicate boundedPhiInp( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + phi.getAnOperand() = op and + exists(int d, boolean fromBackEdge0 | + boundedPhiOperand(op, b, d, upper, fromBackEdge0, origdelta, reason) + or + b.(ValueNumberBound).getInstruction() = op.getDef() and + d = 0 and + (upper = true or upper = false) and + fromBackEdge0 = false and + origdelta = 0 and + reason = TNoReason() + | + if backEdge(phi, op) + then + fromBackEdge = true and + ( + fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta + or + fromBackEdge0 = false and delta = d + ) + else ( + delta = d and fromBackEdge = fromBackEdge0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiInp1( + PhiInstruction phi, PhiInputOperand op, Bound b, int delta, boolean upper +) { + boundedPhiInp(phi, op, b, delta, upper, _, _, _) +} + +private predicate selfBoundedPhiInp(PhiInstruction phi, PhiInputOperand op, boolean upper) { + exists(int d, ValueNumberBound phibound | + phibound.getInstruction() = phi and + boundedPhiInp(phi, op, phibound, d, upper, _, _, _) and + ( + upper = true and d <= 0 + or + upper = false and d >= 0 + ) + ) +} + +pragma[noinline] +private predicate boundedPhiCand( + PhiInstruction phi, boolean upper, Bound b, int delta, boolean fromBackEdge, int origdelta, + Reason reason +) { + exists(PhiInputOperand op | + boundedPhiInp(phi, op, b, delta, upper, fromBackEdge, origdelta, reason) + ) +} + +/** + * Holds if the value being cast has an upper (for `upper = true`) or lower + * (for `upper = false`) bound within the bounds of the resulting type. + * For `upper = true` this means that the cast will not overflow and for + * `upper = false` this means that the cast will not underflow. + */ +private predicate safeNarrowingCast(NarrowingCastInstruction cast, boolean upper) { + exists(int bound | + boundedNonPhiOperand(cast.getAnOperand(), any(ZeroBound zb), bound, upper, _, _, _) + | + upper = true and bound <= cast.getUpperBound() + or + upper = false and bound >= cast.getLowerBound() + ) +} + +pragma[noinline] +private predicate boundedCastExpr( + NarrowingCastInstruction cast, Bound b, int delta, boolean upper, boolean fromBackEdge, + int origdelta, Reason reason +) { + boundedNonPhiOperand(cast.getAnOperand(), b, delta, upper, fromBackEdge, origdelta, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `i`. + * - `upper = true` : `i <= b + delta` + * - `upper = false` : `i >= b + delta` + */ +private predicate boundedInstruction( + Instruction i, Bound b, int delta, boolean upper, boolean fromBackEdge, int origdelta, + Reason reason +) { + i instanceof PhiInstruction and + forex(PhiInputOperand op | op = i.getAnOperand() | + boundedPhiCandValidForEdge(i, b, delta, upper, fromBackEdge, origdelta, reason, op) + ) + or + i = b.getInstruction(delta) and + (upper = true or upper = false) and + fromBackEdge = false and + origdelta = delta and + reason = TNoReason() + or + exists(Operand mid, int d1, int d2 | + boundFlowStep(i, mid, d1, upper) and + boundedNonPhiOperand(mid, b, d2, upper, fromBackEdge, origdelta, reason) and + delta = d1 + d2 and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepMul(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + b instanceof ZeroBound and + delta = d * factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(Operand mid, int factor, int d | + boundFlowStepDiv(i, mid, factor) and + boundedNonPhiOperand(mid, b, d, upper, fromBackEdge, origdelta, reason) and + d >= 0 and + b instanceof ZeroBound and + delta = d / factor and + not exists(getValue(getConstantValue(i))) + ) + or + exists(NarrowingCastInstruction cast | + cast = i and + safeNarrowingCast(cast, upper.booleanNot()) and + boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason) + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll new file mode 100644 index 00000000000..bffd08fbe52 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/RangeUtils.qll @@ -0,0 +1,134 @@ +import cpp +private import semmle.code.cpp.ir.IR +// TODO: move this dependency +import semmle.code.cpp.ir.internal.IntegerConstant + +// TODO: move this out of test code +language[monotonicAggregates] +IntValue getConstantValue(Instruction instr) { + result = instr.(IntegerConstantInstruction).getValue().toInt() + or + exists(BinaryInstruction binInstr, IntValue left, IntValue right | + binInstr = instr and + left = getConstantValue(binInstr.getLeft()) and + right = getConstantValue(binInstr.getRight()) and + ( + binInstr instanceof AddInstruction and result = add(left, right) + or + binInstr instanceof SubInstruction and result = sub(left, right) + or + binInstr instanceof MulInstruction and result = mul(left, right) + or + binInstr instanceof DivInstruction and result = div(left, right) + ) + ) + or + result = getConstantValue(instr.(CopyInstruction).getSourceValue()) + or + exists(PhiInstruction phi | + phi = instr and + result = + max(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) and + result = + min(PhiInputOperand operand | + operand = phi.getAnOperand() + | + getConstantValue(operand.getDef()) + ) + ) +} + +predicate valueFlowStep(Instruction i, Operand op, int delta) { + i.(CopyInstruction).getSourceValueOperand() = op and delta = 0 + or + exists(Operand x | + i.(AddInstruction).getAnOperand() = op and + i.(AddInstruction).getAnOperand() = x and + op != x + | + delta = getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(SubInstruction).getLeftOperand() = op and + i.(SubInstruction).getRightOperand() = x + | + delta = -getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerAddInstruction).getAnOperand() = op and + i.(PointerAddInstruction).getAnOperand() = x and + op != x + | + delta = i.(PointerAddInstruction).getElementSize() * getValue(getConstantValue(x.getDef())) + ) + or + exists(Operand x | + i.(PointerSubInstruction).getLeftOperand() = op and + i.(PointerSubInstruction).getRightOperand() = x + | + delta = i.(PointerSubInstruction).getElementSize() * -getValue(getConstantValue(x.getDef())) + ) +} + +predicate backEdge(PhiInstruction phi, PhiInputOperand op) { + phi.getAnOperand() = op and + phi.getBlock() = op.getPredecessorBlock().getBackEdgeSuccessor(_) +} + +/** + * Holds if a cast from `fromtyp` to `totyp` can be ignored for the purpose of + * range analysis. + */ +pragma[inline] +private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) { + fromtyp.getByteSize() < totyp.getByteSize() and + ( + fromtyp.isUnsigned() + or + totyp.isSigned() + ) + or + fromtyp.getByteSize() <= totyp.getByteSize() and + ( + fromtyp.isSigned() and + totyp.isSigned() + or + fromtyp.isUnsigned() and + totyp.isUnsigned() + ) +} + +/** + * A `ConvertInstruction` which casts from one pointer type to another. + */ +class PtrToPtrCastInstruction extends ConvertInstruction { + PtrToPtrCastInstruction() { + getResultIRType() instanceof IRAddressType and + getUnary().getResultIRType() instanceof IRAddressType + } +} + +/** + * A `ConvertInstruction` which casts from one integer type to another in a way + * that cannot overflow or underflow. + */ +class SafeIntCastInstruction extends ConvertInstruction { + SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) } +} + +/** + * A `ConvertInstruction` which does not invalidate bounds determined by + * range analysis. + */ +class SafeCastInstruction extends ConvertInstruction { + SafeCastInstruction() { + this instanceof PtrToPtrCastInstruction or + this instanceof SafeIntCastInstruction + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll new file mode 100644 index 00000000000..0bd73105cc5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/SignAnalysis.qll @@ -0,0 +1,583 @@ +/** + * Provides sign analysis to determine whether expression are always positive + * or negative. + * + * The analysis is implemented as an abstract interpretation over the + * three-valued domain `{negative, zero, positive}`. + */ + +import cpp +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.ir.ValueNumbering +private import SignAnalysisCached + +private newtype TSign = + TNeg() or + TZero() or + TPos() + +private class Sign extends TSign { + string toString() { + result = "-" and this = TNeg() + or + result = "0" and this = TZero() + or + result = "+" and this = TPos() + } + + Sign inc() { + this = TNeg() and result = TNeg() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TPos() + or + this = TPos() and result = TPos() + } + + Sign dec() { result.inc() = this } + + Sign neg() { + this = TNeg() and result = TPos() + or + this = TZero() and result = TZero() + or + this = TPos() and result = TNeg() + } + + Sign bitnot() { + this = TNeg() and result = TPos() + or + this = TNeg() and result = TZero() + or + this = TZero() and result = TNeg() + or + this = TPos() and result = TNeg() + } + + Sign add(Sign s) { + this = TZero() and result = s + or + s = TZero() and result = this + or + this = s and this = result + or + this = TPos() and s = TNeg() + or + this = TNeg() and s = TPos() + } + + Sign mul(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign div(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign rem(Sign s) { + result = TZero() and s = TNeg() + or + result = TZero() and s = TPos() + or + result = this and s = TNeg() + or + result = this and s = TPos() + } + + Sign bitand(Sign s) { + result = TZero() and this = TZero() + or + result = TZero() and s = TZero() + or + result = TZero() and this = TPos() + or + result = TZero() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TPos() + or + result = TPos() and this = TPos() and s = TNeg() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitor(Sign s) { + result = TZero() and this = TZero() and s = TZero() + or + result = TNeg() and this = TNeg() + or + result = TNeg() and s = TNeg() + or + result = TPos() and this = TPos() and s = TZero() + or + result = TPos() and this = TZero() and s = TPos() + or + result = TPos() and this = TPos() and s = TPos() + } + + Sign bitxor(Sign s) { + result = TZero() and this = s + or + result = this and s = TZero() + or + result = s and this = TZero() + or + result = TPos() and this = TPos() and s = TPos() + or + result = TNeg() and this = TNeg() and s = TPos() + or + result = TNeg() and this = TPos() and s = TNeg() + or + result = TPos() and this = TNeg() and s = TNeg() + } + + Sign lshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + this != TZero() and s != TZero() + } + + Sign rshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result = TNeg() and this = TNeg() + or + result != TNeg() and this = TPos() and s != TZero() + } + + Sign urshift(Sign s) { + result = TZero() and this = TZero() + or + result = this and s = TZero() + or + result != TZero() and this = TNeg() and s != TZero() + or + result != TNeg() and this = TPos() and s != TZero() + } +} + +private Sign certainInstructionSign(Instruction inst) { + exists(int i | inst.(IntegerConstantInstruction).getValue().toInt() = i | + i < 0 and result = TNeg() + or + i = 0 and result = TZero() + or + i > 0 and result = TPos() + ) + or + exists(float f | f = inst.(FloatConstantInstruction).getValue().toFloat() | + f < 0 and result = TNeg() + or + f = 0 and result = TZero() + or + f > 0 and result = TPos() + ) +} + +private newtype CastKind = + TWiden() or + TSame() or + TNarrow() + +private CastKind getCastKind(ConvertInstruction ci) { + exists(int fromSize, int toSize | + toSize = ci.getResultSize() and + fromSize = ci.getUnary().getResultSize() + | + fromSize < toSize and + result = TWiden() + or + fromSize = toSize and + result = TSame() + or + fromSize > toSize and + result = TNarrow() + ) +} + +private predicate bindBool(boolean bool) { + bool = true or + bool = false +} + +private Sign castSign(Sign s, boolean fromSigned, boolean toSigned, CastKind ck) { + result = TZero() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TZero() + or + bindBool(fromSigned) and + bindBool(toSigned) and + ck = TNarrow() + ) + or + result = TPos() and + ( + bindBool(fromSigned) and + bindBool(toSigned) and + s = TPos() + or + bindBool(fromSigned) and + bindBool(toSigned) and + s = TNeg() and + ck = TNarrow() + or + fromSigned = true and + toSigned = false and + s = TNeg() + ) + or + result = TNeg() and + ( + fromSigned = true and + toSigned = true and + s = TNeg() + or + fromSigned = false and + toSigned = true and + s = TPos() and + ck != TWiden() + ) +} + +/** Holds if the sign of `e` is too complicated to determine. */ +private predicate unknownSign(Instruction i) { + // REVIEW: This should probably be a list of the instructions that we _do_ understand, rather than + // the ones we don't understand. Currently, if we try to compute the sign of an instruction that + // we don't understand, and it isn't on this list, we incorrectly compute the sign as "none" + // instead of "+,0,-". + // Even better, we could track the state of each instruction as a power set of {non-negative, + // non-positive, non-zero}, which would mean that the representation of the sign of an unknown + // value would be the empty set. + ( + i instanceof UninitializedInstruction + or + i instanceof InitializeParameterInstruction + or + i instanceof BuiltInOperationInstruction + or + i instanceof CallInstruction + or + i instanceof ChiInstruction + ) +} + +/** + * Holds if `lowerbound` is a lower bound for `bounded`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate lowerBound( + IRGuardCondition comp, Operand lowerbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(lowerbound, compared, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `upperbound` is an upper bound for `bounded` at `pos`. This is restricted + * to only include bounds for which we might determine a sign. + */ +private predicate upperBound( + IRGuardCondition comp, Operand upperbound, Operand bounded, boolean isStrict +) { + exists(int adjustment, Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + ( + isStrict = true and + adjustment = 0 + or + isStrict = false and + adjustment = 1 + ) and + comp.ensuresLt(compared, upperbound, adjustment, bounded.getUse().getBlock(), true) + ) +} + +/** + * Holds if `eqbound` is an equality/inequality for `bounded` at `pos`. This is + * restricted to only include bounds for which we might determine a sign. The + * boolean `isEq` gives the polarity: + * - `isEq = true` : `bounded = eqbound` + * - `isEq = false` : `bounded != eqbound` + */ +private predicate eqBound(IRGuardCondition guard, Operand eqbound, Operand bounded, boolean isEq) { + exists(Operand compared | + valueNumberOfOperand(bounded) = valueNumberOfOperand(compared) and + guard.ensuresEq(compared, eqbound, 0, bounded.getUse().getBlock(), isEq) + ) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be positive in + * order for `v` to be positive. + */ +private predicate posBound(IRGuardCondition comp, Operand bound, Operand op) { + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that needs to be negative in + * order for `v` to be negative. + */ +private predicate negBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + eqBound(comp, bound, op, true) +} + +/** + * Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v` + * can be zero. + */ +private predicate zeroBound(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) or + upperBound(comp, bound, op, _) or + eqBound(comp, bound, op, _) +} + +/** Holds if `bound` allows `v` to be positive at `pos`. */ +private predicate posBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + posBound(comp, bound, op) and TPos() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be negative at `pos`. */ +private predicate negBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + negBound(comp, bound, op) and TNeg() = operandSign(bound) +} + +/** Holds if `bound` allows `v` to be zero at `pos`. */ +private predicate zeroBoundOk(IRGuardCondition comp, Operand bound, Operand op) { + lowerBound(comp, bound, op, _) and TNeg() = operandSign(bound) + or + lowerBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + upperBound(comp, bound, op, _) and TPos() = operandSign(bound) + or + upperBound(comp, bound, op, false) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, true) and TZero() = operandSign(bound) + or + eqBound(comp, bound, op, false) and TZero() != operandSign(bound) +} + +private Sign binaryOpLhsSign(BinaryInstruction i) { result = operandSign(i.getLeftOperand()) } + +private Sign binaryOpRhsSign(BinaryInstruction i) { result = operandSign(i.getRightOperand()) } + +pragma[noinline] +private predicate binaryOpSigns(BinaryInstruction i, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(i) and + rhs = binaryOpRhsSign(i) +} + +private Sign unguardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + not hasGuard(operand, result) +} + +private Sign guardedOperandSign(Operand operand) { + result = instructionSign(operand.getDef()) and + hasGuard(operand, result) +} + +private Sign guardedOperandSignOk(Operand operand) { + result = TPos() and + forex(IRGuardCondition guard, Operand bound | posBound(guard, bound, operand) | + posBoundOk(guard, bound, operand) + ) + or + result = TNeg() and + forex(IRGuardCondition guard, Operand bound | negBound(guard, bound, operand) | + negBoundOk(guard, bound, operand) + ) + or + result = TZero() and + forex(IRGuardCondition guard, Operand bound | zeroBound(guard, bound, operand) | + zeroBoundOk(guard, bound, operand) + ) +} + +/** + * Holds if there is a bound that might restrict whether `v` has the sign `s` + * at `pos`. + */ +private predicate hasGuard(Operand op, Sign s) { + s = TPos() and posBound(_, _, op) + or + s = TNeg() and negBound(_, _, op) + or + s = TZero() and zeroBound(_, _, op) +} + +cached +module SignAnalysisCached { + /** + * Gets a sign that `operand` may have at `pos`, taking guards into account. + */ + cached + Sign operandSign(Operand operand) { + result = unguardedOperandSign(operand) + or + result = guardedOperandSign(operand) and + result = guardedOperandSignOk(operand) + or + // `result` is unconstrained if the definition is inexact. Then any sign is possible. + operand.isDefinitionInexact() + } + + cached + Sign instructionSign(Instruction i) { + result = certainInstructionSign(i) + or + not exists(certainInstructionSign(i)) and + not ( + result = TNeg() and + i.getResultIRType().(IRIntegerType).isUnsigned() + ) and + ( + unknownSign(i) + or + exists(ConvertInstruction ci, Instruction prior, boolean fromSigned, boolean toSigned | + i = ci and + prior = ci.getUnary() and + ( + if ci.getResultIRType().(IRIntegerType).isSigned() + then toSigned = true + else toSigned = false + ) and + ( + if prior.getResultIRType().(IRIntegerType).isSigned() + then fromSigned = true + else fromSigned = false + ) and + result = castSign(operandSign(ci.getAnOperand()), fromSigned, toSigned, getCastKind(ci)) + ) + or + result = operandSign(i.(CopyInstruction).getSourceValueOperand()) + or + result = operandSign(i.(BitComplementInstruction).getAnOperand()).bitnot() + or + result = operandSign(i.(NegateInstruction).getAnOperand()).neg() + or + exists(Sign s1, Sign s2 | binaryOpSigns(i, s1, s2) | + i instanceof AddInstruction and result = s1.add(s2) + or + i instanceof SubInstruction and result = s1.add(s2.neg()) + or + i instanceof MulInstruction and result = s1.mul(s2) + or + i instanceof DivInstruction and result = s1.div(s2) + or + i instanceof RemInstruction and result = s1.rem(s2) + or + i instanceof BitAndInstruction and result = s1.bitand(s2) + or + i instanceof BitOrInstruction and result = s1.bitor(s2) + or + i instanceof BitXorInstruction and result = s1.bitxor(s2) + or + i instanceof ShiftLeftInstruction and result = s1.lshift(s2) + or + i instanceof ShiftRightInstruction and + i.getResultIRType().(IRIntegerType).isSigned() and + result = s1.rshift(s2) + or + i instanceof ShiftRightInstruction and + not i.getResultIRType().(IRIntegerType).isSigned() and + result = s1.urshift(s2) + ) + or + // use hasGuard here? + result = operandSign(i.(PhiInstruction).getAnOperand()) + ) + } +} + +/** Holds if `i` can be positive and cannot be negative. */ +predicate positiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() +} + +/** Holds if `i` at `pos` can be positive at and cannot be negative. */ +predicate positive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() +} + +/** Holds if `i` can be negative and cannot be positive. */ +predicate negativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() +} + +/** Holds if `i` at `pos` can be negative and cannot be positive. */ +predicate negative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() +} + +/** Holds if `i` is strictly positive. */ +predicate strictlyPositiveInstruction(Instruction i) { + instructionSign(i) = TPos() and + not instructionSign(i) = TNeg() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly positive at `pos`. */ +predicate strictlyPositive(Operand op) { + operandSign(op) = TPos() and + not operandSign(op) = TNeg() and + not operandSign(op) = TZero() +} + +/** Holds if `i` is strictly negative. */ +predicate strictlyNegativeInstruction(Instruction i) { + instructionSign(i) = TNeg() and + not instructionSign(i) = TPos() and + not instructionSign(i) = TZero() +} + +/** Holds if `i` is strictly negative at `pos`. */ +predicate strictlyNegative(Operand op) { + operandSign(op) = TNeg() and + not operandSign(op) = TPos() and + not operandSign(op) = TZero() +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll new file mode 100644 index 00000000000..33776bd8105 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/ConstantBitwiseAndExprRange.qll @@ -0,0 +1,90 @@ +private import cpp +private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr +private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils + +/** + * Holds if `e` is a constant or if it is a variable with a constant value + */ +float evaluateConstantExpr(Expr e) { + result = e.getValue().toFloat() + or + exists(SsaDefinition defn, StackVariable sv | + defn.getAUse(sv) = e and + result = defn.getDefiningValue(sv).getValue().toFloat() + ) +} + +/** + * The current implementation for `BitwiseAndExpr` only handles cases where both operands are + * either unsigned or non-negative constants. This class not only covers these cases, but also + * adds support for `&` expressions between a signed integer with a non-negative range and a + * non-negative constant. It also adds support for `&=` for the same set of cases as `&`. + */ +private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr { + ConstantBitwiseAndExprRange() { + exists(Expr l, Expr r | + l = this.(BitwiseAndExpr).getLeftOperand() and + r = this.(BitwiseAndExpr).getRightOperand() + or + l = this.(AssignAndExpr).getLValue() and + r = this.(AssignAndExpr).getRValue() + | + // No operands can be negative constants + not (evaluateConstantExpr(l) < 0 or evaluateConstantExpr(r) < 0) and + // At least one operand must be a non-negative constant + (evaluateConstantExpr(l) >= 0 or evaluateConstantExpr(r) >= 0) + ) + } + + Expr getLeftOperand() { + result = this.(BitwiseAndExpr).getLeftOperand() or + result = this.(AssignAndExpr).getLValue() + } + + Expr getRightOperand() { + result = this.(BitwiseAndExpr).getRightOperand() or + result = this.(AssignAndExpr).getRValue() + } + + override float getLowerBounds() { + // If an operand can have negative values, the lower bound is unconstrained. + // Otherwise, the lower bound is zero. + exists(float lLower, float rLower | + lLower = getFullyConvertedLowerBounds(getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMinVal(this) + or + // This technically results in two lowerBounds when an operand range is negative, but + // that's fine since `exprMinVal(x) <= 0`. We can't use an if statement here without + // non-monotonic recursion issues + result = 0 + ) + ) + } + + override float getUpperBounds() { + // If an operand can have negative values, the upper bound is unconstrained. + // Otherwise, the upper bound is the minimum of the upper bounds of the operands + exists(float lLower, float lUpper, float rLower, float rUpper | + lLower = getFullyConvertedLowerBounds(getLeftOperand()) and + lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and + rLower = getFullyConvertedLowerBounds(getRightOperand()) and + rUpper = getFullyConvertedUpperBounds(getRightOperand()) and + ( + (lLower < 0 or rLower < 0) and + result = exprMaxVal(this) + or + // This technically results in two upperBounds when an operand range is negative, but + // that's fine since `exprMaxVal(b) >= result`. We can't use an if statement here without + // non-monotonic recursion issues + result = rUpper.minimum(lUpper) + ) + ) + } + + override predicate dependsOnChild(Expr child) { + child = getLeftOperand() or child = getRightOperand() + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll new file mode 100644 index 00000000000..ff716d02d6f --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/rangeanalysis/extensions/SubtractSelf.qll @@ -0,0 +1,15 @@ +import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr + +private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr { + SelfSub() { + // Match `x - x` but not `myInt - (unsigned char)myInt`. + getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() = + getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget() + } + + override float getLowerBounds() { result = 0 } + + override float getUpperBounds() { result = 0 } + + override predicate dependsOnChild(Expr child) { none() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll new file mode 100644 index 00000000000..922dadaa20e --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateCleartextWrite.qll @@ -0,0 +1,62 @@ +/** + * Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location. + */ + +import cpp +import semmle.code.cpp.dataflow.TaintTracking +import experimental.semmle.code.cpp.security.PrivateData +import semmle.code.cpp.security.FileWrite +import semmle.code.cpp.security.BufferWrite + +module PrivateCleartextWrite { + /** + * A data flow source for private information flowing unencrypted to an external location. + */ + abstract class Source extends DataFlow::ExprNode { } + + /** + * A data flow sink for private information flowing unencrypted to an external location. + */ + abstract class Sink extends DataFlow::ExprNode { } + + /** + * A sanitizer for private information flowing unencrypted to an external location. + */ + abstract class Sanitizer extends DataFlow::ExprNode { } + + /** A call to any method whose name suggests that it encodes or encrypts the parameter. */ + class ProtectSanitizer extends Sanitizer { + ProtectSanitizer() { + exists(Function m, string s | + this.getExpr().(FunctionCall).getTarget() = m and + m.getName().regexpMatch("(?i).*" + s + ".*") + | + s = "protect" or s = "encode" or s = "encrypt" + ) + } + } + + class WriteConfig extends TaintTracking::Configuration { + WriteConfig() { this = "Write configuration" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + } + + class PrivateDataSource extends Source { + PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr } + } + + class WriteSink extends Sink { + WriteSink() { + exists(FileWrite f, BufferWrite b | + this.asExpr() = f.getASource() + or + this.asExpr() = b.getAChild() + ) + } + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll new file mode 100644 index 00000000000..621e8aad707 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/experimental/semmle/code/cpp/security/PrivateData.qll @@ -0,0 +1,53 @@ +/** + * Provides classes and predicates for identifying private data and functions for security. + * + * 'Private' data in general is anything that would compromise user privacy if exposed. This + * library tries to guess where private data may either be stored in a variable or produced by a + * function. + * + * This library is not concerned with credentials. See `SensitiveActions` for expressions related + * to credentials. + */ + +import cpp + +/** A string for `match` that identifies strings that look like they represent private data. */ +private string privateNames() { + // Inspired by the list on https://cwe.mitre.org/data/definitions/359.html + // Government identifiers, such as Social Security Numbers + result = "%social%security%number%" or + // Contact information, such as home addresses and telephone numbers + result = "%postcode%" or + result = "%zipcode%" or + // result = "%telephone%" or + // Geographic location - where the user is (or was) + result = "%latitude%" or + result = "%longitude%" or + // Financial data - such as credit card numbers, salary, bank accounts, and debts + result = "%creditcard%" or + result = "%salary%" or + result = "%bankaccount%" or + // Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc. + // result = "%email%" or + // result = "%mobile%" or + result = "%employer%" or + // Health - medical conditions, insurance status, prescription records + result = "%medical%" +} + +/** An expression that might contain private data. */ +abstract class PrivateDataExpr extends Expr { } + +/** A functiond call that might produce private data. */ +class PrivateFunctionCall extends PrivateDataExpr, FunctionCall { + PrivateFunctionCall() { + exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames())) + } +} + +/** An access to a variable that might contain private data. */ +class PrivateVariableAccess extends PrivateDataExpr, VariableAccess { + PrivateVariableAccess() { + exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames())) + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/external/ExternalArtifact.qll b/repo-tests/codeql/cpp/ql/lib/external/ExternalArtifact.qll new file mode 100644 index 00000000000..abbc96a7b47 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/external/ExternalArtifact.qll @@ -0,0 +1,60 @@ +/** + * Provides classes for working with external data. + */ + +import cpp + +/** + * An external data item. + */ +class ExternalData extends @externalDataElement { + /** Gets the path of the file this data was loaded from. */ + string getDataPath() { externalData(this, result, _, _) } + + /** + * Gets the path of the file this data was loaded from, with its + * extension replaced by `.ql`. + */ + string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + + /** Gets the number of fields in this data item. */ + int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } + + /** Gets the value of the `i`th field of this data item. */ + string getField(int i) { externalData(this, _, i, result) } + + /** Gets the integer value of the `i`th field of this data item. */ + int getFieldAsInt(int i) { result = getField(i).toInt() } + + /** Gets the floating-point value of the `i`th field of this data item. */ + float getFieldAsFloat(int i) { result = getField(i).toFloat() } + + /** Gets the value of the `i`th field of this data item, interpreted as a date. */ + date getFieldAsDate(int i) { result = getField(i).toDate() } + + /** Gets a textual representation of this data item. */ + string toString() { result = getQueryPath() + ": " + buildTupleString(0) } + + /** Gets a textual representation of this data item, starting with the `n`th field. */ + private string buildTupleString(int n) { + n = getNumFields() - 1 and result = getField(n) + or + n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1) + } +} + +/** + * External data with a location, and a message, as produced by tools that used to produce QLDs. + */ +class DefectExternalData extends ExternalData { + DefectExternalData() { + this.getField(0).regexpMatch("\\w+://.*:[0-9]+:[0-9]+:[0-9]+:[0-9]+$") and + this.getNumFields() = 2 + } + + /** Gets the URL associated with this data item. */ + string getURL() { result = getField(0) } + + /** Gets the message associated with this data item. */ + string getMessage() { result = getField(1) } +} diff --git a/repo-tests/codeql/cpp/ql/lib/qlpack.yml b/repo-tests/codeql/cpp/ql/lib/qlpack.yml new file mode 100644 index 00000000000..d386b0ba2ce --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/qlpack.yml @@ -0,0 +1,7 @@ +name: codeql/cpp-all +version: 0.0.2 +dbscheme: semmlecode.cpp.dbscheme +extractor: cpp +library: true +dependencies: + codeql/cpp-upgrades: 0.0.2 diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ASTConsistency.ql b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ASTConsistency.ql new file mode 100644 index 00000000000..6d3c9f48457 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ASTConsistency.ql @@ -0,0 +1,9 @@ +/** + * @name AST Consistency Check + * @description Performs consistency checks on the Abstract Syntax Tree. This query should have no results. + * @kind table + * @id cpp/ast-consistency-check + */ + +import cpp +import CastConsistency diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/AutogeneratedFile.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/AutogeneratedFile.qll new file mode 100644 index 00000000000..829249e7ded --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/AutogeneratedFile.qll @@ -0,0 +1,133 @@ +/** + * Provides a class and predicate for recognizing files that are likely to have been generated + * automatically. + */ + +import semmle.code.cpp.Comments +import semmle.code.cpp.File +import semmle.code.cpp.Preprocessor + +/** + * Holds if comment `c` indicates that it might be in an auto-generated file, for + * example because it contains the text "auto-generated by". + */ +bindingset[comment] +private predicate autogeneratedComment(string comment) { + // ?s = include newlines in anything (`.`) + // ?i = ignore case + exists(string cond | + cond = + // generated by (not mid-sentence) + "(^ generated by[^a-z])|" + "(! generated by[^a-z])|" + + // generated file + "(generated file)|" + + // file [is/was/has been] generated + "(file( is| was| has been)? generated)|" + + // changes made in this file will be lost + "(changes made in this file will be lost)|" + + // do not edit/modify (not mid-sentence) + "(^ do(n't|nt| not) (hand-?)?(edit|modify))|" + + "(! do(n't|nt| not) (hand-?)?(edit|modify))|" + + // do not edit/modify + generated + "(do(n't|nt| not) (hand-?)?(edit|modify).*generated)|" + + "(generated.*do(n't|nt| not) (hand-?)?(edit|modify))" and + comment + .regexpMatch("(?si).*(" + + // replace `generated` with a regexp that also catches things like + // `auto-generated`. + cond.replaceAll("generated", "(auto[\\w-]*[\\s/\\*\\r\\n]*)?generated") + // replace `!` with a regexp for end-of-sentence / separator characters. + .replaceAll("!", "[\\.\\?\\!\\-\\;\\,]") + // replace ` ` with a regexp for one or more whitespace characters + // (including newlines and `/*`). + .replaceAll(" ", "[\\s/\\*\\r\\n]+") + ").*") + ) +} + +/** + * Holds if the file contains `#line` pragmas that refer to a different file. + * For example, in `parser.c` a pragma `#line 1 "parser.rl"`. + * Such pragmas usually indicate that the file was automatically generated. + */ +predicate hasPragmaDifferentFile(File f) { + exists(PreprocessorLine pl, string s | + pl.getFile() = f and + pl.getHead().splitAt(" ", 1) = s and + /* Zero index is line number, one index is file reference */ + not "\"" + f.getAbsolutePath() + "\"" = s and + not "\"" + f.getRelativePath() + "\"" = s and + not "\"" + f.getBaseName() + "\"" = s + ) +} + +/** + * The line where the first comment in file `f` begins (maximum of 5). This allows + * us to skip past any preprocessor logic or similar code before the first comment. + */ +private int fileFirstComment(File f) { + result = + min(int line | + exists(Comment c | + c.getFile() = f and + c.getLocation().getStartLine() = line and + line < 5 + ) + ).minimum(5) +} + +/** + * The line where the initial comments of file `f` end. This is just before the + * first bit of code, excluding anything skipped over by `fileFirstComment`. + */ +private int fileHeaderLimit(File f) { + exists(int fc | + fc = fileFirstComment(f) and + result = + min(int line | + exists(DeclarationEntry de, Location l | + l = de.getLocation() and + l.getFile() = f and + line = l.getStartLine() - 1 and + line > fc + ) + or + exists(PreprocessorDirective pd, Location l | + l = pd.getLocation() and + l.getFile() = f and + line = l.getStartLine() - 1 and + line > fc + ) + or + exists(NamespaceDeclarationEntry nde, Location l | + l = nde.getLocation() and + l.getFile() = f and + line = l.getStartLine() - 1 and + line > fc + ) + or + line = f.getMetrics().getNumberOfLines() + ) + ) +} + +/** + * Holds if the file is probably an autogenerated file. + * + * A file is probably autogenerated if either of the following heuristics + * hold: + * 1. There is a comment in the start of the file that matches + * 'autogenerated', 'generated by', or a similar phrase. + * 2. There is a `#line` directive referring to a different file. + */ +class AutogeneratedFile extends File { + cached + AutogeneratedFile() { + autogeneratedComment(strictconcat(Comment c | + c.getFile() = this and + c.getLocation().getStartLine() <= fileHeaderLimit(this) + | + c.getContents() order by c.getLocation().getStartLine() + )) or + hasPragmaDifferentFile(this) + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Class.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Class.qll new file mode 100644 index 00000000000..987ec7ffa3d --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Class.qll @@ -0,0 +1,1171 @@ +/** + * Provides classes representing C++ classes, including structs, unions, and template classes. + */ + +import semmle.code.cpp.Type +import semmle.code.cpp.UserType +import semmle.code.cpp.metrics.MetricClass +import semmle.code.cpp.Linkage +private import semmle.code.cpp.internal.ResolveClass + +/** + * A class type [N4140 9]. + * + * While this does include types declared with the `class` keyword, it also + * includes types declared with the `struct` and `union` keywords. For example, + * the types `MyClass`, `MyStruct` and `MyUnion` in: + * ``` + * class MyClass { + * public: + * MyClass(); + * }; + * + * struct MyStruct { + * int x, y, z; + * }; + * + * union MyUnion { + * int i; + * float f; + * }; + * ``` + */ +class Class extends UserType { + Class() { isClass(underlyingElement(this)) } + + override string getAPrimaryQlClass() { result = "Class" } + + /** Gets a child declaration of this class, struct or union. */ + override Declaration getADeclaration() { result = this.getAMember() } + + /** Gets a type declared in this class, struct or union. */ + UserType getANestedType() { result = this.getAMember() } + + /** + * Gets a function declared in this class, struct or union. + * For template member functions, results include both the template + * and the instantiations of that template. If you only want the + * template, then use `getACanonicalMemberFunction()` instead. + */ + MemberFunction getAMemberFunction() { result = this.getAMember() } + + /** + * Gets a function declared in this class, struct or union. + * For template member functions, results include only the template. + * If you also want instantiations of the template, then use + * `getAMemberFunction()` instead. + */ + MemberFunction getACanonicalMemberFunction() { result = this.getACanonicalMember() } + + /** + * Gets a member variable declared in this class, struct or union. + * For template member variables, results include both the template + * and the instantiations of that template. If you only want the + * template, then use `getACanonicalMemberVariable()` instead. + */ + MemberVariable getAMemberVariable() { result = this.getAMember() } + + /** + * Gets a member variable declared in this class, struct or union. + * For template member variables, results include only the template. + * If you also want instantiations of the template, then use + * `getAMemberVariable()` instead. + */ + MemberVariable getACanonicalMemberVariable() { result = this.getAMember() } + + /** + * Gets a member declared in this class, struct or union. + * For template members, this may be either the template or an instantiation + * of that template. If you only want the template, see + * `getACanonicalMember()`. + */ + Declaration getAMember() { result = this.getAMember(_) } + + /** + * Gets a member declared in this class, struct or union. + * If you also want template instantiations of results, see + * `getAMember()`. + */ + Declaration getACanonicalMember() { result = this.getCanonicalMember(_) } + + /** + * Gets the (zero-based) `index`th member declared in this class, struct + * or union. + * If you also want template instantiations of results, see + * `getAMember(int)`. + */ + Declaration getCanonicalMember(int index) { + member(underlyingElement(this), index, unresolveElement(result)) + } + + /** + * Gets the (zero-based) `index`th canonical member declared in this + * class, struct or union. If that member is a template, all instantiations + * of that template. If you only want the canonical member, see + * `getCanonicalMember(int)`. + */ + Declaration getAMember(int index) { + result = this.getCanonicalMember(index) or + result = this.getCanonicalMember(index).(TemplateClass).getAnInstantiation() or + result = this.getCanonicalMember(index).(TemplateFunction).getAnInstantiation() or + result = this.getCanonicalMember(index).(TemplateVariable).getAnInstantiation() + } + + /** + * DEPRECATED: Use `getCanonicalMember(int)` or `getAMember(int)` instead. + * Gets the `index`th member of this class. + */ + deprecated Declaration getMember(int index) { + member(underlyingElement(this), index, unresolveElement(result)) + } + + /** + * DEPRECATED: As this includes a somewhat arbitrary number of + * template instantiations, it is unlikely to do what + * you need. + * Gets the number of members that this class has. This includes both + * templates that are in this class, and instantiations of those + * templates. + */ + deprecated int getNumMember() { result = count(this.getAMember()) } + + /** + * Gets a private member declared in this class, struct or union. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAPrivateCanonicalMember()`. + */ + Declaration getAPrivateMember() { result = this.getAMember() and result.hasSpecifier("private") } + + /** + * Gets a private canonical member declared in this class, struct or union. + * If you also want template instantiations of results, see + * `getAPrivateMember()`. + */ + Declaration getAPrivateCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("private") + } + + /** + * Gets a protected member declared in this class, struct or union. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAProtectedCanonicalMember()`. + */ + Declaration getAProtectedMember() { + result = this.getAMember() and result.hasSpecifier("protected") + } + + /** + * Gets a protected canonical member declared in this class, struct or union. + * If you also want template instantiations of results, see + * `getAProtectedMember()`. + */ + Declaration getAProtectedCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("protected") + } + + /** + * Gets a public member declared in this class, struct or union. + * For template members, this may be either the template or an + * instantiation of that template. For just the template, use + * `getAPublicCanonicalMember()`. + */ + Declaration getAPublicMember() { result = this.getAMember() and result.hasSpecifier("public") } + + /** + * Gets a public canonical member declared in this class, struct or union. + * If you also want template instantiations of results, see + * `getAPublicMember()`. + */ + Declaration getAPublicCanonicalMember() { + result = this.getACanonicalMember() and result.hasSpecifier("public") + } + + /** Gets a static member declared in this class, struct or union. */ + Declaration getAStaticMember() { result = this.getAMember() and result.isStatic() } + + /** Gets a field of this class, struct or union. */ + Field getAField() { result = this.getAMemberVariable() } + + /** Gets a constructor of this class, struct or union. */ + Constructor getAConstructor() { result = this.getAMemberFunction() } + + /** Holds if this class, struct or union has a constructor. */ + predicate hasConstructor() { exists(this.getAConstructor()) } + + /** + * Holds if this class has a copy constructor that is either explicitly + * declared (though possibly `= delete`) or is auto-generated, non-trivial + * and called from somewhere. + * + * DEPRECATED: There is more than one reasonable definition of what it means + * to have a copy constructor, and we do not want to promote one particular + * definition by naming it with this predicate. Having a copy constructor + * could mean that such a member is declared or defined in the source or that + * it is callable by a particular caller. For C++11, there's also a question + * of whether to include members that are defaulted or deleted. + */ + deprecated predicate hasCopyConstructor() { + exists(CopyConstructor cc | cc = this.getAMemberFunction()) + } + + /** + * Holds if this class has a copy assignment operator that is either + * explicitly declared (though possibly `= delete`) or is auto-generated, + * non-trivial and called from somewhere. + * + * DEPRECATED: There is more than one reasonable definition of what it means + * to have a copy assignment operator, and we do not want to promote one + * particular definition by naming it with this predicate. Having a copy + * assignment operator could mean that such a member is declared or defined + * in the source or that it is callable by a particular caller. For C++11, + * there's also a question of whether to include members that are defaulted + * or deleted. + */ + deprecated predicate hasCopyAssignmentOperator() { + exists(CopyAssignmentOperator coa | coa = this.getAMemberFunction()) + } + + /** + * Like accessOfBaseMember but returns multiple results if there are multiple + * paths to `base` through the inheritance graph. + */ + private AccessSpecifier accessOfBaseMemberMulti(Class base, AccessSpecifier fieldInBase) { + this = base and result = fieldInBase + or + exists(ClassDerivation cd | cd.getBaseClass() = base | + result = + this.accessOfBaseMemberMulti(cd.getDerivedClass(), + fieldInBase.accessInDirectDerived(cd.getASpecifier().(AccessSpecifier))) + ) + } + + /** + * Gets the access specifier, if any, that a hypothetical member of `base` + * would have when viewed as a member of `this`, given that this member had + * access specifier `fieldInBase`. Encodes the rules of C++14 11.2/1 and + * 11.6/1, except that this predicate includes the case of `base` = `this`. + */ + AccessSpecifier accessOfBaseMember(Class base, AccessSpecifier fieldInBase) { + // If there are multiple paths through the inheritance graph, we take the + // most permissive one (C++14 11.6/1). This implementation relies on the + // alphabetical order of "private", "protected", "public". + result.hasName(max(this.accessOfBaseMemberMulti(base, fieldInBase).getName())) + } + + /** + * Gets the access specifier, if any, that `member` has when viewed as a + * member of `this`, where `member` may come from a base class of `this`. + * Encodes the rules of C++14 11.2/1 and 11.6/1, except that this predicate + * includes the case of `base` = `this`. + */ + AccessSpecifier accessOfBaseMember(Declaration member) { + result = + this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier)) + } + + /** + * DEPRECATED: name changed to `hasImplicitCopyConstructor` to reflect that + * `= default` members are no longer included. + */ + deprecated predicate hasGeneratedCopyConstructor() { hasImplicitCopyConstructor() } + + /** + * DEPRECATED: name changed to `hasImplicitCopyAssignmentOperator` to + * reflect that `= default` members are no longer included. + */ + deprecated predicate hasGeneratedCopyAssignmentOperator() { hasImplicitCopyConstructor() } + + /** + * Holds if this class, struct or union has an implicitly-declared copy + * constructor that is not _deleted_. This predicate is more accurate than + * checking if this class, struct or union has a `CopyConstructor cc` where + * `cc.isCompilerGenerated()` since such a `CopyConstructor` may not exist + * in the database if (1) it is never called or (2) it is _trivial_, meaning + * that it is equivalent to `memcpy`. + */ + predicate hasImplicitCopyConstructor() { + not this.implicitCopyConstructorDeleted() and + forall(CopyConstructor cc | cc = this.getAMemberFunction() | + cc.isCompilerGenerated() and not cc.isDeleted() + ) + } + + /** + * Holds if this class, struct or union has an implicitly-declared copy + * assignment operator that is not _deleted_. This predicate is more + * accurate than checking if this class, struct or union has a + * `CopyAssignmentOperator ca` where `ca.isCompilerGenerated()` since such a + * `CopyAssignmentOperator` may not exist in the database if (1) it is never + * called or (2) it is _trivial_, meaning that it is equivalent to `memcpy`. + */ + predicate hasImplicitCopyAssignmentOperator() { + not this.implicitCopyAssignmentOperatorDeleted() and + forall(CopyAssignmentOperator ca | ca = this.getAMemberFunction() | + ca.isCompilerGenerated() and not ca.isDeleted() + ) + } + + /** + * Holds if the compiler would be unable to generate a copy constructor for + * this class, struct or union. This predicate implements the rules listed + * here: + * http://en.cppreference.com/w/cpp/language/copy_constructor#Deleted_implicitly-declared_copy_constructor + */ + predicate implicitCopyConstructorDeleted() { + // - T has non-static data members that cannot be copied (have deleted, + // inaccessible, or ambiguous copy constructors); + exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() | + // Note: Overload resolution is not implemented -- all copy + // constructors are considered equal. + this.cannotAccessCopyConstructorOnAny(t.(Class)) + ) + or + // - T has direct or virtual base class that cannot be copied (has deleted, + // inaccessible, or ambiguous copy constructors); + exists(Class c | c = this.getADirectOrVirtualBase() | + // Note: Overload resolution is not implemented -- all copy + // constructors are considered equal. + this.cannotAccessCopyConstructorOnThis(c) + ) + or + // - T has direct or virtual base class with a deleted or inaccessible + // destructor; + exists(Class base | base = this.getADirectOrVirtualBase() | + this.cannotAccessDestructor(base, this) + ) + or + // - T has a user-defined move constructor or move assignment operator; + exists(MoveConstructor mc | mc = this.getAMemberFunction() | not mc.isCompilerGenerated()) + or + exists(MoveAssignmentOperator ma | ma = this.getAMemberFunction() | + not ma.isCompilerGenerated() + ) + or + // - T is a union and has a variant member with non-trivial copy + // constructor (since C++11) + none() // Not implemented + or + // - T has a data member of rvalue reference type. + exists(Type t | t = this.getAFieldSubobjectType() | t instanceof RValueReferenceType) + } + + /** + * Holds if the compiler would be unable to generate a copy assignment + * operator for this class, struct or union. This predicate implements the + * rules listed here: + * http://en.cppreference.com/w/cpp/language/copy_assignment#Deleted_implicitly-declared_copy_assignment_operator + */ + predicate implicitCopyAssignmentOperatorDeleted() { + // - T has a user-declared move constructor; + exists(MoveConstructor mc | mc = this.getAMemberFunction() | not mc.isCompilerGenerated()) + or + // - T has a user-declared move assignment operator. + exists(MoveAssignmentOperator ma | ma = this.getAMemberFunction() | + not ma.isCompilerGenerated() + ) + or + // - T has a non-static data member of non-class type (or array thereof) + // that is const; + exists(Type t | t = this.getAFieldSubobjectType() | + // The rule for this case refers only to non-class types only, but our + // implementation extends it to cover class types too. Class types are + // supposed to be covered by the rule below on data members that + // cannot be copy-assigned. Copy-assigning a const class-typed member + // would call an overload of type + // `const C& operator=(const C&) const;`. Such an overload is unlikely + // to exist because it contradicts the intention of "const": it allows + // assigning to a const object. But since we have not implemented the + // ability to distinguish between overloads, we cannot distinguish that + // overload from the ordinary `C& operator=(const C&);`. Instead, we + // require class types to be non-const in this clause. + /* not t instanceof Class and */ t.isConst() + ) + or + // - T has a non-static data member of a reference type; + exists(Type t | t = this.getAFieldSubobjectType() | t instanceof ReferenceType) + or + // - T has a non-static data member or a direct or virtual base class that + // cannot be copy-assigned (overload resolution for the copy assignment + // fails, or selects a deleted or inaccessible function); + exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() | + // Note: Overload resolution is not implemented -- all copy assignment + // operators are considered equal. + this.cannotAccessCopyAssignmentOperatorOnAny(t.(Class)) + ) + or + exists(Class c | c = this.getADirectOrVirtualBase() | + // Note: Overload resolution is not implemented -- all copy assignment + // operators are considered equal. + this.cannotAccessCopyAssignmentOperatorOnThis(c) + ) + // - T is a union-like class, and has a variant member whose corresponding + // assignment operator is non-trivial. + // Not implemented + } + + /** Gets the destructor of this class, struct or union, if any. */ + Destructor getDestructor() { result = this.getAMemberFunction() } + + /** Holds if this class, struct or union has a destructor. */ + predicate hasDestructor() { exists(this.getDestructor()) } + + /** + * Holds if this class, struct or union is a POD (Plain Old Data) class + * [N4140 9(10)]. + * + * The definition of POD changed between C++03 and C++11, so whether + * a class is POD can depend on which version of the language it was + * compiled for. For this reason, the `is_pod_class` predicate is + * generated by the extractor. + */ + predicate isPOD() { is_pod_class(underlyingElement(this)) } + + /** + * Holds if this class, struct or union is a standard-layout class + * [N4140 9(7)]. Also holds for structs in C programs. + */ + predicate isStandardLayout() { is_standard_layout_class(underlyingElement(this)) } + + /** + * Holds if this class/struct is abstract, in other words whether + * it declares one or more pure virtual member functions. + */ + predicate isAbstract() { this.getAMemberFunction() instanceof PureVirtualFunction } + + /** Gets a direct base class/struct of this class/struct [N4140 10]. */ + Class getABaseClass() { this.getADerivation().getBaseClass() = result } + + /** Gets a class/struct that is directly derived from this class/struct [N4140 10]. */ + Class getADerivedClass() { result.getABaseClass() = this } + + /** Holds if this class/struct derives directly from that. */ + predicate derivesFrom(Class that) { this.getABaseClass() = that } + + override predicate refersToDirectly(Type t) { + t = this.getATemplateArgument() or + this.isConstructedFrom(t) + } + + /** + * Gets a class derivation of this class/struct, for example the + * `public B` in the following code: + * ``` + * class D : public B { + * ... + * }; + * ``` + */ + ClassDerivation getADerivation() { + exists(ClassDerivation d | d.getDerivedClass() = this and d = result) + } + + /** + * Gets class derivation number `index` of this class/struct, for example the + * `public B` is derivation 1 in the following code: + * ``` + * class D : public A, public B, public C { + * ... + * }; + * ``` + */ + ClassDerivation getDerivation(int index) { + exists(ClassDerivation d | d.getDerivedClass() = this and d.getIndex() = index and d = result) + } + + /** + * Gets the byte offset within `this` of each base class subobject of type + * `baseClass`, or zero if `baseClass` and `this` are the same type. Both + * direct and indirect base classes are included. + * Does not hold for base class subobjects for virtual base classes, nor does + * it hold for further base class subobjects of virtual base classes. + */ + private int getANonVirtualBaseClassByteOffset(Class baseClass) { + baseClass = this and result = 0 // `baseClass` is the most-derived type + or + exists(ClassDerivation cd | + // Add the offset of the direct base class and the offset of `baseClass` + // within that direct base class. + cd = getADerivation() and + result = cd.getBaseClass().getANonVirtualBaseClassByteOffset(baseClass) + cd.getByteOffset() + ) + } + + /** + * Gets the byte offset within `this` of each base class subobject of type + * `baseClass`, or zero if `baseClass` and `this` are the same type. Both + * direct and indirect base classes are included. + * Note that for virtual base classes, and non-virtual base classes thereof, + * this predicate assumes that `this` is the type of the complete most-derived + * object. + */ + int getABaseClassByteOffset(Class baseClass) { + // Handle the non-virtual case. + result = getANonVirtualBaseClassByteOffset(baseClass) + or + exists(Class virtualBaseClass, int virtualBaseOffset, int offsetFromVirtualBase | + // Look for the base class as a non-virtual base of a direct or indirect + // virtual base, adding the two offsets. + getVirtualBaseClassByteOffset(virtualBaseClass) = virtualBaseOffset and + offsetFromVirtualBase = virtualBaseClass.getANonVirtualBaseClassByteOffset(baseClass) and + result = virtualBaseOffset + offsetFromVirtualBase + ) + } + + /** + * Holds if this class/struct has a virtual class derivation, for + * example the `virtual public B` in the following code: + * ``` + * class D : virtual public B { + * ... + * }; + * ``` + */ + predicate hasVirtualBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("virtual") + ) + } + + /** + * Gets the byte offset of virtual base class subobject `base` within a + * most-derived object of class `this`. The virtual base can be a direct or + * indirect virtual base of `this`. Does not hold if `this` is an + * uninstantiated template. + * See `ClassDerivation.getByteOffset` for offsets of non-virtual base + * classes. + */ + int getVirtualBaseClassByteOffset(Class base) { + virtual_base_offsets(underlyingElement(this), unresolveElement(base), result) + } + + /** + * Holds if this class/struct has a private class derivation, for + * example the `private B` in the following code: + * ``` + * class D : private B { + * ... + * }; + * ``` + */ + predicate hasPrivateBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("private") + ) + } + + /** + * Holds if this class/struct has a public class derivation, for + * example the `public B` in the following code: + * ``` + * class D : public B { + * ... + * }; + * ``` + */ + predicate hasPublicBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("public") + ) + } + + /** + * Holds if this class/struct has a protected class derivation, for + * example the `protected B` in the following code: + * ``` + * class D : protected B { + * ... + * }; + * ``` + */ + predicate hasProtectedBaseClass(Class base) { + exists(ClassDerivation cd | + this.getADerivation() = cd and + cd.getBaseClass() = base and + cd.hasSpecifier("protected") + ) + } + + /** Gets the metric class associated with this class, struct or union. */ + MetricClass getMetrics() { result = this } + + /** Gets a friend declaration in this class, struct or union. */ + FriendDecl getAFriendDecl() { result.getDeclaringClass() = this } + + override string explain() { result = "class " + this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts + + /** + * Gets the alignment of this type in bytes (on the machine where facts were + * extracted). + */ + override int getAlignment() { usertypesize(underlyingElement(this), _, result) } + + /** + * Holds if this class, struct or union is constructed from another class as + * a result of template instantiation. It originates either from a class + * template or from a class nested in a class template. + */ + predicate isConstructedFrom(Class c) { + class_instantiation(underlyingElement(this), unresolveElement(c)) + } + + /** + * Holds if this class/struct is polymorphic (has a virtual function, or + * inherits one). + */ + predicate isPolymorphic() { + exists(MemberFunction f | f.getDeclaringType() = getABaseClass*() and f.isVirtual()) + } + + override predicate involvesTemplateParameter() { + getATemplateArgument().(Type).involvesTemplateParameter() + } + + /** Holds if this class, struct or union was declared 'final'. */ + predicate isFinal() { usertype_final(underlyingElement(this)) } + + /** Gets a link target which references this class, struct or union. */ + LinkTarget getALinkTarget() { this = result.getAClass() } + + /** + * Gets the UUID that associated with this class, struct or union via the + * `__declspec(uuid)` attribute. + * + * Regardless of the format of the UUID string in source code, the returned + * value is normalized to the standard "registry format", without braces, and + * using lowercase letters (e.g. "01234567-89ab-cdef-0123-456789abcdef"). + */ + string getUuid() { usertype_uuid(underlyingElement(this), result) } + + private Type getAFieldSubobjectType() { + result = stripArrayTypes(this.getAField().getUnderlyingType()) + } + + private Class getADirectOrVirtualBase() { + // `result` is a direct base of `this` + result.getADerivedClass() = this + or + // `result` is an indirect virtual base of `this`. The case where `result` + // is a direct virtual base of `this` is included in the above clause, and + // therefore we can use "+"-closure instead of "*"-closure here. + result.(VirtualBaseClass).getAVirtuallyDerivedClass().getADerivedClass+() = this + } + + /** + * Holds if `this` can NOT access the destructor of class `c` on an object of + * type `objectClass`. Note: this implementation is incomplete but will be + * correct in most cases; it errs on the side of claiming that the destructor + * is accessible. + */ + pragma[inline] + private predicate cannotAccessDestructor(Class c, Class objectClass) { + // The destructor in our db, if any, is accessible. If there is no + // destructor in our db, it usually means that there is a default + // public one. + exists(Destructor d | d = c.getAMemberFunction() | not this.canAccessMember(d, objectClass)) + // The extractor doesn't seem to support the case of a deleted destructor, + // so we ignore that. It is very much a corner case. + // To implement this properly, there should be a predicate about whether + // the implicit destructor is deleted, similar to + // `implicitCopyConstructorDeleted`. See + // http://en.cppreference.com/w/cpp/language/destructor#Deleted_implicitly-declared_destructor + } + + private predicate cannotAccessCopyConstructorOnThis(Class c) { + this.cannotAccessCopyConstructor(c, this) + } + + private predicate cannotAccessCopyConstructorOnAny(Class c) { + this.cannotAccessCopyConstructor(c, c) + } + + /** + * Holds if `this` can NOT access the copy constructor of class `c` in order + * to construct an object of class `objectClass`. In practice, set + * `objectClass` to `this` when access-checking a base subobject + * initialization (like `class D : C { D(D& that) : C(that) { ... } }`). Set + * `objectClass` to `c` for any other purpose (like `C y = x;`). + */ + pragma[inline] + private predicate cannotAccessCopyConstructor(Class c, Class objectClass) { + // Pseudocode: + // if c has CopyConstructor cc + // then this.cannotAccess(cc) + // else this.implicitCopyConstructorDeleted() + exists(CopyConstructor cc | cc = c.getAMemberFunction() | + not this.canAccessMember(cc, objectClass) + ) + or + not exists(CopyConstructor cc | cc = c.getAMemberFunction() and not cc.isDeleted()) and + c.implicitCopyConstructorDeleted() // mutual recursion here + // no access check in this case since the implicit member is always + // public. + } + + private predicate cannotAccessCopyAssignmentOperatorOnThis(Class c) { + this.cannotAccessCopyAssignmentOperator(c, this) + } + + private predicate cannotAccessCopyAssignmentOperatorOnAny(Class c) { + this.cannotAccessCopyAssignmentOperator(c, c) + } + + /** + * Holds if `this` can NOT access the copy assignment operator of class `c` on + * an object of type `objectClass`, where `objectClass` is derived from or + * equal to `c`. That is, whether the call `x.C::operator=(...)` is forbidden + * when the type of `x` is `objectClass`, and `c` has the name `C`. + */ + pragma[inline] + private predicate cannotAccessCopyAssignmentOperator(Class c, Class objectClass) { + // Pseudocode: + // if c has CopyAssignmentOperator ca + // then this.cannotAccess(ca) + // else this.implicitCopyAssignmentOperatorDeleted() + exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() | + not this.canAccessMember(ca, objectClass) + ) + or + not exists(CopyAssignmentOperator ca | ca = c.getAMemberFunction() and not ca.isDeleted()) and + c.implicitCopyAssignmentOperatorDeleted() // mutual recursion here + // no access check in this case since the implicit member is always + // public. + } +} + +/** + * A class derivation, for example the `public B` in the following code: + * ``` + * class D : public B { + * ... + * }; + * ``` + */ +class ClassDerivation extends Locatable, @derivation { + /** + * Gets the class/struct from which we are actually deriving, resolving a + * typedef if necessary. For example, the base class in the following code + * would be `B`: + * ``` + * struct B { + * }; + * + * typedef B T; + * + * struct D : T { + * }; + * ``` + */ + Class getBaseClass() { result = getBaseType().getUnderlyingType() } + + override string getAPrimaryQlClass() { result = "ClassDerivation" } + + /** + * Gets the type from which we are deriving, without resolving any + * typedef. For example, the base type in the following code would be `T`: + * ``` + * struct B { + * }; + * + * typedef B T; + * + * struct D : T { + * }; + * ``` + */ + Type getBaseType() { derivations(underlyingElement(this), _, _, unresolveElement(result), _) } + + /** + * Gets the class/struct that is doing the deriving. For example, the derived + * class in the following code would be `D`: + * ``` + * struct B { + * }; + * + * struct D : B { + * }; + * ``` + */ + Class getDerivedClass() { + derivations(underlyingElement(this), unresolveElement(result), _, _, _) + } + + /** + * Gets the index of the derivation in the derivation list for the + * derived class/struct (indexed from 0). For example, the index of the + * derivation of `B2` in the following code would be `1`: + * ``` + * struct D : B1, B2 { + * ... + * }; + * ``` + */ + int getIndex() { derivations(underlyingElement(this), _, result, _, _) } + + /** Gets a specifier (for example `public`) applied to the derivation. */ + Specifier getASpecifier() { derspecifiers(underlyingElement(this), unresolveElement(result)) } + + /** Holds if the derivation has specifier `s`. */ + predicate hasSpecifier(string s) { this.getASpecifier().hasName(s) } + + /** Holds if the derivation is for a virtual base class. */ + predicate isVirtual() { hasSpecifier("virtual") } + + /** Gets the location of the derivation. */ + override Location getLocation() { derivations(underlyingElement(this), _, _, _, result) } + + /** + * Gets the byte offset of the base class subobject relative to the start of + * the derived class object. Only holds for non-virtual bases, since the + * offset of a virtual base class is not a constant. Does not hold if the + * derived class is an uninstantiated template. + * See `Class.getVirtualBaseClassByteOffset` for offsets of virtual base + * classes. + */ + int getByteOffset() { direct_base_offsets(underlyingElement(this), result) } + + override string toString() { result = "derivation" } +} + +/** + * A class, struct or union that is directly enclosed by a function. For example + * the `struct` in the following code is a `LocalClass`: + * ``` + * void myFunction() { + * struct { int x; int y; } vec = { 1, 2 }; + * }; + * ``` + */ +class LocalClass extends Class { + LocalClass() { isLocal() } + + override string getAPrimaryQlClass() { not this instanceof LocalStruct and result = "LocalClass" } + + override Function getEnclosingAccessHolder() { result = this.getEnclosingFunction() } +} + +/** + * A class, struct or union that is declared within another class. For example + * the struct `PairT` in the following code is a nested class: + * ``` + * template + * class MyTemplateClass { + * public: + * struct PairT { + * T first, second; + * }; + * }; + * ``` + */ +class NestedClass extends Class { + NestedClass() { this.isMember() } + + override string getAPrimaryQlClass() { + not this instanceof NestedStruct and result = "NestedClass" + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is public. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } +} + +/** + * An "abstract class", in other words a class/struct that contains at least one + * pure virtual function. + */ +class AbstractClass extends Class { + AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) } + + override string getAPrimaryQlClass() { result = "AbstractClass" } +} + +/** + * A class template (this class also finds partial specializations + * of class templates). For example in the following code there is a + * `MyTemplateClass` template: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * ``` + * Note that this does not include template instantiations, and full + * specializations. See `ClassTemplateInstantiation` and + * `FullClassTemplateSpecialization`. + */ +class TemplateClass extends Class { + TemplateClass() { usertypes(underlyingElement(this), _, 6) } + + /** + * Gets a class instantiated from this template. + * + * For example for `MyTemplateClass` in the following code, the results are + * `MyTemplateClass` and `MyTemplateClass`: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * + * MyTemplateClass instance; + * + * MyTemplateClass instance; + * ``` + */ + Class getAnInstantiation() { + result.isConstructedFrom(this) and + exists(result.getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "TemplateClass" } +} + +/** + * A class that is an instantiation of a template. For example in the following + * code there is a `MyTemplateClass` instantiation: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * + * MyTemplateClass instance; + * ``` + * For the `MyTemplateClass` template itself, see `TemplateClass`. + */ +class ClassTemplateInstantiation extends Class { + TemplateClass tc; + + ClassTemplateInstantiation() { tc.getAnInstantiation() = this } + + override string getAPrimaryQlClass() { result = "ClassTemplateInstantiation" } + + /** + * Gets the class template from which this instantiation was instantiated. + * + * For example for `MyTemplateClass` in the following code, the result is + * `MyTemplateClass`: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * + * MyTemplateClass instance; + * ``` + */ + TemplateClass getTemplate() { result = tc } +} + +/** + * A specialization of a class template (this may be a full or partial template + * specialization - see `FullClassTemplateSpecialization` and + * `PartialClassTemplateSpecialization`). + */ +class ClassTemplateSpecialization extends Class { + ClassTemplateSpecialization() { + isFullClassTemplateSpecialization(this) or + isPartialClassTemplateSpecialization(this) + } + + /** + * Gets the primary template for the specialization, for example on + * `S`, the result is `S`. + */ + TemplateClass getPrimaryTemplate() { + // Ignoring template arguments, the primary template has the same name + // as each of its specializations. + result.getSimpleName() = getSimpleName() and + // It is in the same namespace as its specializations. + result.getNamespace() = getNamespace() and + // It is distinguished by the fact that each of its template arguments + // is a distinct template parameter. + count(TemplateParameter tp | tp = result.getATemplateArgument()) = + count(int i | exists(result.getTemplateArgument(i))) + } + + override string getAPrimaryQlClass() { result = "ClassTemplateSpecialization" } +} + +private predicate isFullClassTemplateSpecialization(Class c) { + // This class has template arguments, but none of them involves a template parameter. + exists(c.getATemplateArgument()) and + not exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and + // This class does not have any instantiations. + not exists(c.(TemplateClass).getAnInstantiation()) and + // This class is not an instantiation of a class template. + not c instanceof ClassTemplateInstantiation +} + +/** + * A full specialization of a class template. For example `MyTemplateClass` + * in the following code is a `FullClassTemplateSpecialization`: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * + * template<> + * class MyTemplateClass { + * ... + * }; + * ``` + */ +class FullClassTemplateSpecialization extends ClassTemplateSpecialization { + FullClassTemplateSpecialization() { isFullClassTemplateSpecialization(this) } + + override string getAPrimaryQlClass() { result = "FullClassTemplateSpecialization" } +} + +private predicate isPartialClassTemplateSpecialization(Class c) { + /* + * (a) At least one of this class's template arguments involves a + * template parameter in some respect, for example T, T*, etc. + * + * (b) It is not the case that the n template arguments of this class + * are a set of n distinct template parameters. + * + * template class X {}; // class template + * template class X {}; // partial class template specialization + * template class X {}; // partial class template specialization + * template class Y {}; // class template + * template class Y {}; // partial class template specialization + */ + + exists(Type ta | ta = c.getATemplateArgument() and ta.involvesTemplateParameter()) and + count(TemplateParameter tp | tp = c.getATemplateArgument()) != + count(int i | exists(c.getTemplateArgument(i))) +} + +/** + * A partial specialization of a class template. For example `MyTemplateClass` + * in the following code is a `PartialClassTemplateSpecialization`: + * ``` + * template + * class MyTemplateClass { + * ... + * }; + * + * template + * class MyTemplateClass { + * ... + * }; + * ``` + */ +class PartialClassTemplateSpecialization extends ClassTemplateSpecialization { + PartialClassTemplateSpecialization() { isPartialClassTemplateSpecialization(this) } + + override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" } +} + +/** + * An "interface" is a class that only contains pure virtual functions (and contains + * at least one such function). For example: + * ``` + * class MyInterfaceClass { + * public: + * virtual void myMethod1() = 0; + * virtual void myMethod2() = 0; + * }; + * ``` + * + * DEPRECATED: This class is considered to be too specific for general usage. + */ +deprecated class Interface extends Class { + Interface() { + forex(Declaration m | + m.getDeclaringType() = this.getABaseClass*() and not compgenerated(unresolveElement(m)) + | + m instanceof PureVirtualFunction + ) + } + + override string getAPrimaryQlClass() { result = "Interface" } +} + +/** + * A class/struct derivation that is virtual. For example the derivation in + * the following code is a `VirtualClassDerivation`: + * ``` + * class MyClass : public virtual MyBaseClass { + * ... + * }; + * ``` + */ +class VirtualClassDerivation extends ClassDerivation { + VirtualClassDerivation() { hasSpecifier("virtual") } + + override string getAPrimaryQlClass() { result = "VirtualClassDerivation" } +} + +/** + * A class/struct that is the base of some virtual class derivation. For + * example `MyBaseClass` in the following code is a `VirtualBaseClass` of + * `MyClass`: + * ``` + * class MyBaseClass { + * ... + * }; + * + * class MyClass : public virtual MyBaseClass { + * ... + * }; + * ``` + */ +class VirtualBaseClass extends Class { + VirtualBaseClass() { exists(VirtualClassDerivation cd | cd.getBaseClass() = this) } + + override string getAPrimaryQlClass() { result = "VirtualBaseClass" } + + /** A virtual class derivation of which this class/struct is the base. */ + VirtualClassDerivation getAVirtualDerivation() { result.getBaseClass() = this } + + /** A class/struct that is derived from this one using virtual inheritance. */ + Class getAVirtuallyDerivedClass() { result = getAVirtualDerivation().getDerivedClass() } +} + +/** + * The proxy class (where needed) associated with a template parameter, as + * in the following code: + * ``` + * template + * struct S : T { // the type of this T is a proxy class + * ... + * }; + * ``` + */ +class ProxyClass extends UserType { + ProxyClass() { usertypes(underlyingElement(this), _, 9) } + + override string getAPrimaryQlClass() { result = "ProxyClass" } + + /** Gets the location of the proxy class. */ + override Location getLocation() { result = getTemplateParameter().getDefinitionLocation() } + + /** Gets the template parameter for which this is the proxy class. */ + TemplateParameter getTemplateParameter() { + is_proxy_class_for(underlyingElement(this), unresolveElement(result)) + } +} + +// Unpacks "array of ... of array of t" into t. +private Type stripArrayTypes(Type t) { + not t instanceof ArrayType and result = t + or + result = stripArrayTypes(t.(ArrayType).getBaseType()) +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Comments.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Comments.qll new file mode 100644 index 00000000000..7574f0ff32c --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Comments.qll @@ -0,0 +1,54 @@ +/** + * Provides classes representing C and C++ comments. + */ + +import semmle.code.cpp.Location +import semmle.code.cpp.Element + +/** + * A C/C++ comment. For example the comment in the following code: + * ``` + * // C++ style single-line comment + * ``` + * or a C style comment (which starts with `/*`). + */ +class Comment extends Locatable, @comment { + override string toString() { result = this.getContents() } + + override Location getLocation() { comments(underlyingElement(this), _, result) } + + /** + * Gets the text of this comment, including the opening `//` or `/*`, and the closing `*``/` if + * present. + */ + string getContents() { comments(underlyingElement(this), result, _) } + + /** + * Gets the AST element this comment is associated with. For example, the comment in the + * following code is associated with the declaration of `j`. + * ``` + * int i; + * int j; // Comment on j + * ``` + */ + Element getCommentedElement() { + commentbinding(underlyingElement(this), unresolveElement(result)) + } +} + +/** + * A C style comment (one which starts with `/*`). + */ +class CStyleComment extends Comment { + CStyleComment() { this.getContents().matches("/*%") } +} + +/** + * A CPP style comment. For example the comment in the following code: + * ``` + * // C++ style single-line comment + * ``` + */ +class CppStyleComment extends Comment { + CppStyleComment() { this.getContents().matches("//%") } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Compilation.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Compilation.qll new file mode 100644 index 00000000000..812c417dbdd --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Compilation.qll @@ -0,0 +1,115 @@ +/** + * Provides a class representing individual compiler invocations that occurred during the build. + */ + +import semmle.code.cpp.File + +/* + * These two helper predicates are used to associate a unique integer with + * each `@compilation`, for use in the `toString` method of `Compilation`. + * These integers are not stable across trap imports, but stable across + * runs with the same database. + */ + +private predicate id(@compilation x, @compilation y) { x = y } + +private predicate idOf(@compilation x, int y) = equivalenceRelation(id/2)(x, y) + +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * gcc -c f1.c f2.c f3.c + * + * Three things happen to each file during a compilation: + * + * 1. The file is compiled by a real compiler, such as gcc or VC. + * 2. The file is parsed by the CodeQL C++ front-end. + * 3. The parsed representation is converted to database tables by + * the CodeQL extractor. + * + * This class provides CPU and elapsed time information for steps 2 and 3, + * but not for step 1. + */ +class Compilation extends @compilation { + /** Gets a textual representation of this element. */ + string toString() { + exists(int i | + idOf(this, i) and + result = "" + ) + } + + /** Gets a file compiled during this invocation. */ + File getAFileCompiled() { result = getFileCompiled(_) } + + /** Gets the `i`th file compiled during this invocation */ + File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) } + + /** + * Gets the amount of CPU time spent processing file number `i` in the C++ + * front-end. + */ + float getFrontendCpuSeconds(int i) { compilation_time(this, i, 1, result) } + + /** + * Gets the amount of elapsed time while processing file number `i` in the + * C++ front-end. + */ + float getFrontendElapsedSeconds(int i) { compilation_time(this, i, 2, result) } + + /** + * Gets the amount of CPU time spent processing file number `i` in the + * extractor. + */ + float getExtractorCpuSeconds(int i) { compilation_time(this, i, 3, result) } + + /** + * Gets the amount of elapsed time while processing file number `i` in the + * extractor. + */ + float getExtractorElapsedSeconds(int i) { compilation_time(this, i, 4, result) } + + /** + * Gets an argument passed to the extractor on this invocation. + */ + string getAnArgument() { result = getArgument(_) } + + /** + * Gets the `i`th argument passed to the extractor on this invocation. + * + * If the compiler was invoked as `gcc -c f1.c f2.c f3.c` then this + * will typically hold for + * + * i | result + * - | --- + * 0 | *path to extractor* + * 1 | `--mimic` + * 2 | `/usr/bin/gcc` + * 3 | `-c` + * 4 | f1.c + * 5 | f2.c + * 6 | f3.c + */ + string getArgument(int i) { compilation_args(this, i, result) } + + /** + * Gets the total amount of CPU time spent processing all the files in the + * front-end and extractor. + */ + float getTotalCpuSeconds() { compilation_finished(this, result, _) } + + /** + * Gets the total amount of elapsed time while processing all the files in + * the front-end and extractor. + */ + float getTotalElapsedSeconds() { compilation_finished(this, _, result) } + + /** + * Holds if the extractor terminated normally. Terminating with an exit + * code indicating that an error occurred is considered normal + * termination, but crashing due to something like a segfault is not. + */ + predicate normalTermination() { compilation_finished(this, _, _) } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Declaration.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Declaration.qll new file mode 100644 index 00000000000..b1422aa6342 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Declaration.qll @@ -0,0 +1,720 @@ +/** + * Provides classes for working with C and C++ declarations. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.Specifier +import semmle.code.cpp.Namespace +private import semmle.code.cpp.internal.QualifiedName as Q + +/** + * A C/C++ declaration: for example, a variable declaration, a type + * declaration, or a function declaration. + * + * This file defines two closely related classes: `Declaration` and + * `DeclarationEntry`. Some declarations do not correspond to a unique + * location in the source code. For example, a global variable might + * be declared in multiple source files: + * ``` + * extern int myglobal; + * ``` + * and defined in one: + * ``` + * int myglobal; + * ``` + * Each of these declarations (including the definition) is given its own + * distinct `DeclarationEntry`, but they all share the same `Declaration`. + * + * Some derived class of `Declaration` do not have a corresponding + * `DeclarationEntry`, because they always have a unique source location. + * `EnumConstant` and `FriendDecl` are both examples of this. + */ +class Declaration extends Locatable, @declaration { + /** + * Gets the innermost namespace which contains this declaration. + * + * The result will either be `GlobalNamespace`, or the tightest lexically + * enclosing namespace block. In particular, note that for declarations + * within structures, the namespace of the declaration is the same as the + * namespace of the structure. + */ + Namespace getNamespace() { + result = underlyingElement(this).(Q::Declaration).getNamespace() + or + exists(Parameter p | p = this and result = p.getFunction().getNamespace()) + or + exists(LocalVariable v | v = this and result = v.getFunction().getNamespace()) + } + + /** + * Gets the name of the declaration, fully qualified with its + * namespace and declaring type. + * + * For performance, prefer the multi-argument `hasQualifiedName` or + * `hasGlobalName` predicates since they don't construct so many intermediate + * strings. For debugging, the `semmle.code.cpp.Print` module produces more + * detailed output but are also more expensive to compute. + * + * Example: `getQualifiedName() = + * "namespace1::namespace2::TemplateClass1::Class2::memberName"`. + */ + string getQualifiedName() { result = underlyingElement(this).(Q::Declaration).getQualifiedName() } + + /** + * DEPRECATED: Prefer `hasGlobalName` or the 2-argument or 3-argument + * `hasQualifiedName` predicates. To get the exact same results as this + * predicate in all edge cases, use `getQualifiedName()`. + * + * Holds if this declaration has the fully-qualified name `qualifiedName`. + * See `getQualifiedName`. + */ + predicate hasQualifiedName(string qualifiedName) { this.getQualifiedName() = qualifiedName } + + /** + * Holds if this declaration has a fully-qualified name with a name-space + * component of `namespaceQualifier`, a declaring type of `typeQualifier`, + * and a base name of `baseName`. Template parameters and arguments are + * stripped from all components. Missing components are `""`. + * + * Example: `hasQualifiedName("namespace1::namespace2", + * "TemplateClass1::Class2", "memberName")`. + * + * Example (the class `std::vector`): `hasQualifiedName("std", "", "vector")` + * or `hasQualifiedName("std", "vector")`. + * + * Example (the `size` member function of class `std::vector`): + * `hasQualifiedName("std", "vector", "size")`. + */ + predicate hasQualifiedName(string namespaceQualifier, string typeQualifier, string baseName) { + underlyingElement(this) + .(Q::Declaration) + .hasQualifiedName(namespaceQualifier, typeQualifier, baseName) + } + + /** + * Holds if this declaration has a fully-qualified name with a name-space + * component of `namespaceQualifier`, no declaring type, and a base name of + * `baseName`. + * + * See the 3-argument `hasQualifiedName` for examples. + */ + predicate hasQualifiedName(string namespaceQualifier, string baseName) { + this.hasQualifiedName(namespaceQualifier, "", baseName) + } + + /** + * Gets a description of this `Declaration` for display purposes. + */ + string getDescription() { result = this.getName() } + + final override string toString() { result = this.getDescription() } + + /** + * Gets the name of this declaration. + * + * This name doesn't include a namespace or any argument types, so + * for example both functions `::open()` and `::std::ifstream::open(...)` + * have the same name. The name of a template _class_ includes a string + * representation of its parameters, and the names of its instantiations + * include string representations of their arguments. Template _functions_ + * and their instantiations do not include template parameters or arguments. + * + * To get the name including the namespace, use `hasQualifiedName`. + * + * To test whether this declaration has a particular name in the global + * namespace, use `hasGlobalName`. + */ + string getName() { none() } // overridden in subclasses + + /** Holds if this declaration has the given name. */ + predicate hasName(string name) { name = this.getName() } + + /** Holds if this declaration has the given name in the global namespace. */ + predicate hasGlobalName(string name) { this.hasQualifiedName("", "", name) } + + /** Holds if this declaration has the given name in the global namespace or the `std` namespace. */ + predicate hasGlobalOrStdName(string name) { + this.hasGlobalName(name) + or + this.hasQualifiedName("std", "", name) + } + + /** + * Holds if this declaration has the given name in the global namespace, + * the `std` namespace or the `bsl` namespace. + * We treat `std` and `bsl` as the same in some of our models. + */ + predicate hasGlobalOrStdOrBslName(string name) { + this.hasGlobalName(name) + or + this.hasQualifiedName("std", "", name) + or + this.hasQualifiedName("bsl", "", name) + } + + /** Gets a specifier of this declaration. */ + Specifier getASpecifier() { none() } // overridden in subclasses + + /** Holds if this declaration has a specifier with the given name. */ + predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) } + + /** + * Gets a declaration entry corresponding to this declaration. See the + * comment above this class for an explanation of the relationship + * between `Declaration` and `DeclarationEntry`. + */ + DeclarationEntry getADeclarationEntry() { none() } + + /** + * Gets the location of a declaration entry corresponding to this + * declaration. + */ + Location getADeclarationLocation() { none() } // overridden in subclasses + + /** + * Gets the declaration entry corresponding to this declaration that is a + * definition, if any. + */ + DeclarationEntry getDefinition() { none() } + + /** Gets the location of the definition, if any. */ + Location getDefinitionLocation() { none() } // overridden in subclasses + + /** Holds if the declaration has a definition. */ + predicate hasDefinition() { exists(this.getDefinition()) } + + /** DEPRECATED: Use `hasDefinition` instead. */ + predicate isDefined() { hasDefinition() } + + /** Gets the preferred location of this declaration, if any. */ + override Location getLocation() { none() } + + /** Gets a file where this element occurs. */ + File getAFile() { result = this.getADeclarationLocation().getFile() } + + /** Holds if this declaration is a top-level declaration. */ + predicate isTopLevel() { + not ( + this.isMember() or + this instanceof EnumConstant or + this instanceof Parameter or + this instanceof ProxyClass or + this instanceof LocalVariable or + this instanceof TemplateParameter or + this.(UserType).isLocal() + ) + } + + /** Holds if this declaration is static. */ + predicate isStatic() { this.hasSpecifier("static") } + + /** Holds if this declaration is a member of a class/struct/union. */ + predicate isMember() { hasDeclaringType() } + + /** Holds if this declaration is a member of a class/struct/union. */ + predicate hasDeclaringType() { exists(this.getDeclaringType()) } + + /** + * Gets the class where this member is declared, if it is a member. + * For templates, both the template itself and all instantiations of + * the template are considered to have the same declaring class. + */ + Class getDeclaringType() { this = result.getAMember() } + + /** + * Gets a template argument used to instantiate this declaration from a template. + * When called on a template, this will return a template parameter type for + * both typed and non-typed parameters. + */ + final Locatable getATemplateArgument() { result = getTemplateArgument(_) } + + /** + * Gets a template argument used to instantiate this declaration from a template. + * When called on a template, this will return a non-typed template + * parameter value. + */ + final Locatable getATemplateArgumentKind() { result = getTemplateArgumentKind(_) } + + /** + * Gets the `i`th template argument used to instantiate this declaration from a + * template. + * + * For example: + * + * `template class Foo;` + * + * Will have `getTemplateArgument(0)` return `T`, and + * `getTemplateArgument(1)` return `X`. + * + * `Foo bar;` + * + * Will have `getTemplateArgument())` return `int`, and + * `getTemplateArgument(1)` return `1`. + */ + final Locatable getTemplateArgument(int index) { + if exists(getTemplateArgumentValue(index)) + then result = getTemplateArgumentValue(index) + else result = getTemplateArgumentType(index) + } + + /** + * Gets the `i`th template argument value used to instantiate this declaration + * from a template. When called on a template, this will return the `i`th template + * parameter value if it exists. + * + * For example: + * + * `template class Foo;` + * + * Will have `getTemplateArgumentKind(1)` return `T`, and no result for + * `getTemplateArgumentKind(0)`. + * + * `Foo bar; + * + * Will have `getTemplateArgumentKind(1)` return `int`, and no result for + * `getTemplateArgumentKind(0)`. + */ + final Locatable getTemplateArgumentKind(int index) { + exists(getTemplateArgumentValue(index)) and + result = getTemplateArgumentType(index) + } + + /** Gets the number of template arguments for this declaration. */ + final int getNumberOfTemplateArguments() { + result = count(int i | exists(getTemplateArgument(i))) + } + + private Type getTemplateArgumentType(int index) { + class_template_argument(underlyingElement(this), index, unresolveElement(result)) + or + function_template_argument(underlyingElement(this), index, unresolveElement(result)) + or + variable_template_argument(underlyingElement(this), index, unresolveElement(result)) + } + + private Expr getTemplateArgumentValue(int index) { + class_template_argument_value(underlyingElement(this), index, unresolveElement(result)) + or + function_template_argument_value(underlyingElement(this), index, unresolveElement(result)) + or + variable_template_argument_value(underlyingElement(this), index, unresolveElement(result)) + } +} + +private class TDeclarationEntry = @var_decl or @type_decl or @fun_decl; + +/** + * A C/C++ declaration entry. For example the following code contains five + * declaration entries: + * ``` + * extern int myGlobal; + * int myVariable; + * typedef char MyChar; + * void myFunction(); + * void myFunction() { + * // ... + * } + * ``` + * See the comment above `Declaration` for an explanation of the relationship + * between `Declaration` and `DeclarationEntry`. + */ +class DeclarationEntry extends Locatable, TDeclarationEntry { + /** Gets a specifier associated with this declaration entry. */ + string getASpecifier() { none() } // overridden in subclasses + + /** + * Gets the name associated with the corresponding definition (where + * available), or the name declared by this entry otherwise. + */ + string getCanonicalName() { + if getDeclaration().hasDefinition() + then result = getDeclaration().getDefinition().getName() + else result = getName() + } + + /** + * Gets the declaration for which this is a declaration entry. + * + * Note that this is *not* always the inverse of + * `Declaration.getADeclarationEntry()`, for example if `C` is a + * `TemplateClass`, `I` is an instantiation of `C`, and `D` is a + * `Declaration` of `C`, then: + * `C.getADeclarationEntry()` returns `D` + * `I.getADeclarationEntry()` returns `D` + * but `D.getDeclaration()` only returns `C` + */ + Declaration getDeclaration() { none() } // overridden in subclasses + + /** Gets the name associated with this declaration entry, if any. */ + string getName() { none() } // overridden in subclasses + + /** + * Gets the type associated with this declaration entry. + * + * For variable declarations, get the type of the variable. + * For function declarations, get the return type of the function. + * For type declarations, get the type being declared. + */ + Type getType() { none() } // overridden in subclasses + + /** + * Gets the type associated with this declaration entry after specifiers + * have been deeply stripped and typedefs have been resolved. + * + * For variable declarations, get the type of the variable. + * For function declarations, get the return type of the function. + * For type declarations, get the type being declared. + */ + Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() } + + /** + * Holds if this declaration entry has a specifier with the given name. + */ + predicate hasSpecifier(string specifier) { getASpecifier() = specifier } + + /** Holds if this declaration entry is a definition. */ + predicate isDefinition() { none() } // overridden in subclasses + + override string toString() { + if isDefinition() + then result = "definition of " + getName() + else + if getName() = getCanonicalName() + then result = "declaration of " + getName() + else result = "declaration of " + getCanonicalName() + " as " + getName() + } +} + +private class TAccessHolder = @function or @usertype; + +/** + * A declaration that can potentially have more C++ access rights than its + * enclosing element. This comprises `Class` (they have access to their own + * private members) along with other `UserType`s and `Function` (they can be + * the target of `friend` declarations). For example `MyClass` and + * `myFunction` in the following code: + * ``` + * class MyClass + * { + * public: + * ... + * }; + * + * void myFunction() { + * // ... + * } + * ``` + * In the C++ standard (N4140 11.2), rules for access control revolve around + * the informal phrase "_R_ occurs in a member or friend of class C", where + * `AccessHolder` corresponds to this _R_. + */ +class AccessHolder extends Declaration, TAccessHolder { + /** + * Holds if `this` can access private members of class `c`. + * + * This predicate encodes the phrase "occurs in a member or friend" that is + * repeated many times in the C++14 standard, section 11.2. + */ + predicate inMemberOrFriendOf(Class c) { + this.getEnclosingAccessHolder*() = c + or + exists(FriendDecl fd | fd.getDeclaringClass() = c | + this.getEnclosingAccessHolder*() = fd.getFriend() + ) + } + + /** + * Gets the nearest enclosing `AccessHolder`. + */ + AccessHolder getEnclosingAccessHolder() { none() } // overridden in subclasses + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` (N4140 + * 11.2/4). When this holds, and `derived` has only one base subobject of + * type `base`, code in `this` can implicitly convert a pointer to `derived` + * into a pointer to `base`. Conversely, if such a conversion is possible + * then this predicate holds. + * + * For the sake of generality, this predicate also holds whenever `base` = + * `derived`. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all classes `derived` can be converted to + * their public bases `base` from everywhere (`this`), so this predicate + * could yield a number of tuples that is quadratic in the size of the + * program. To avoid this combinatorial explosion, only use this predicate in + * a context where `this` together with `base` or `derived` are sufficiently + * restricted. + */ + pragma[inline] + predicate canAccessClass(Class base, Class derived) { + // This predicate is marked `inline` and implemented in a very particular + // way. If we allowed this predicate to be fully computed, it would relate + // all `AccessHolder`s to all classes, which would be too much. + // There are four rules in N4140 11.2/4. Only the one named (4.4) is + // recursive, and it describes a transitive closure: intuitively, if A can + // be converted to B, and B can be converted to C, then A can be converted + // to C. To limit the number of tuples in the non-inline helper predicates, + // we first separate the derivation of 11.2/4 into two cases: + // Derivations using only (4.1) and (4.4). Note that these derivations are + // independent of `this`, which is why users of this predicate must take + // care to avoid a combinatorial explosion. + isDirectPublicBaseOf*(base, derived) + or + exists(DirectAccessHolder n | + this.getEnclosingAccessHolder*() = n and + // Derivations using (4.2) or (4.3) at least once. + n.thisCanAccessClassTrans(base, derived) + ) + } + + /** + * Holds if a non-static member `member` _is accessible at_ `this` when named + * in a class `derived` that is derived from or equal to the declaring class + * of `member` (N4140 11.2/5 and 11.4). + * + * This predicate determines whether an expression `x.member` would be + * allowed in `this` when `x` has type `derived`. The more general syntax + * `x.N::member`, where `N` may be a base class of `derived`, is not + * supported. This should only affect very rare edge cases of 11.4. This + * predicate concerns only _access_ and thus does not determine whether + * `member` can be unambiguously named at `this`: multiple overloads may + * apply, or `member` may be declared in an ambiguous base class. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all public members `member` are accessible + * from everywhere (`this`), so this predicate could yield a number of tuples + * that is quadratic in the size of the program. To avoid this combinatorial + * explosion, only use this predicate in a context where `this` and `member` + * are sufficiently restricted when `member` is public. + */ + pragma[inline] + predicate canAccessMember(Declaration member, Class derived) { + this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier), + derived) + } + + /** + * Holds if a hypothetical non-static member of `memberClass` with access + * specifier `memberAccess` _is accessible at_ `this` when named in a class + * `derived` that is derived from or equal to `memberClass` (N4140 11.2/5 and + * 11.4). + * + * This predicate determines whether an expression `x.m` would be + * allowed in `this` when `x` has type `derived` and `m` has `memberAccess` + * in `memberClass`. The more general syntax `x.N::n`, where `N` may be a + * base class of `derived`, is not supported. This should only affect very + * rare edge cases of 11.4. + * + * This predicate is `pragma[inline]` because it is infeasible to fully + * compute it on large code bases: all classes `memberClass` have their + * public members accessible from everywhere (`this`), so this predicate + * could yield a number of tuples that is quadratic in the size of the + * program. To avoid this combinatorial explosion, only use this predicate in + * a context where `this` and `memberClass` are sufficiently restricted when + * `memberAccess` is public. + */ + pragma[inline] + predicate couldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) { + // There are four rules in N4140 11.2/5. To limit the number of tuples in + // the non-inline helper predicates, we first separate the derivation of + // 11.2/5 into two cases: + // Rule (5.1) directly: the member is public, and `derived` uses public + // inheritance all the way up to `memberClass`. Note that these derivations + // are independent of `this`, which is why users of this predicate must + // take care to avoid a combinatorial explosion. + everyoneCouldAccessMember(memberClass, memberAccess, derived) + or + exists(DirectAccessHolder n | + this.getEnclosingAccessHolder*() = n and + // Any other derivation. + n.thisCouldAccessMember(memberClass, memberAccess, derived) + ) + } +} + +/** + * A declaration that very likely has more C++ access rights than its + * enclosing element. This comprises `Class` (they have access to their own + * private members) along with any target of a `friend` declaration. For + * example `MyClass` and `friendFunction` in the following code: + * ``` + * class MyClass + * { + * public: + * friend void friendFunction(); + * }; + * + * void friendFunction() { + * // ... + * } + * ``` + * Most access rights are computed for `DirectAccessHolder` instead of + * `AccessHolder` -- that's more efficient because there are fewer + * `DirectAccessHolder`s. If a `DirectAccessHolder` contains an `AccessHolder`, + * then the contained `AccessHolder` inherits its access rights. + */ +private class DirectAccessHolder extends Element { + DirectAccessHolder() { + this instanceof Class + or + exists(FriendDecl fd | fd.getFriend() = this) + } + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` when + * the derivation of that fact uses rule (4.2) and (4.3) of N4140 11.2/4 at + * least once. In other words, the `this` parameter is not ignored. This + * restriction makes it feasible to fully enumerate this predicate even on + * large code bases. + */ + predicate thisCanAccessClassTrans(Class base, Class derived) { + // This implementation relies on the following property of our predicates: + // if `this.thisCanAccessClassStep(b, d)` and + // `isDirectPublicBaseOf(b2, b)`, then + // `this.thisCanAccessClassStep(b2, d)`. In other words, if a derivation + // uses (4.2) or (4.3) somewhere and uses (4.1) directly above that in the + // transitive chain, then the use of (4.1) is redundant. This means we only + // need to consider derivations that use (4.2) or (4.3) as the "first" + // step, that is, towards `base`, so this implementation is essentially a + // transitive closure with a restricted base case. + this.thisCanAccessClassStep(base, derived) + or + exists(Class between | thisCanAccessClassTrans(base, between) | + isDirectPublicBaseOf(between, derived) or + this.thisCanAccessClassStep(between, derived) + ) + // It is possible that this predicate could be computed faster for deep + // hierarchies if we can prove and utilize that all derivations of 11.2/4 + // can be broken down into steps where `base` is a _direct_ base of + // `derived` in each step. + } + + /** + * Holds if a base class `base` of `derived` _is accessible at_ `this` using + * only a single application of rule (4.2) and (4.3) of N4140 11.2/4. + */ + private predicate thisCanAccessClassStep(Class base, Class derived) { + exists(AccessSpecifier public | public.hasName("public") | + // Rules (4.2) and (4.3) are implemented together as one here with + // reflexive-transitive inheritance, where (4.3) is the transitive case, + // and (4.2) is the reflexive case. + exists(Class p | p = derived.getADerivedClass*() | + this.isFriendOfOrEqualTo(p) and + // Note: it's crucial that this is `!=` rather than `not =` since + // `accessOfBaseMember` does not have a result when the member would be + // inaccessible. + p.accessOfBaseMember(base, public) != public + ) + ) and + // This is the only case that doesn't in itself guarantee that + // `derived` < `base`, so we add the check here. The standard suggests + // computing `canAccessClass` only for derived classes, but that seems + // incompatible with the execution model of QL, so we instead construct + // every case to guarantee `derived` < `base`. + derived = base.getADerivedClass+() + } + + /** + * Like `couldAccessMember` but only contains derivations in which either + * (5.2), (5.3) or (5.4) must be invoked. In other words, the `this` + * parameter is not ignored. This restriction makes it feasible to fully + * enumerate this predicate even on large code bases. We check for 11.4 as + * part of (5.3), since this further limits the number of tuples produced by + * this predicate. + */ + predicate thisCouldAccessMember(Class memberClass, AccessSpecifier memberAccess, Class derived) { + // Only (5.4) is recursive, and chains of invocations of (5.4) can always + // be collapsed to one invocation by the transitivity of 11.2/4. + // Derivations not using (5.4) can always be rewritten to have a (5.4) rule + // in front because our encoding of 11.2/4 in `canAccessClass` is + // reflexive. Thus, we only need to consider three cases: rule (5.4) + // followed by either (5.1), (5.2) or (5.3). + // Rule (5.4), using a non-trivial derivation of 11.2/4, followed by (5.1). + // If the derivation of 11.2/4 is trivial (only uses (4.1) and (4.4)), this + // case can be replaced with purely (5.1) and thus does not need to be in + // this predicate. + exists(Class between | this.thisCanAccessClassTrans(between, derived) | + everyoneCouldAccessMember(memberClass, memberAccess, between) + ) + or + // Rule (5.4) followed by Rule (5.2) + exists(Class between | this.(AccessHolder).canAccessClass(between, derived) | + between.accessOfBaseMember(memberClass, memberAccess).hasName("private") and + this.isFriendOfOrEqualTo(between) + ) + or + // Rule (5.4) followed by Rule (5.3), integrating 11.4. We integrate 11.4 + // here because we would otherwise generate too many tuples. This code is + // very performance-sensitive, and any changes should be benchmarked on + // LibreOffice. + // Rule (5.4) requires that `this.canAccessClass(between, derived)` + // (implying that `derived <= between` in the class hierarchy) and that + // `p <= between`. Rule 11.4 additionally requires `derived <= p`, but + // all these rules together result in too much freedom and overlap between + // cases. Therefore, for performance, we split into three cases for how + // `between` as a base of `derived` is accessible at `this`, where `this` + // is the implementation of `p`: + // 1. `between` is an accessible base of `derived` by going through `p` as + // an intermediate step. + // 2. `this` is part of the implementation of `derived` because it's a + // member or a friend. In this case, we do not need `p` to perform this + // derivation, so we can set `p = derived` and proceed as in case 1. + // 3. `derived` has an alternative inheritance path up to `between` that + // bypasses `p`. Then that path must be public, or we are in case 2. + exists(AccessSpecifier public | public.hasName("public") | + exists(Class between, Class p | + between.accessOfBaseMember(memberClass, memberAccess).hasName("protected") and + this.isFriendOfOrEqualTo(p) and + ( + // This is case 1 from above. If `p` derives privately from `between` + // then the member we're trying to access is private or inaccessible + // in `derived`, so either rule (5.2) applies instead, or the member + // is inaccessible. Therefore, in this case, `p` must derive at least + // protected from `between`. Further, since the access of `derived` + // to its base `between` must pass through `p` in this case, we know + // that `derived` must derived publicly from `p` unless we are in + // case 2; there are no other cases of 11.2/4 where the + // implementation of a base class can access itself as a base. + p.accessOfBaseMember(between, public).getName() >= "protected" and + derived.accessOfBaseMember(p, public) = public + or + // This is case 3 above. + derived.accessOfBaseMember(between, public) = public and + derived = p.getADerivedClass*() and + exists(p.accessOfBaseMember(between, memberAccess)) + ) + ) + ) + } + + private predicate isFriendOfOrEqualTo(Class c) { + exists(FriendDecl fd | fd.getDeclaringClass() = c | this = fd.getFriend()) + or + this = c + } +} + +/** + * Holds if `base` is a direct public base of `derived`, possibly virtual and + * possibly through typedefs. The transitive closure of this predicate encodes + * derivations of N4140 11.2/4 that use only (4.1) and (4.4). + */ +private predicate isDirectPublicBaseOf(Class base, Class derived) { + exists(ClassDerivation cd | + cd.getBaseClass() = base and + cd.getDerivedClass() = derived and + cd.getASpecifier().hasName("public") + ) +} + +/** + * Holds if a hypothetical member of `memberClass` with access specifier + * `memberAccess` would be public when named as a member of `derived`. + * This encodes N4140 11.2/5 case (5.1). + */ +private predicate everyoneCouldAccessMember( + Class memberClass, AccessSpecifier memberAccess, Class derived +) { + derived.accessOfBaseMember(memberClass, memberAccess).hasName("public") +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Diagnostics.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Diagnostics.qll new file mode 100644 index 00000000000..9ad38d4e4be --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Diagnostics.qll @@ -0,0 +1,68 @@ +/** + * Provides classes representing warnings generated during compilation. + */ + +import semmle.code.cpp.Location + +/** A compiler-generated error, warning or remark. */ +class Diagnostic extends Locatable, @diagnostic { + /** Gets the compilation that generated this diagnostic. */ + Compilation getCompilation() { diagnostic_for(underlyingElement(this), result, _, _) } + + /** + * Gets the severity of the message, on a range from 1 to 5: 1=remark, + * 2=warning, 3=discretionary error, 4=error, 5=catastrophic error. + */ + int getSeverity() { diagnostics(underlyingElement(this), result, _, _, _, _) } + + /** Gets the error code for this compiler message. */ + string getTag() { diagnostics(underlyingElement(this), _, result, _, _, _) } + + /** Holds if `s` is the error code for this compiler message. */ + predicate hasTag(string s) { this.getTag() = s } + + /** + * Gets the error message text associated with this compiler + * diagnostic. + */ + string getMessage() { diagnostics(underlyingElement(this), _, _, result, _, _) } + + /** + * Gets the full error message text associated with this compiler + * diagnostic. + */ + string getFullMessage() { diagnostics(underlyingElement(this), _, _, _, result, _) } + + /** Gets the source location corresponding to the compiler message. */ + override Location getLocation() { diagnostics(underlyingElement(this), _, _, _, _, result) } + + override string toString() { result = this.getMessage() } +} + +/** A compiler-generated remark (milder than a warning). */ +class CompilerRemark extends Diagnostic { + CompilerRemark() { this.getSeverity() = 1 } +} + +/** A compiler-generated warning. */ +class CompilerWarning extends Diagnostic { + CompilerWarning() { this.getSeverity() = 2 } +} + +/** + * A compiler-generated discretionary error (a compile-time error that may + * be suppressed). + */ +class CompilerDiscretionaryError extends Diagnostic { + CompilerDiscretionaryError() { this.getSeverity() = 3 } +} + +/** A compiler error message. */ +class CompilerError extends Diagnostic { + CompilerError() { this.getSeverity() = 4 } +} + +/** A compiler error that prevents compilation from continuing. */ +class CompilerCatastrophe extends Diagnostic { + CompilerCatastrophe() { this.getSeverity() = 5 } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Element.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Element.qll new file mode 100644 index 00000000000..1f547adccaa --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Element.qll @@ -0,0 +1,297 @@ +/** + * Provides the `Element` class, which is the base class for all classes representing C or C++ + * program elements. + */ + +import semmle.code.cpp.Location +private import semmle.code.cpp.Enclosing +private import semmle.code.cpp.internal.ResolveClass + +/** + * Get the `Element` that represents this `@element`. + * Normally this will simply be a cast of `e`, but sometimes it is not. + * For example, for an incomplete struct `e` the result may be a + * complete struct with the same name. + */ +pragma[inline] +Element mkElement(@element e) { unresolveElement(result) = e } + +/** + * INTERNAL: Do not use. + * + * Gets an `@element` that resolves to the `Element`. This should + * normally only be called from member predicates, where `e` is not + * `this` and you need the result for an argument to a database + * extensional. + * See `underlyingElement` for when `e` is `this`. + */ +pragma[inline] +@element unresolveElement(Element e) { + not result instanceof @usertype and + result = e + or + e = resolveClass(result) +} + +/** + * INTERNAL: Do not use. + * + * Gets the `@element` that this `Element` extends. This should normally + * only be called from member predicates, where `e` is `this` and you + * need the result for an argument to a database extensional. + * See `unresolveElement` for when `e` is not `this`. + */ +@element underlyingElement(Element e) { result = e } + +/** + * A C/C++ element with no member predicates other than `toString`. Not for + * general use. This class does not define a location, so classes wanting to + * change their location without affecting other classes can extend + * `ElementBase` instead of `Element` to create a new rootdef for `getURL`, + * `getLocation`, or `hasLocationInfo`. + */ +class ElementBase extends @element { + /** Gets a textual representation of this element. */ + cached + string toString() { none() } + + /** DEPRECATED: use `getAPrimaryQlClass` instead. */ + deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() } + + /** + * Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs. + */ + final string getPrimaryQlClasses() { result = concat(getAPrimaryQlClass(), ",") } + + /** + * Gets the name of a primary CodeQL class to which this element belongs. + * + * For most elements, this is simply the most precise syntactic category to + * which they belong; for example, `AddExpr` is a primary class, but + * `BinaryOperation` is not. + * + * This predicate can have multiple results if multiple primary classes match. + * For some elements, this predicate may not have a result. + */ + string getAPrimaryQlClass() { none() } +} + +/** + * A C/C++ element. This class is the base class for all C/C++ + * elements, such as functions, classes, expressions, and so on. + */ +class Element extends ElementBase { + /** Gets the primary file where this element occurs. */ + File getFile() { result = this.getLocation().getFile() } + + /** + * Holds if this element may be from source. This predicate holds for all + * elements, except for those in the dummy file, whose name is the empty string. + * The dummy file contains declarations that are built directly into the compiler. + */ + predicate fromSource() { this.getFile().fromSource() } + + /** + * Holds if this element may be from a library. + * + * DEPRECATED: always true. + */ + deprecated predicate fromLibrary() { this.getFile().fromLibrary() } + + /** Gets the primary location of this element. */ + Location getLocation() { none() } + + /** + * Gets the source of this element: either itself or a macro that expanded + * to this element. + * + * If the element is not in a macro expansion, then the "root" is just + * the element itself. Otherwise, it is the definition of the innermost + * macro whose expansion the element is in. + * + * This method is useful for filtering macro results in checks: simply + * blame `e.findRootCause` rather than `e`. This will report only bugs + * that are not in macros, and in addition report macros that (somewhere) + * expand to a bug. + */ + Element findRootCause() { + if exists(MacroInvocation mi | this = mi.getAGeneratedElement()) + then + exists(MacroInvocation mi | + this = mi.getAGeneratedElement() and + not exists(MacroInvocation closer | + this = closer.getAGeneratedElement() and + mi = closer.getParentInvocation+() + ) and + result = mi.getMacro() + ) + else result = this + } + + /** + * Gets the parent scope of this `Element`, if any. + * A scope is a `Type` (`Class` / `Enum`), a `Namespace`, a `BlockStmt`, a `Function`, + * or certain kinds of `Statement`. + */ + Element getParentScope() { + // result instanceof class + exists(Declaration m | + m = this and + result = m.getDeclaringType() and + not this instanceof EnumConstant + ) + or + exists(TemplateClass tc | this = tc.getATemplateArgument() and result = tc) + or + // result instanceof namespace + exists(Namespace n | result = n and n.getADeclaration() = this) + or + exists(FriendDecl d, Namespace n | this = d and n.getADeclaration() = d and result = n) + or + exists(Namespace n | this = n and result = n.getParentNamespace()) + or + // result instanceof stmt + exists(LocalVariable v | + this = v and + exists(DeclStmt ds | ds.getADeclaration() = v and result = ds.getParent()) + ) + or + exists(Parameter p | this = p and result = p.getFunction()) + or + exists(GlobalVariable g, Namespace n | this = g and n.getADeclaration() = g and result = n) + or + exists(EnumConstant e | this = e and result = e.getDeclaringEnum()) + or + // result instanceof block|function + exists(BlockStmt b | this = b and blockscope(unresolveElement(b), unresolveElement(result))) + or + exists(TemplateFunction tf | this = tf.getATemplateArgument() and result = tf) + or + // result instanceof stmt + exists(ControlStructure s | this = s and result = s.getParent()) + or + using_container(unresolveElement(result), underlyingElement(this)) + } + + /** + * Holds if this element comes from a macro expansion. Only elements that + * are entirely generated by a macro are included - for elements that + * partially come from a macro, see `isAffectedByMacro`. + */ + predicate isInMacroExpansion() { inMacroExpansion(this) } + + /** + * Holds if this element is affected in any way by a macro. All elements + * that are totally or partially generated by a macro are included, so + * this is a super-set of `isInMacroExpansion`. + */ + predicate isAffectedByMacro() { affectedByMacro(this) } + + private Element getEnclosingElementPref() { + enclosingfunction(underlyingElement(this), unresolveElement(result)) or + result.(Function) = stmtEnclosingElement(this) or + this.(LocalScopeVariable).getFunction() = result or + enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) or + derivations(underlyingElement(this), unresolveElement(result), _, _, _) or + stmtparents(underlyingElement(this), _, unresolveElement(result)) or + exprparents(underlyingElement(this), _, unresolveElement(result)) or + namequalifiers(underlyingElement(this), unresolveElement(result), _, _) or + initialisers(underlyingElement(this), unresolveElement(result), _, _) or + exprconv(unresolveElement(result), underlyingElement(this)) or + param_decl_bind(underlyingElement(this), _, unresolveElement(result)) or + using_container(unresolveElement(result), underlyingElement(this)) or + static_asserts(unresolveElement(this), _, _, _, underlyingElement(result)) + } + + /** Gets the closest `Element` enclosing this one. */ + cached + Element getEnclosingElement() { + result = getEnclosingElementPref() + or + not exists(getEnclosingElementPref()) and + ( + this = result.(Class).getAMember() + or + result = exprEnclosingElement(this) + or + var_decls(underlyingElement(this), unresolveElement(result), _, _, _) + ) + } + + /** + * Holds if this `Element` is a part of a template instantiation (but not + * the template itself). + */ + predicate isFromTemplateInstantiation(Element instantiation) { + exists(Element e | isFromTemplateInstantiationRec(e, instantiation) | + this = e or + this.(DeclarationEntry).getDeclaration() = e + ) + } + + /** + * Holds if this `Element` is part of a template `template` (not if it is + * part of an instantiation of `template`). This means it is represented in + * the database purely as syntax and without guarantees on the presence or + * correctness of type-based operations such as implicit conversions. + * + * If an element is nested within several templates, this predicate holds with + * a value of `template` for each containing template. + */ + predicate isFromUninstantiatedTemplate(Element template) { + exists(Element e | isFromUninstantiatedTemplateRec(e, template) | + this = e or + this.(DeclarationEntry).getDeclaration() = e + ) + } +} + +private predicate isFromTemplateInstantiationRec(Element e, Element instantiation) { + instantiation.(Function).isConstructedFrom(_) and + e = instantiation + or + instantiation.(Class).isConstructedFrom(_) and + e = instantiation + or + instantiation.(Variable).isConstructedFrom(_) and + e = instantiation + or + isFromTemplateInstantiationRec(e.getEnclosingElement(), instantiation) +} + +private predicate isFromUninstantiatedTemplateRec(Element e, Element template) { + is_class_template(unresolveElement(template)) and + e = template + or + is_function_template(unresolveElement(template)) and + e = template + or + is_variable_template(unresolveElement(template)) and + e = template + or + isFromUninstantiatedTemplateRec(e.getEnclosingElement(), template) +} + +/** + * A C++11 `static_assert` or C11 `_Static_assert` construct. For example each + * line in the following example contains a static assert: + * ``` + * static_assert(sizeof(MyStruct) <= 4096); + * static_assert(sizeof(MyStruct) <= 4096, "MyStruct is too big!"); + * ``` + */ +class StaticAssert extends Locatable, @static_assert { + override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" } + + /** + * Gets the expression which this static assertion ensures is true. + */ + Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _, _) } + + /** + * Gets the message which will be reported by the compiler if this static assertion fails. + */ + string getMessage() { static_asserts(underlyingElement(this), _, result, _, _) } + + override Location getLocation() { static_asserts(underlyingElement(this), _, _, result, _) } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enclosing.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enclosing.qll new file mode 100644 index 00000000000..d821589a76c --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enclosing.qll @@ -0,0 +1,63 @@ +/** + * Provides predicates for finding the smallest element that encloses an expression or statement. + */ + +import cpp + +/** + * Gets the enclosing element of statement `s`. + */ +cached +Element stmtEnclosingElement(Stmt s) { + result.(Function).getEntryPoint() = s or + result = stmtEnclosingElement(s.getParent()) or + result = exprEnclosingElement(s.getParent()) +} + +/** + * Gets the enclosing element of expression `e`. + */ +// The `pragma[nomagic]` is a workaround to prevent this cached stage (and all +// subsequent stages) from being evaluated twice. See QL-888. It has the effect +// of making the `Conversion` class predicate get the same optimization in all +// queries. +pragma[nomagic] +cached +Element exprEnclosingElement(Expr e) { + result = exprEnclosingElement(e.getParent()) + or + result = stmtEnclosingElement(e.getParent()) + or + result.(Function) = e.getParent() + or + result = exprEnclosingElement(e.(Conversion).getExpr()) + or + exists(Initializer i | + i.getExpr() = e and + if exists(i.getEnclosingStmt()) + then result = stmtEnclosingElement(i.getEnclosingStmt()) + else + if i.getDeclaration() instanceof Parameter + then result = i.getDeclaration().(Parameter).getFunction() + else result = i.getDeclaration() + ) + or + exists(Expr anc | + expr_ancestor(unresolveElement(e), unresolveElement(anc)) and result = exprEnclosingElement(anc) + ) + or + exists(Stmt anc | + expr_ancestor(unresolveElement(e), unresolveElement(anc)) and result = stmtEnclosingElement(anc) + ) + or + exists(DeclarationEntry de | + expr_ancestor(unresolveElement(e), unresolveElement(de)) and + if exists(DeclStmt ds | de = ds.getADeclarationEntry()) + then + exists(DeclStmt ds | + de = ds.getADeclarationEntry() and + result = stmtEnclosingElement(ds) + ) + else result = de.getDeclaration() + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enum.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enum.qll new file mode 100644 index 00000000000..9cddeb78f9b --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Enum.qll @@ -0,0 +1,204 @@ +/** + * Provides classes representing C/C++ enums and enum constants. + */ + +import semmle.code.cpp.Type +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C/C++ enum [N4140 7.2]. For example, the types `MyEnum` and + * `MyScopedEnum` in: + * ``` + * enum MyEnum { + * MyEnumConstant + * }; + * + * enum class MyScopedEnum { + * MyScopedEnumConstant + * }; + * ``` + * This includes C++ scoped enums, see the `ScopedEnum` QL class. + */ +class Enum extends UserType, IntegralOrEnumType { + /** Gets an enumerator of this enumeration. */ + EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this } + + /** + * Gets the enumerator of this enumeration that was declared at the zero-based position `index`. + * For example, `zero` is at index 2 in the following declaration: + * ``` + * enum ReversedOrder { + * two = 2, + * one = 1, + * zero = 0 + * }; + * ``` + */ + EnumConstant getEnumConstant(int index) { + enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _) + } + + override string getAPrimaryQlClass() { result = "Enum" } + + /** + * Gets a descriptive string for the enum. This method is only intended to + * be used for debugging purposes. For more information, see the comment + * for `Type.explain`. + */ + override string explain() { result = "enum " + this.getName() } + + override int getSize() { + // Workaround for extractor bug CPP-348: No size information for enums. + // If the extractor didn't provide a size, assume four bytes. + result = UserType.super.getSize() + or + not exists(UserType.super.getSize()) and result = 4 + } + + /** See `Type.isDeeplyConst` and `Type.isDeeplyConstBelow`. Internal. */ + override predicate isDeeplyConstBelow() { any() } // No subparts + + /** + * Holds if this enum has an enum-base [N4140 7.2]. + * For example: `enum E : int`. + */ + predicate hasExplicitUnderlyingType() { derivations(_, underlyingElement(this), _, _, _) } + + /** + * The type of the enum-base [N4140 7.2], if it is specified. + * For example: `int` in `enum E : int`. + */ + Type getExplicitUnderlyingType() { + derivations(_, underlyingElement(this), _, unresolveElement(result), _) + } +} + +/** + * A C/C++ enum that is directly enclosed by a function. For example, the type + * `MyLocalEnum` in: + * ``` + * void myFunction() { + * enum MyLocalEnum { + * MyLocalEnumConstant + * }; + * } + * ``` + */ +class LocalEnum extends Enum { + LocalEnum() { isLocal() } + + override string getAPrimaryQlClass() { result = "LocalEnum" } +} + +/** + * A C/C++ enum that is declared within a class/struct. For example, the type + * `MyNestedEnum` in: + * ``` + * class MyClass { + * public: + * enum MyNestedEnum { + * MyNestedEnumConstant + * }; + * }; + * ``` + */ +class NestedEnum extends Enum { + NestedEnum() { this.isMember() } + + override string getAPrimaryQlClass() { result = "NestedEnum" } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } +} + +/** + * A C++ scoped enum, that is, an enum whose constants must be qualified with + * the name of the enum. For example, the type `Color` in: + * ``` + * enum class Color { + * red, + * blue + * } + * ``` + */ +class ScopedEnum extends Enum { + ScopedEnum() { usertypes(underlyingElement(this), _, 13) } + + override string getAPrimaryQlClass() { result = "ScopedEnum" } +} + +/** + * A C/C++ enumerator [N4140 7.2], also known as an enumeration constant. + * + * For example the enumeration constant `green` in: + * ``` + * enum { + * red, + * green, + * blue + * } + * ``` + */ +class EnumConstant extends Declaration, @enumconstant { + /** + * Gets the enumeration of which this enumerator is a member. + */ + Enum getDeclaringEnum() { + enumconstants(underlyingElement(this), unresolveElement(result), _, _, _, _) + } + + override string getAPrimaryQlClass() { result = "EnumConstant" } + + override Class getDeclaringType() { result = this.getDeclaringEnum().getDeclaringType() } + + /** + * Gets the name of this enumerator. + */ + override string getName() { enumconstants(underlyingElement(this), _, _, _, result, _) } + + /** + * Gets the value that this enumerator is initialized to, as a + * string. This can be a value explicitly given to the enumerator, or an + * automatically assigned value. + */ + string getValue() { result = this.getInitializer().getExpr().getValue() } + + /** Gets the type of this enumerator. */ + Type getType() { enumconstants(underlyingElement(this), _, _, unresolveElement(result), _, _) } + + /** Gets the location of a declaration of this enumerator. */ + override Location getADeclarationLocation() { result = this.getDefinitionLocation() } + + /** Gets the location of the definition of this enumerator. */ + override Location getDefinitionLocation() { + enumconstants(underlyingElement(this), _, _, _, _, result) + } + + /** Gets the location of the definition of this enumerator. */ + override Location getLocation() { result = this.getDefinitionLocation() } + + /** Gets the initializer of this enumerator, if any. */ + Initializer getInitializer() { result.getDeclaration() = this } + + /** Gets an access of this enumerator. */ + EnumConstantAccess getAnAccess() { result.getTarget() = this } + + /** Gets a specifier of this enumerator. */ + override Specifier getASpecifier() { + varspecifiers(underlyingElement(this), unresolveElement(result)) + } + + /** + * An attribute of this enumerator. + * + * Note that allowing attributes on enumerators is a language extension + * which is only supported by Clang. + */ + Attribute getAnAttribute() { varattributes(underlyingElement(this), unresolveElement(result)) } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Field.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Field.qll new file mode 100644 index 00000000000..5ed5e8e4b4b --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Field.qll @@ -0,0 +1,126 @@ +/** + * Provides classes representing C structure members and C++ non-static member variables. + */ + +import semmle.code.cpp.Variable +import semmle.code.cpp.Enum +import semmle.code.cpp.exprs.Access + +/** + * A C structure member or C++ non-static member variable. For example the + * member variable `m` in the following code (but not `s`): + * ``` + * class MyClass { + * public: + * int m; + * static int s; + * }; + * ``` + * + * This does not include static member variables in C++. To include static member + * variables, use `MemberVariable` instead of `Field`. + */ +class Field extends MemberVariable { + Field() { fieldoffsets(underlyingElement(this), _, _) } + + override string getAPrimaryQlClass() { result = "Field" } + + /** + * Gets the offset of this field in bytes from the start of its declaring + * type (on the machine where facts were extracted). + */ + int getByteOffset() { fieldoffsets(underlyingElement(this), result, _) } + + /** + * Gets the byte offset within `mostDerivedClass` of each occurence of this + * field within `mostDerivedClass` itself or a base class subobject of + * `mostDerivedClass`. + * Note that for fields of virtual base classes, and non-virtual base classes + * thereof, this predicate assumes that `mostDerivedClass` is the type of the + * complete most-derived object. + */ + int getAByteOffsetIn(Class mostDerivedClass) { + result = mostDerivedClass.getABaseClassByteOffset(getDeclaringType()) + getByteOffset() + } + + /** + * Holds if the field can be initialized as part of an initializer list. For + * example, in: + * ``` + * struct S { + * unsigned int a : 5; + * unsigned int : 5; + * unsigned int b : 5; + * }; + * ``` + * + * Fields `a` and `b` are initializable, but the unnamed bitfield is not. + */ + predicate isInitializable() { + // All non-bitfield fields are initializable. This predicate is overridden + // in `BitField` to handle the anonymous bitfield case. + any() + } + + /** + * Gets the zero-based index of the specified field within its enclosing + * class, counting only fields that can be initialized. This is the order in + * which the field will be initialized, whether by an initializer list or in a + * constructor. + */ + pragma[nomagic] + final int getInitializationOrder() { + exists(Class cls, int memberIndex | + this = cls.getCanonicalMember(memberIndex) and + memberIndex = + rank[result + 1](int index | cls.getCanonicalMember(index).(Field).isInitializable()) + ) + } +} + +/** + * A C structure member or C++ member variable declared with an explicit size + * in bits. For example the member variable `x` in the following code: + * ``` + * struct MyStruct { + * int x : 3; + * }; + * ``` + */ +class BitField extends Field { + BitField() { bitfield(underlyingElement(this), _, _) } + + override string getAPrimaryQlClass() { result = "BitField" } + + /** + * Gets the size of this bitfield in bits (on the machine where facts + * were extracted). + */ + int getNumBits() { bitfield(underlyingElement(this), result, _) } + + /** + * Gets the value which appeared after the colon in the bitfield + * declaration. + * + * In most cases, this will give the same value as `getNumBits`. It will + * only differ when the value after the colon is larger than the size of + * the variable's type. For example, given `int32_t x : 1234`, + * `getNumBits` will give 32, whereas `getDeclaredNumBits` will give + * 1234. + */ + int getDeclaredNumBits() { bitfield(underlyingElement(this), _, result) } + + /** + * Gets the offset of this bitfield in bits from the byte identified by + * getByteOffset (on the machine where facts were extracted). + */ + int getBitOffset() { fieldoffsets(underlyingElement(this), _, result) } + + /** Holds if this bitfield is anonymous. */ + predicate isAnonymous() { hasName("(unnamed bitfield)") } + + override predicate isInitializable() { + // Anonymous bitfields are not initializable. + not isAnonymous() + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/File.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/File.qll new file mode 100644 index 00000000000..f486dd8d3c5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/File.qll @@ -0,0 +1,448 @@ +/** + * Provides classes representing files and folders. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.Declaration +import semmle.code.cpp.metrics.MetricFile + +/** A file or folder. */ +class Container extends Locatable, @container { + /** + * Gets the absolute, canonical path of this container, using forward slashes + * as path separator. + * + * The path starts with a _root prefix_ followed by zero or more _path + * segments_ separated by forward slashes. + * + * The root prefix is of one of the following forms: + * + * 1. A single forward slash `/` (Unix-style) + * 2. An upper-case drive letter followed by a colon and a forward slash, + * such as `C:/` (Windows-style) + * 3. Two forward slashes, a computer name, and then another forward slash, + * such as `//FileServer/` (UNC-style) + * + * Path segments are never empty (that is, absolute paths never contain two + * contiguous slashes, except as part of a UNC-style root prefix). Also, path + * segments never contain forward slashes, and no path segment is of the + * form `.` (one dot) or `..` (two dots). + * + * Note that an absolute path never ends with a forward slash, except if it is + * a bare root prefix, that is, the path has no path segments. A container + * whose absolute path has no segments is always a `Folder`, not a `File`. + */ + string getAbsolutePath() { none() } // overridden by subclasses + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets a URL representing the location of this container. + * + * For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls). + */ + deprecated string getURL() { none() } // overridden by subclasses + + /** + * Gets the relative path of this file or folder from the root folder of the + * analyzed source location. The relative path of the root folder itself is + * the empty string. + * + * This has no result if the container is outside the source root, that is, + * if the root folder is not a reflexive, transitive parent of this container. + */ + string getRelativePath() { + exists(string absPath, string pref | + absPath = getAbsolutePath() and sourceLocationPrefix(pref) + | + absPath = pref and result = "" + or + absPath = pref.regexpReplaceAll("/$", "") + "/" + result and + not result.matches("/%") + ) + } + + /** + * Gets the base name of this container including extension, that is, the last + * segment of its absolute path, or the empty string if it has no segments. + * + * Here are some examples of absolute paths and the corresponding base names + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + * + *
Absolute pathBase name
"/tmp/tst.js""tst.js"
"C:/Program Files (x86)""Program Files (x86)"
"/"""
"C:/"""
"D:/"""
"//FileServer/"""
+ */ + string getBaseName() { + result = getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1) + } + + /** + * Gets the extension of this container, that is, the suffix of its base name + * after the last dot character, if any. + * + * In particular, + * + * - if the name does not include a dot, there is no extension, so this + * predicate has no result; + * - if the name ends in a dot, the extension is the empty string; + * - if the name contains multiple dots, the extension follows the last dot. + * + * Here are some examples of absolute paths and the corresponding extensions + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathExtension
"/tmp/tst.js""js"
"/tmp/.classpath""classpath"
"/bin/bash"not defined
"/tmp/tst2."""
"/tmp/x.tar.gz""gz"
+ */ + string getExtension() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3) } + + /** + * Gets the stem of this container, that is, the prefix of its base name up to + * (but not including) the last dot character if there is one, or the entire + * base name if there is not. + * + * Here are some examples of absolute paths and the corresponding stems + * (surrounded with quotes to avoid ambiguity): + * + * + * + * + * + * + * + * + *
Absolute pathStem
"/tmp/tst.js""tst"
"/tmp/.classpath"""
"/bin/bash""bash"
"/tmp/tst2.""tst2"
"/tmp/x.tar.gz""x.tar"
+ */ + string getStem() { result = getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1) } + + /** Gets the parent container of this file or folder, if any. */ + Container getParentContainer() { + containerparent(unresolveElement(result), underlyingElement(this)) + } + + /** Gets a file or sub-folder in this container. */ + Container getAChildContainer() { this = result.getParentContainer() } + + /** Gets a file in this container. */ + File getAFile() { result = getAChildContainer() } + + /** Gets the file in this container that has the given `baseName`, if any. */ + File getFile(string baseName) { + result = getAFile() and + result.getBaseName() = baseName + } + + /** Gets a sub-folder in this container. */ + Folder getAFolder() { result = getAChildContainer() } + + /** Gets the sub-folder in this container that has the given `baseName`, if any. */ + Folder getFolder(string baseName) { + result = getAFolder() and + result.getBaseName() = baseName + } + + /** + * Gets a textual representation of the path of this container. + * + * This is the absolute path of the container. + */ + override string toString() { result = getAbsolutePath() } +} + +/** + * A folder that was observed on disk during the build process. + * + * For the example folder name of "/usr/home/me", the path decomposes to: + * + * 1. "/usr/home" - see `getParentContainer`. + * 2. "me" - see `getBaseName`. + * + * To get the full path, use `getAbsolutePath`. + */ +class Folder extends Container, @folder { + override string getAbsolutePath() { folders(underlyingElement(this), result) } + + override Location getLocation() { + result.getContainer() = this and + result.hasLocationInfo(_, 0, 0, 0, 0) + } + + override string getAPrimaryQlClass() { result = "Folder" } + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets the URL of this folder. + */ + deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Gets the name of this folder. + */ + deprecated string getName() { folders(underlyingElement(this), result) } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Holds if this element is named `name`. + */ + deprecated predicate hasName(string name) { name = this.getName() } + + /** + * DEPRECATED: use `getAbsolutePath` instead. + * Gets the full name of this folder. + */ + deprecated string getFullName() { result = this.getName() } + + /** + * DEPRECATED: use `getBaseName` instead. + * Gets the last part of the folder name. + */ + deprecated string getShortName() { result = this.getBaseName() } + + /** + * DEPRECATED: use `getParentContainer` instead. + * Gets the parent folder. + */ + deprecated Folder getParent() { + containerparent(unresolveElement(result), underlyingElement(this)) + } +} + +/** + * A file that was observed on disk during the build process. + * + * For the example filename of "/usr/home/me/myprogram.c", the filename + * decomposes to: + * + * 1. "/usr/home/me" - see `getParentContainer`. + * 2. "myprogram.c" - see `getBaseName`. + * + * The base name further decomposes into the _stem_ and _extension_ -- see + * `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`. + */ +class File extends Container, @file { + override string getAbsolutePath() { files(underlyingElement(this), result) } + + override string toString() { result = Container.super.toString() } + + override string getAPrimaryQlClass() { result = "File" } + + override Location getLocation() { + result.getContainer() = this and + result.hasLocationInfo(_, 0, 0, 0, 0) + } + + /** + * DEPRECATED: Use `getLocation` instead. + * Gets the URL of this file. + */ + deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } + + /** Holds if this file was compiled as C (at any point). */ + predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") } + + /** Holds if this file was compiled as C++ (at any point). */ + predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") } + + /** + * Holds if this file was compiled by a Microsoft compiler (at any point). + * + * Note: currently unreliable - on some projects only some of the files that + * are compiled by a Microsoft compiler are detected by this predicate. + */ + predicate compiledAsMicrosoft() { + exists(File f, Compilation c | + c.getAFileCompiled() = f and + ( + c.getAnArgument() = "--microsoft" or + c.getAnArgument() + .toLowerCase() + .replaceAll("\\", "/") + .matches(["%/cl.exe", "%/clang-cl.exe"]) + ) and + f.getAnIncludedFile*() = this + ) + } + + /** Gets a top-level element declared in this file. */ + Declaration getATopLevelDeclaration() { result.getAFile() = this and result.isTopLevel() } + + /** Gets a declaration in this file. */ + Declaration getADeclaration() { result.getAFile() = this } + + /** Holds if this file uses the given macro. */ + predicate usesMacro(Macro m) { + exists(MacroInvocation mi | + mi.getFile() = this and + mi.getMacro() = m + ) + } + + /** + * Gets a file that is directly included from this file (using a + * pre-processor directive like `#include`). + */ + File getAnIncludedFile() { + exists(Include i | i.getFile() = this and i.getIncludedFile() = result) + } + + /** + * Holds if this file may be from source. This predicate holds for all files + * except the dummy file, whose name is the empty string, which contains + * declarations that are built into the compiler. + */ + override predicate fromSource() { numlines(underlyingElement(this), _, _, _) } + + /** + * Holds if this file may be from a library. + * + * DEPRECATED: For historical reasons this is true for any file. + */ + deprecated override predicate fromLibrary() { any() } + + /** Gets the metric file. */ + MetricFile getMetrics() { result = this } + + /** + * Gets the remainder of the base name after the first dot character. Note + * that the name of this predicate is in plural form, unlike `getExtension`, + * which gets the remainder of the base name after the _last_ dot character. + * + * Predicates `getStem` and `getExtension` should be preferred over + * `getShortName` and `getExtensions` since the former pair is compatible + * with the file libraries of other languages. + * Note the slight difference between this predicate and `getStem`: + * for example, for "file.tar.gz", this predicate will have the result + * "tar.gz", while `getExtension` will have the result "gz". + */ + string getExtensions() { + exists(string name, int firstDotPos | + name = this.getBaseName() and + firstDotPos = min([name.indexOf("."), name.length() - 1]) and + result = name.suffix(firstDotPos + 1) + ) + } + + /** + * Gets the short name of this file, that is, the prefix of its base name up + * to (but not including) the first dot character if there is one, or the + * entire base name if there is not. For example, if the full name is + * "/path/to/filename.a.bcd" then the short name is "filename". + * + * Predicates `getStem` and `getExtension` should be preferred over + * `getShortName` and `getExtensions` since the former pair is compatible + * with the file libraries of other languages. + * Note the slight difference between this predicate and `getStem`: + * for example, for "file.tar.gz", this predicate will have the result + * "file", while `getStem` will have the result "file.tar". + */ + string getShortName() { + exists(string name, int firstDotPos | + name = this.getBaseName() and + firstDotPos = min([name.indexOf("."), name.length()]) and + result = name.prefix(firstDotPos) + ) + or + this.getAbsolutePath() = "" and + result = "" + } +} + +/** + * Holds if any file was compiled by a Microsoft compiler. + */ +predicate anyFileCompiledAsMicrosoft() { any(File f).compiledAsMicrosoft() } + +/** + * A C/C++ header file, as determined (mainly) by file extension. + * + * For the related notion of whether a file is included anywhere (using a + * pre-processor directive like `#include`), use `Include.getIncludedFile`. + */ +class HeaderFile extends File { + HeaderFile() { + this.getExtension().toLowerCase() = + ["h", "r", "hpp", "hxx", "h++", "hh", "hp", "tcc", "tpp", "txx", "t++"] + or + not exists(this.getExtension()) and + exists(Include i | i.getIncludedFile() = this) + } + + override string getAPrimaryQlClass() { result = "HeaderFile" } + + /** + * Holds if this header file does not contain any declaration entries or top level + * declarations. For example it might be: + * - a file containing only preprocessor directives and/or comments + * - an empty file + * - a file that contains non-top level code or data that's included in an + * unusual way + */ + predicate noTopLevelCode() { + not exists(DeclarationEntry de | de.getFile() = this) and + not exists(Declaration d | d.getFile() = this and d.isTopLevel()) and + not exists(UsingEntry ue | ue.getFile() = this) + } +} + +/** + * A C source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as C code, use + * `File.compiledAsC`. + */ +class CFile extends File { + CFile() { this.getExtension().toLowerCase() = ["c", "i"] } + + override string getAPrimaryQlClass() { result = "CFile" } +} + +/** + * A C++ source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as C++ code, use + * `File.compiledAsCpp`. + */ +class CppFile extends File { + CppFile() { + this.getExtension().toLowerCase() = + ["cpp", "cxx", "c++", "cc", "cp", "icc", "ipp", "ixx", "i++", "ii"] + // Note: .C files are indistinguishable from .c files on some + // file systems, so we just treat them as CFile's. + } + + override string getAPrimaryQlClass() { result = "CppFile" } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as Objective C + * code, use `File.compiledAsObjC`. + */ +deprecated class ObjCFile extends File { + ObjCFile() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C++ source file, as determined by file extension. + * + * For the related notion of whether a file is compiled as Objective C++ + * code, use `File.compiledAsObjCpp`. + */ +deprecated class ObjCppFile extends File { + ObjCppFile() { none() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/FriendDecl.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/FriendDecl.qll new file mode 100644 index 00000000000..9a35f7527d5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/FriendDecl.qll @@ -0,0 +1,69 @@ +/** + * Provides a class representing C++ `friend` declarations. + */ + +import semmle.code.cpp.Declaration +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C++ friend declaration [N4140 11.3]. For example the two friend + * declarations in class `A` of the following code: + * ``` + * class A { + * friend void f(int); + * friend class X; + * }; + * + * void f(int x) { ... } + * class X { ... }; + * ``` + */ +class FriendDecl extends Declaration, @frienddecl { + /** + * Gets the location of this friend declaration. The result is the + * location of the friend declaration itself, not the class or function + * that it refers to. Note: to get the target of the friend declaration, + * use `getFriend`. + */ + override Location getADeclarationLocation() { result = this.getLocation() } + + override string getAPrimaryQlClass() { result = "FriendDecl" } + + /** + * Implements the abstract method `Declaration.getDefinitionLocation`. A + * friend declaration cannot be a definition because it is only a link to + * another class or function. But we have to provide an implementation of + * this method, so we use the location of the declaration as the location + * of the definition. Note: to get the target of the friend declaration, + * use `getFriend`. + */ + override Location getDefinitionLocation() { result = this.getLocation() } + + /** Gets the location of this friend declaration. */ + override Location getLocation() { frienddecls(underlyingElement(this), _, _, result) } + + /** Gets a descriptive string for this friend declaration. */ + override string getName() { result = this.getDeclaringClass().getName() + "'s friend" } + + /** + * Friend declarations do not have specifiers. It makes no difference + * whether they are declared in a public, protected or private section of + * the class. + */ + override Specifier getASpecifier() { none() } + + /** + * Gets the target of this friend declaration. + * For example: `X` in `class A { friend class X }`. + */ + AccessHolder getFriend() { frienddecls(underlyingElement(this), _, unresolveElement(result), _) } + + /** + * Gets the declaring class (also known as the befriending class). + * For example: `A` in `class A { friend class X }`. + */ + Class getDeclaringClass() { frienddecls(underlyingElement(this), unresolveElement(result), _, _) } + + /* Holds if this declaration is a top-level declaration. */ + override predicate isTopLevel() { none() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Function.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Function.qll new file mode 100644 index 00000000000..6cae134645f --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Function.qll @@ -0,0 +1,876 @@ +/** + * Provides classes for working with functions, including template functions. + */ + +import semmle.code.cpp.Location +import semmle.code.cpp.Class +import semmle.code.cpp.Parameter +import semmle.code.cpp.exprs.Call +import semmle.code.cpp.metrics.MetricFunction +import semmle.code.cpp.Linkage +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C/C++ function [N4140 8.3.5]. Both member functions and non-member + * functions are included. For example the function `MyFunction` in: + * ``` + * void MyFunction() { + * DoSomething(); + * } + * ``` + * + * Function has a one-to-many relationship with FunctionDeclarationEntry, + * because the same function can be declared in multiple locations. This + * relationship between `Declaration` and `DeclarationEntry` is explained + * in more detail in `Declaration.qll`. + */ +class Function extends Declaration, ControlFlowNode, AccessHolder, @function { + override string getName() { functions(underlyingElement(this), result, _) } + + /** + * DEPRECATED: Use `getIdentityString(Declaration)` from `semmle.code.cpp.Print` instead. + * Gets the full signature of this function, including return type, parameter + * types, and template arguments. + * + * For example, in the following code: + * ``` + * template T min(T x, T y); + * int z = min(5, 7); + * ``` + * The full signature of the function called on the last line would be + * "min(int, int) -> int", and the full signature of the uninstantiated + * template on the first line would be "min(T, T) -> T". + */ + string getFullSignature() { + exists(string name, string templateArgs, string args | + result = name + templateArgs + args + " -> " + getType().toString() and + name = getQualifiedName() and + ( + if exists(getATemplateArgument()) + then + templateArgs = + "<" + + concat(int i | + exists(getTemplateArgument(i)) + | + getTemplateArgument(i).toString(), ", " order by i + ) + ">" + else templateArgs = "" + ) and + args = + "(" + + concat(int i | + exists(getParameter(i)) + | + getParameter(i).getType().toString(), ", " order by i + ) + ")" + ) + } + + /** Gets a specifier of this function. */ + override Specifier getASpecifier() { + funspecifiers(underlyingElement(this), unresolveElement(result)) or + result.hasName(getADeclarationEntry().getASpecifier()) + } + + /** Gets an attribute of this function. */ + Attribute getAnAttribute() { funcattributes(underlyingElement(this), unresolveElement(result)) } + + /** Holds if this function is generated by the compiler. */ + predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) } + + /** Holds if this function is inline. */ + predicate isInline() { this.hasSpecifier("inline") } + + /** + * Holds if this function is virtual. + * + * Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function + * is not explicitly declared with the `virtual` specifier. + */ + predicate isVirtual() { this.hasSpecifier("virtual") } + + /** Holds if this function is declared with the `virtual` specifier. */ + predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") } + + /** Holds if this function is declared with the `override` specifier. */ + predicate isOverride() { this.hasSpecifier("override") } + + /** Holds if this function is declared with the `final` specifier. */ + predicate isFinal() { this.hasSpecifier("final") } + + /** + * Holds if this function is deleted. + * This may be because it was explicitly deleted with an `= delete` + * definition, or because the compiler was unable to auto-generate a + * definition for it. + * + * Most implicitly deleted functions are omitted from the database. + * `Class.implicitCopyConstructorDeleted` and + * `Class.implicitCopyAssignmentOperatorDeleted` can be used to find + * whether a class would have had those members implicitly deleted. + */ + predicate isDeleted() { function_deleted(underlyingElement(this)) } + + /** + * Holds if this function is explicitly defaulted with the `= default` + * specifier. + */ + predicate isDefaulted() { function_defaulted(underlyingElement(this)) } + + /** + * Holds if this function is declared to be `constexpr`. + * + * Note that this does not hold if the function has been declared + * `consteval`. + */ + predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") } + + /** + * Holds if this function is `constexpr`. Normally, this holds if and + * only if `isDeclaredConstexpr()` holds, but in some circumstances + * they differ. For example, with + * ``` + * int f(int i) { return 6; } + * template constexpr int g(T x) { return f(x); } + * ``` + * `g` is declared constexpr, but is not constexpr. + * + * Will also hold if this function is `consteval`. + */ + predicate isConstexpr() { this.hasSpecifier("is_constexpr") } + + /** + * Holds if this function is declared to be `consteval`. + */ + predicate isConsteval() { this.hasSpecifier("is_consteval") } + + /** + * Holds if this function is declared with `__attribute__((naked))` or + * `__declspec(naked)`. + */ + predicate isNaked() { getAnAttribute().hasName("naked") } + + /** + * Holds if this function has a trailing return type. + * + * Note that this is true whether or not deduction took place. For example, + * this holds for both `e` and `f`, but not `g` or `h`: + * ``` + * auto e() -> int { return 0; } + * auto f() -> auto { return 0; } + * auto g() { return 0; } + * int h() { return 0; } + * ``` + */ + predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") } + + /** Gets the return type of this function. */ + Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets the return type of this function after specifiers have been deeply + * stripped and typedefs have been resolved. + */ + Type getUnspecifiedType() { result = getType().getUnspecifiedType() } + + /** + * Gets the nth parameter of this function. There is no result for the + * implicit `this` parameter, and there is no `...` varargs pseudo-parameter. + */ + Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) } + + /** + * Gets a parameter of this function. There is no result for the implicit + * `this` parameter, and there is no `...` varargs pseudo-parameter. + */ + Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) } + + /** + * Gets an access of this function. + * + * To get calls to this function, use `getACallToThisFunction` instead. + */ + FunctionAccess getAnAccess() { result.getTarget() = this } + + /** + * Gets the number of parameters of this function, _not_ including any + * implicit `this` parameter or any `...` varargs pseudo-parameter. + */ + int getNumberOfParameters() { result = count(this.getAParameter()) } + + /** + * Gets the number of parameters of this function, _including_ any implicit + * `this` parameter but _not_ including any `...` varargs pseudo-parameter. + */ + int getEffectiveNumberOfParameters() { + // This method is overridden in `MemberFunction`, where the result is + // adjusted to account for the implicit `this` parameter. + result = getNumberOfParameters() + } + + /** + * Gets a string representing the parameters of this function. + * + * For example: for a function `int Foo(int p1, int p2)` this would + * return `int p1, int p2`. + */ + string getParameterString() { + result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i) + } + + /** Gets a call to this function. */ + FunctionCall getACallToThisFunction() { result.getTarget() = this } + + /** + * Gets a declaration entry corresponding to this declaration. The + * relationship between `Declaration` and `DeclarationEntry` is explained + * in `Declaration.qll`. + */ + override FunctionDeclarationEntry getADeclarationEntry() { + if fun_decls(_, underlyingElement(this), _, _, _) + then declEntry(result) + else + exists(Function f | + this.isConstructedFrom(f) and + fun_decls(unresolveElement(result), unresolveElement(f), _, _, _) + ) + } + + private predicate declEntry(FunctionDeclarationEntry fde) { + fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and + // If one .cpp file specializes a function, and another calls the + // specialized function, then when extracting the second we only see an + // instantiation, not the specialization. We Therefore need to ignore + // any non-specialized declarations if there are any specialized ones. + (this.isSpecialization() implies fde.isSpecialization()) + } + + /** + * Gets the location of a `FunctionDeclarationEntry` corresponding to this + * declaration. + */ + override Location getADeclarationLocation() { result = getADeclarationEntry().getLocation() } + + /** Holds if this Function is a Template specialization. */ + predicate isSpecialization() { + exists(FunctionDeclarationEntry fde | + fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and + fde.isSpecialization() + ) + } + + /** + * Gets the declaration entry corresponding to this declaration that is a + * definition, if any. + */ + override FunctionDeclarationEntry getDefinition() { + result = getADeclarationEntry() and + result.isDefinition() + } + + /** Gets the location of the definition, if any. */ + override Location getDefinitionLocation() { + if exists(getDefinition()) + then result = getDefinition().getLocation() + else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation()) + } + + /** + * Gets the preferred location of this declaration. (The location of the + * definition, if possible.) + */ + override Location getLocation() { + if exists(getDefinition()) + then result = this.getDefinitionLocation() + else result = this.getADeclarationLocation() + } + + /** Gets a child declaration of this function. */ + Declaration getADeclaration() { result = this.getAParameter() } + + /** + * Gets the block that is the function body. + * + * For C++ functions whose body is a function try statement rather than a + * block, this gives the block guarded by the try statement. See + * `FunctionTryStmt` for further information. + */ + BlockStmt getBlock() { result.getParentScope() = this } + + /** Holds if this function has an entry point. */ + predicate hasEntryPoint() { exists(getEntryPoint()) } + + /** + * Gets the first node in this function's control flow graph. + * + * For most functions, this first node will be the `BlockStmt` returned by + * `getBlock`. However in C++, the first node can also be a + * `FunctionTryStmt`. + */ + Stmt getEntryPoint() { function_entry_point(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets the metric class. `MetricFunction` has methods for computing + * various metrics, such as "number of lines of code" and "number of + * function calls". + */ + MetricFunction getMetrics() { result = this } + + /** Holds if this function calls the function `f`. */ + predicate calls(Function f) { exists(Locatable l | this.calls(f, l)) } + + /** + * Holds if this function calls the function `f` in the `FunctionCall` + * expression `l`. + */ + predicate calls(Function f, Locatable l) { + exists(FunctionCall call | + call.getEnclosingFunction() = this and call.getTarget() = f and call = l + ) + or + exists(DestructorCall call | + call.getEnclosingFunction() = this and call.getTarget() = f and call = l + ) + } + + /** Holds if this function accesses a function or variable or enumerator `a`. */ + predicate accesses(Declaration a) { exists(Locatable l | this.accesses(a, l)) } + + /** + * Holds if this function accesses a function or variable or enumerator `a` + * in the `Access` expression `l`. + */ + predicate accesses(Declaration a, Locatable l) { + exists(Access access | + access.getEnclosingFunction() = this and + a = access.getTarget() and + access = l + ) + } + + /** Gets a variable that is written-to in this function. */ + Variable getAWrittenVariable() { + exists(ConstructorFieldInit cfi | + cfi.getEnclosingFunction() = this and result = cfi.getTarget() + ) + or + exists(VariableAccess va | + va = result.getAnAccess() and + va.isUsedAsLValue() and + va.getEnclosingFunction() = this + ) + } + + /** + * Gets the class of which this function, called `memberName`, is a member. + * + * Prefer to use `getDeclaringType()` or `getName()` directly if you do not + * need to reason about both. + */ + pragma[nomagic] + Class getClassAndName(string memberName) { + this.hasName(memberName) and + this.getDeclaringType() = result + } + + /** + * Implements `ControlFlowNode.getControlFlowScope`. The `Function` is + * used to represent the exit node of the control flow graph, so it is + * its own scope. + */ + override Function getControlFlowScope() { result = this } + + /** + * Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is + * used to represent the exit node of the control flow graph, so it + * has no enclosing statement. + */ + override Stmt getEnclosingStmt() { none() } + + /** + * Holds if this function has C linkage, as specified by one of its + * declaration entries. For example: `extern "C" void foo();`. + */ + predicate hasCLinkage() { getADeclarationEntry().hasCLinkage() } + + /** + * Holds if this function is constructed from `f` as a result + * of template instantiation. If so, it originates either from a template + * function or from a function nested in a template class. + */ + predicate isConstructedFrom(Function f) { + function_instantiation(underlyingElement(this), unresolveElement(f)) + } + + /** + * Holds if this function is defined in several files. This is illegal in + * C (though possible in some C++ compilers), and likely indicates that + * several functions that are not linked together have been compiled. An + * example would be a project with many 'main' functions. + */ + predicate isMultiplyDefined() { strictcount(getFile()) > 1 } + + /** Holds if this function is a varargs function. */ + predicate isVarargs() { hasSpecifier("varargs") } + + /** Gets a type that is specified to be thrown by the function. */ + Type getAThrownType() { result = getADeclarationEntry().getAThrownType() } + + /** + * Gets the `i`th type specified to be thrown by the function. + */ + Type getThrownType(int i) { result = getADeclarationEntry().getThrownType(i) } + + /** Holds if the function has an exception specification. */ + predicate hasExceptionSpecification() { getADeclarationEntry().hasExceptionSpecification() } + + /** Holds if this function has a `throw()` exception specification. */ + predicate isNoThrow() { getADeclarationEntry().isNoThrow() } + + /** Holds if this function has a `noexcept` exception specification. */ + predicate isNoExcept() { getADeclarationEntry().isNoExcept() } + + /** + * Gets a function that overloads this one. + * + * Note: if _overrides_ are wanted rather than _overloads_ then + * `MemberFunction::getAnOverridingFunction` should be used instead. + */ + Function getAnOverload() { + ( + // If this function is declared in a class, only consider other + // functions from the same class. + exists(string name, Class declaringType | + candGetAnOverloadMember(name, declaringType, this) and + candGetAnOverloadMember(name, declaringType, result) + ) + or + // Conversely, if this function is not + // declared in a class, only consider other functions not declared in a + // class. + exists(string name, Namespace namespace | + candGetAnOverloadNonMember(name, namespace, this) and + candGetAnOverloadNonMember(name, namespace, result) + ) + ) and + result != this and + // Instantiations and specializations don't participate in overload + // resolution. + not ( + this instanceof FunctionTemplateInstantiation or + result instanceof FunctionTemplateInstantiation + ) and + not ( + this instanceof FunctionTemplateSpecialization or + result instanceof FunctionTemplateSpecialization + ) + } + + /** Gets a link target which compiled or referenced this function. */ + LinkTarget getALinkTarget() { this = result.getAFunction() } + + /** + * Holds if this function is side-effect free (conservative + * approximation). + */ + predicate isSideEffectFree() { not this.mayHaveSideEffects() } + + /** + * Holds if this function may have side-effects; if in doubt, we assume it + * may. + */ + predicate mayHaveSideEffects() { + // If we cannot see the definition then we assume that it may have + // side-effects. + if exists(this.getEntryPoint()) + then + // If it might be globally impure (we don't care about it modifying + // temporaries) then it may have side-effects. + this.getEntryPoint().mayBeGloballyImpure() + or + // Constructor initializers are separate from the entry point ... + this.(Constructor).getAnInitializer().mayBeGloballyImpure() + or + // ... and likewise for destructors. + this.(Destructor).getADestruction().mayBeGloballyImpure() + else + // Unless it's a function that we know is side-effect free, it may + // have side-effects. + not this.hasGlobalOrStdName([ + "strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen", + "_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l", + "_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr", + "memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit", + "isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof" + ]) + } + + /** + * Gets the nearest enclosing AccessHolder. + */ + override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() } +} + +pragma[noinline] +private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) { + f.getName() = name and + f.getDeclaringType() = declaringType +} + +pragma[noinline] +private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) { + f.getName() = name and + f.getNamespace() = namespace and + not exists(f.getDeclaringType()) +} + +/** + * A particular declaration or definition of a C/C++ function. For example the + * declaration and definition of `MyFunction` in the following code are each a + * `FunctionDeclarationEntry`: + * ``` + * void MyFunction(); + * + * void MyFunction() { + * DoSomething(); + * } + * ``` + */ +class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl { + /** Gets the function which is being declared or defined. */ + override Function getDeclaration() { result = getFunction() } + + override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" } + + /** Gets the function which is being declared or defined. */ + Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) } + + /** Gets the name of the function. */ + override string getName() { fun_decls(underlyingElement(this), _, _, result, _) } + + /** + * Gets the return type of the function which is being declared or + * defined. + */ + override Type getType() { fun_decls(underlyingElement(this), _, unresolveElement(result), _, _) } + + /** Gets the location of this declaration entry. */ + override Location getLocation() { fun_decls(underlyingElement(this), _, _, _, result) } + + /** Gets a specifier associated with this declaration entry. */ + override string getASpecifier() { fun_decl_specifiers(underlyingElement(this), result) } + + /** + * Implements `Element.getEnclosingElement`. A function declaration does + * not have an enclosing element. + */ + override Element getEnclosingElement() { none() } + + /** + * Gets the typedef type (if any) used for this function declaration. As + * an example, the typedef type in the declaration of function foo in the + * following is Foo: + * + * typedef int Foo(); + * static Foo foo; + */ + TypedefType getTypedefType() { + fun_decl_typedef_type(underlyingElement(this), unresolveElement(result)) + } + + /** + * Gets the cyclomatic complexity of this function: + * + * The number of branching statements (if, while, do, for, switch, + * case, catch) plus the number of branching expressions (`?`, `&&`, + * `||`) plus one. + */ + int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(getBlock()) } + + /** + * If this is a function definition, get the block containing the + * function body. + */ + BlockStmt getBlock() { + this.isDefinition() and + result = getFunction().getBlock() and + result.getFile() = this.getFile() + } + + /** + * If this is a function definition, get the number of lines of code + * associated with it. + */ + pragma[noopt] + int getNumberOfLines() { + exists(BlockStmt b, Location l, int start, int end, int diff | b = getBlock() | + l = b.getLocation() and + start = l.getStartLine() and + end = l.getEndLine() and + diff = end - start and + result = diff + 1 + ) + } + + /** + * Gets the declaration entry for a parameter of this function + * declaration. + */ + ParameterDeclarationEntry getAParameterDeclarationEntry() { + result = getParameterDeclarationEntry(_) + } + + /** + * Gets the declaration entry for the nth parameter of this function + * declaration. + */ + ParameterDeclarationEntry getParameterDeclarationEntry(int n) { + param_decl_bind(unresolveElement(result), n, underlyingElement(this)) + } + + /** Gets the number of parameters of this function declaration. */ + int getNumberOfParameters() { result = count(this.getAParameterDeclarationEntry()) } + + /** + * Gets a string representing the parameters of this function declaration. + * + * For example: for a function 'int Foo(int p1, int p2)' this would + * return 'int p1, int p2'. + */ + string getParameterString() { + result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i) + } + + /** + * Holds if this declaration entry specifies C linkage: + * + * `extern "C" void foo();` + */ + predicate hasCLinkage() { getASpecifier() = "c_linkage" } + + /** Holds if this declaration entry has a void parameter list. */ + predicate hasVoidParamList() { getASpecifier() = "void_param_list" } + + /** Holds if this declaration is also a definition of its function. */ + override predicate isDefinition() { fun_def(underlyingElement(this)) } + + /** Holds if this declaration is a Template specialization. */ + predicate isSpecialization() { fun_specialized(underlyingElement(this)) } + + /** + * Holds if this declaration is an implicit function declaration, that is, + * where a function is used before it is declared (under older C standards). + */ + predicate isImplicit() { fun_implicit(underlyingElement(this)) } + + /** Gets a type that is specified to be thrown by the declared function. */ + Type getAThrownType() { result = getThrownType(_) } + + /** + * Gets the `i`th type specified to be thrown by the declared function + * (where `i` is indexed from 0). For example, if a function is declared + * to `throw(int,float)`, then the thrown type with index 0 would be + * `int`, and that with index 1 would be `float`. + */ + Type getThrownType(int i) { + fun_decl_throws(underlyingElement(this), i, unresolveElement(result)) + } + + /** + * If this declaration has a noexcept-specification [N4140 15.4], then + * this predicate returns the argument to `noexcept` if one was given. + */ + Expr getNoExceptExpr() { fun_decl_noexcept(underlyingElement(this), unresolveElement(result)) } + + /** + * Holds if the declared function has an exception specification [N4140 + * 15.4]. + */ + predicate hasExceptionSpecification() { + fun_decl_throws(underlyingElement(this), _, _) or + fun_decl_noexcept(underlyingElement(this), _) or + isNoThrow() or + isNoExcept() + } + + /** + * Holds if the declared function has a `throw()` exception specification. + */ + predicate isNoThrow() { fun_decl_empty_throws(underlyingElement(this)) } + + /** + * Holds if the declared function has an empty `noexcept` exception + * specification. + */ + predicate isNoExcept() { fun_decl_empty_noexcept(underlyingElement(this)) } +} + +/** + * A C/C++ non-member function (a function that is not a member of any + * class). For example, in the following code, `MyFunction` is a + * `TopLevelFunction` but `MyMemberFunction` is not: + * ``` + * void MyFunction() { + * DoSomething(); + * } + * + * class MyClass { + * public: + * void MyMemberFunction() { + * DoSomething(); + * } + * }; + * ``` + */ +class TopLevelFunction extends Function { + TopLevelFunction() { not this.isMember() } + + override string getAPrimaryQlClass() { result = "TopLevelFunction" } +} + +/** + * A C++ user-defined operator [N4140 13.5]. + */ +class Operator extends Function { + Operator() { functions(underlyingElement(this), _, 5) } + + override string getAPrimaryQlClass() { + not this instanceof MemberFunction and result = "Operator" + } +} + +/** + * A C++ function which has a non-empty template argument list. For example + * the function `myTemplateFunction` in the following code: + * ``` + * template + * void myTemplateFunction(T t) { + * ... + * } + * ``` + * + * This comprises function declarations which are immediately preceded by + * `template <...>`, where the "..." part is not empty, and therefore it does + * not include: + * + * 1. Full specializations of template functions, as they have an empty + * template argument list. + * 2. Instantiations of template functions, as they don't have an + * explicit template argument list. + * 3. Member functions of template classes - unless they have their own + * (non-empty) template argument list. + */ +class TemplateFunction extends Function { + TemplateFunction() { + is_function_template(underlyingElement(this)) and exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "TemplateFunction" } + + /** + * Gets a compiler-generated instantiation of this function template. + */ + Function getAnInstantiation() { + result.isConstructedFrom(this) and + not result.isSpecialization() + } + + /** + * Gets a full specialization of this function template. + * + * Note that unlike classes, functions overload rather than specialize + * partially. Therefore this does not include things which "look like" + * partial specializations, nor does it include full specializations of + * such things -- see FunctionTemplateSpecialization for further details. + */ + FunctionTemplateSpecialization getASpecialization() { result.getPrimaryTemplate() = this } +} + +/** + * A function that is an instantiation of a template. For example + * the instantiation `myTemplateFunction` in the following code: + * ``` + * template + * void myTemplateFunction(T t) { + * ... + * } + * + * void caller(int i) { + * myTemplateFunction(i); + * } + * ``` + */ +class FunctionTemplateInstantiation extends Function { + TemplateFunction tf; + + FunctionTemplateInstantiation() { tf.getAnInstantiation() = this } + + override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" } + + /** + * Gets the function template from which this instantiation was instantiated. + * + * Example: For `int const& std::min(int const&, int const&)`, returns `T const& min(T const&, T const&)`. + */ + TemplateFunction getTemplate() { result = tf } +} + +/** + * An explicit specialization of a C++ function template. For example the + * function `myTemplateFunction` in the following code: + * ``` + * template + * void myTemplateFunction(T t) { + * ... + * } + * + * template<> + * void myTemplateFunction(int i) { + * ... + * } + * ``` + * + * Note that unlike classes, functions overload rather than specialize + * partially. Therefore this only includes the last two of the following + * four definitions, and in particular does not include the second one: + * + * ``` + * template void f(T) {...} + * template void f(T*) {...} + * template <> void f(int *) {...} + * template <> void f(int *) {...} + * ``` + * + * Furthermore, this does not include compiler-generated instantiations of + * function templates. + * + * For further reference on function template specializations, see: + * http://www.gotw.ca/publications/mill17.htm + */ +class FunctionTemplateSpecialization extends Function { + FunctionTemplateSpecialization() { this.isSpecialization() } + + override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" } + + /** + * Gets the primary template for the specialization (the function template + * this specializes). + */ + TemplateFunction getPrimaryTemplate() { this.isConstructedFrom(result) } +} + +/** + * A GCC built-in function. For example: `__builtin___memcpy_chk`. + */ +class BuiltInFunction extends Function { + BuiltInFunction() { functions(underlyingElement(this), _, 6) } + + /** Gets a dummy location for the built-in function. */ + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } +} + +private predicate suppressUnusedThis(Function f) { any() } diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Include.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Include.qll new file mode 100644 index 00000000000..f21edb2651d --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Include.qll @@ -0,0 +1,69 @@ +/** + * Provides classes representing C/C++ `#include`, `#include_next`, and `#import` preprocessor + * directives. + */ + +import semmle.code.cpp.Preprocessor + +/** + * A C/C++ `#include`, `#include_next`, or `#import` preprocessor + * directive. The following example contains four different `Include` + * directives: + * ``` + * #include "header.h" + * #include + * #include_next + * #import + * ``` + */ +class Include extends PreprocessorDirective, @ppd_include { + override string toString() { result = "#include " + this.getIncludeText() } + + /** + * Gets the token which occurs after `#include`, for example `"filename"` + * or ``. + */ + string getIncludeText() { result = getHead() } + + /** Gets the file directly included by this `#include`. */ + File getIncludedFile() { includes(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets a file which might be transitively included by this `#include`. + * + * Note that as this isn't computed within the context of a particular + * translation unit, it is often a slight over-approximation. + */ + predicate provides(File l) { + exists(Include i | this.getAnInclude*() = i and i.getIncludedFile() = l) + } + + /** + * A `#include` which appears in the file directly included by this + * `#include`. + */ + Include getAnInclude() { this.getIncludedFile() = result.getFile() } +} + +/** + * A `#include_next` preprocessor directive (a non-standard extension to + * C/C++). For example the following code contains one `IncludeNext` directive: + * ``` + * #include_next + * ``` + */ +class IncludeNext extends Include, @ppd_include_next { + override string toString() { result = "#include_next " + getIncludeText() } +} + +/** + * A `#import` preprocessor directive (used heavily in Objective C, and + * supported by GCC as an extension in C). For example the following code + * contains one `Import` directive: + * ``` + * #import + * ``` + */ +class Import extends Include, @ppd_objc_import { + override string toString() { result = "#import " + getIncludeText() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Initializer.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Initializer.qll new file mode 100644 index 00000000000..64607af3393 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Initializer.qll @@ -0,0 +1,54 @@ +/** + * Provides the `Initializer` class, representing C/C++ declaration initializers. + */ + +import semmle.code.cpp.controlflow.ControlFlowGraph + +/** + * A C/C++ declaration initializer. For example the initializers `1`, `2` and + * `3` in the following code: + * ``` + * int myVariable = 1; + * + * enum myEnum { + * MYENUMCONST = 2 + * }; + * + * void myFunction(int param = 3) { + * ... + * } + * ``` + * But _not_ `4` in the following code: + * ``` + * int myUninitializedVariable; + * myUninitializedVariable = 4; + * ``` + * Instead, this is an `Assignment`. + */ +class Initializer extends ControlFlowNode, @initialiser { + override Location getLocation() { initialisers(underlyingElement(this), _, _, result) } + + override string getAPrimaryQlClass() { result = "Initializer" } + + /** Holds if this initializer is explicit in the source. */ + override predicate fromSource() { not this.getLocation() instanceof UnknownLocation } + + override string toString() { + if exists(getDeclaration()) + then result = "initializer for " + max(getDeclaration().getName()) + else result = "initializer" + } + + /** Gets the variable or enum constant being initialized. */ + Declaration getDeclaration() { + initialisers(underlyingElement(this), unresolveElement(result), _, _) + } + + /** Gets the initializing expression. */ + Expr getExpr() { initialisers(underlyingElement(this), _, unresolveElement(result), _) } + + /** Gets the function containing this control-flow node. */ + override Function getControlFlowScope() { result = this.getExpr().getEnclosingFunction() } + + override Stmt getEnclosingStmt() { result = this.getExpr().getEnclosingStmt() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Iteration.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Iteration.qll new file mode 100644 index 00000000000..d87306c4bab --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Iteration.qll @@ -0,0 +1,60 @@ +/** + * Provides classes for loop iteration variables. + */ + +import semmle.code.cpp.Variable + +/** + * A C/C++ variable which is used within the condition of a 'for' loop, and + * mutated within the update expression of the same 'for' loop. + */ +class LoopCounter extends Variable { + LoopCounter() { exists(ForStmt f | f.getAnIterationVariable() = this) } + + /** + * Gets an access of this variable within loop `f`. + */ + VariableAccess getVariableAccessInLoop(ForStmt f) { + this.getALoop() = f and + result.getEnclosingStmt().getParent*() = f and + this = result.getTarget() + } + + /** + * Gets a loop which uses this variable as its counter. + */ + ForStmt getALoop() { result.getAnIterationVariable() = this } +} + +/** + * A C/C++ variable which is used within the initialization, condition, or + * update expression of a 'for' loop. + */ +class LoopControlVariable extends Variable { + LoopControlVariable() { this = loopControlVariable(_) } + + /** + * Gets an access of this variable within loop `f`. + */ + VariableAccess getVariableAccessInLoop(ForStmt f) { + this.getALoop() = f and + result.getEnclosingStmt().getParent*() = f and + this = result.getTarget() + } + + /** + * Gets a loop which uses this variable as its control variable. + */ + ForStmt getALoop() { this = loopControlVariable(result) } +} + +/** + * Gets a control variable of loop `f`. + */ +private Variable loopControlVariable(ForStmt f) { + exists(Expr e | result.getAnAccess().getParent*() = e | + e = f.getControllingExpr() or + e = f.getInitialization().(ExprStmt).getExpr() or + e = f.getUpdate() + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Linkage.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Linkage.qll new file mode 100644 index 00000000000..54a6099eaef --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Linkage.qll @@ -0,0 +1,51 @@ +/** + * Proivdes the `LinkTarget` class representing linker invocations during the build process. + */ + +import semmle.code.cpp.Class +import semmle.code.cpp.File +import semmle.code.cpp.Function + +/** + * A linker call during the build process, typically resulting in an + * executable or a shared library. + * + * Note that if linkage information isn't captured as part of the snapshot, + * then everything is grouped together into a single dummy link target. + */ +class LinkTarget extends @link_target { + /** + * Gets the file which was built. + */ + File getBinary() { link_targets(this, unresolveElement(result)) } + + /** + * Holds if this is the dummy link target: if linkage information isn't + * captured as part of the snapshot, then everything is grouped together + * into a single dummy link target. + */ + predicate isDummy() { getBinary().getAbsolutePath() = "" } + + /** Gets a textual representation of this element. */ + string toString() { result = getBinary().getAbsolutePath() } + + /** + * Gets a function which was compiled into this link target, or had its + * declaration included by one of the translation units which contributed + * to this link target. + */ + Function getAFunction() { link_parent(unresolveElement(result), this) } + + /** + * Gets a class which had its declaration included by one of the + * translation units which contributed to this link target. + */ + Class getAClass() { link_parent(unresolveElement(result), this) } +} + +/** + * Holds if this database was created with the linker awareness feature + * switched on. + */ +cached +predicate isLinkerAwareExtracted() { exists(LinkTarget lt | not lt.isDummy()) } diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Location.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Location.qll new file mode 100644 index 00000000000..15ae2121255 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Location.qll @@ -0,0 +1,173 @@ +/** + * Provides classes and predicates for locations in the source code. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.File + +/** + * A location of a C/C++ artifact. + */ +class Location extends @location { + /** Gets the container corresponding to this location. */ + Container getContainer() { this.fullLocationInfo(result, _, _, _, _) } + + /** Gets the file corresponding to this location, if any. */ + File getFile() { result = this.getContainer() } + + /** Gets the 1-based line number (inclusive) where this location starts. */ + int getStartLine() { this.fullLocationInfo(_, result, _, _, _) } + + /** Gets the 1-based column number (inclusive) where this location starts. */ + int getStartColumn() { this.fullLocationInfo(_, _, result, _, _) } + + /** Gets the 1-based line number (inclusive) where this location ends. */ + int getEndLine() { this.fullLocationInfo(_, _, _, result, _) } + + /** Gets the 1-based column number (inclusive) where this location ends. */ + int getEndColumn() { this.fullLocationInfo(_, _, _, _, result) } + + /** + * Gets a textual representation of this element. + * + * The format is "file://filePath:startLine:startColumn:endLine:endColumn". + */ + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + | + toUrl(filepath, startline, startcolumn, endline, endcolumn, result) + ) + } + + /** + * Holds if this element is in the specified container. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline`. + * + * This predicate is similar to `hasLocationInfo`, but exposes the `Container` + * entity, rather than merely its path. + */ + predicate fullLocationInfo( + Container container, int startline, int startcolumn, int endline, int endcolumn + ) { + locations_default(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or + locations_expr(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) or + locations_stmt(this, unresolveElement(container), startline, startcolumn, endline, endcolumn) + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + exists(Container f | this.fullLocationInfo(f, startline, startcolumn, endline, endcolumn) | + filepath = f.getAbsolutePath() + ) + } + + /** Holds if `this` comes on a line strictly before `l`. */ + pragma[inline] + predicate isBefore(Location l) { + this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine() + } + + /** Holds if location `l` is completely contained within this one. */ + predicate subsumes(Location l) { + exists(File f | f = getFile() | + exists(int thisStart, int thisEnd | charLoc(f, thisStart, thisEnd) | + exists(int lStart, int lEnd | l.charLoc(f, lStart, lEnd) | + thisStart <= lStart and lEnd <= thisEnd + ) + ) + ) + } + + /** + * Holds if this location corresponds to file `f` and character "offsets" + * `start..end`. Note that these are not real character offsets, because + * we use `maxCols` to find the length of the longest line and then pretend + * that all the lines are the same length. However, these offsets are + * convenient for comparing or sorting locations in a file. For an example, + * see `subsumes`. + */ + predicate charLoc(File f, int start, int end) { + f = getFile() and + exists(int maxCols | maxCols = maxCols(f) | + start = getStartLine() * maxCols + getStartColumn() and + end = getEndLine() * maxCols + getEndColumn() + ) + } +} + +/** + * DEPRECATED: Use `Location` instead. + * A location of an element. Not used for expressions or statements, which + * instead use LocationExpr and LocationStmt respectively. + */ +deprecated library class LocationDefault extends Location, @location_default { } + +/** + * DEPRECATED: Use `Location` instead. + * A location of a statement. + */ +deprecated library class LocationStmt extends Location, @location_stmt { } + +/** + * DEPRECATED: Use `Location` instead. + * A location of an expression. + */ +deprecated library class LocationExpr extends Location, @location_expr { } + +/** + * Gets the length of the longest line in file `f`. + */ +pragma[nomagic] +private int maxCols(File f) { + result = max(Location l | l.getFile() = f | l.getStartColumn().maximum(l.getEndColumn())) +} + +/** + * A C/C++ element that has a location in a file + */ +class Locatable extends Element { } + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. There + * may be several distinct kinds of unknown locations. For example: one for + * expressions, one for statements and one for other program elements. + */ +class UnknownLocation extends Location { + UnknownLocation() { getFile().getAbsolutePath() = "" } +} + +/** + * A dummy location which is used when something doesn't have a location in + * the source code but needs to have a `Location` associated with it. + */ +class UnknownDefaultLocation extends UnknownLocation { + UnknownDefaultLocation() { locations_default(this, _, 0, 0, 0, 0) } +} + +/** + * A dummy location which is used when an expression doesn't have a + * location in the source code but needs to have a `Location` associated + * with it. + */ +class UnknownExprLocation extends UnknownLocation { + UnknownExprLocation() { locations_expr(this, _, 0, 0, 0, 0) } +} + +/** + * A dummy location which is used when a statement doesn't have a location + * in the source code but needs to have a `Location` associated with it. + */ +class UnknownStmtLocation extends UnknownLocation { + UnknownStmtLocation() { locations_stmt(this, _, 0, 0, 0, 0) } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Macro.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Macro.qll new file mode 100644 index 00000000000..aa4b8d41999 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Macro.qll @@ -0,0 +1,351 @@ +import cpp + +/** + * A macro. For example, the macro `MYMACRO` in the following code: + * ``` + * #define MYMACRO 1 + * ``` + */ +class Macro extends PreprocessorDirective, @ppd_define { + /** + * Gets the head of this macro. For example, `MAX(x,y)` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + override string getHead() { preproctext(underlyingElement(this), result, _) } + + override string getAPrimaryQlClass() { result = "Macro" } + + /** + * Gets the body of this macro. For example, `(((x)>(y))?(x):(y))` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + string getBody() { preproctext(underlyingElement(this), _, result) } + + /** Gets an invocation of this macro. */ + MacroInvocation getAnInvocation() { result.getMacro() = this } + + override string toString() { + if this.getBody() = "" + then result = "#define " + this.getHead() + else result = "#define " + this.getHead() + " " + this.getBody() + } + + /** + * Holds if the body of the macro starts with an unmatched closing + * parenthesis. For example: + * + * #define RPAREN() ) + * + * DEPRECATED: This predicate has a misleading name. + */ + deprecated predicate isFunctionLike() { this.getBody().regexpMatch("[^(]*\\).*") } + + /** + * Gets the name of the macro. For example, `MAX` in + * `#define MAX(x,y) (((x)>(y))?(x):(y))`. + */ + string getName() { result = getHead().splitAt("(", 0) } + + /** Holds if the macro has name `name`. */ + predicate hasName(string name) { getName() = name } +} + +/** + * A macro access. For example: + * ``` + * #ifdef MACRO1 // this line contains a MacroAccess + * int x = MACRO2; // this line contains a MacroAccess + * #endif + * ``` + * + * See also `MacroInvocation`, which represents only macro accesses + * that are expanded (such as in the second line of the example above). + */ +class MacroAccess extends Locatable, @macroinvocation { + /** Gets the macro that is being accessed. */ + Macro getMacro() { macroinvocations(underlyingElement(this), unresolveElement(result), _, _) } + + /** + * Gets the location of the outermost macro access that triggered this macro + * access. This is equivalent to calling + * `this.getOutermostMacroAccess().getActualLocation()`. For example, the + * location of the invocation of `C` in `P(C)` will be the whole source range + * starting with `P` and ending with `)`. + */ + override Location getLocation() { result = this.getOutermostMacroAccess().getActualLocation() } + + override string getAPrimaryQlClass() { result = "MacroAccess" } + + /** + * Gets the location of this macro access. For a nested access, where + * `exists(this.getParentInvocation())`, this yields a location either inside + * a `#define` directive or inside an argument to another macro. + */ + Location getActualLocation() { macroinvocations(underlyingElement(this), _, result, _) } + + /** + * Gets the parent macro invocation, if any. For example: + * + * ``` + * 1: #define C 0 + * 2: #define P C + * 3: static int x = P; + * ``` + * + * The invocation of `P` on line 3 also invokes `C`. The invocation of + * `P` is the parent of the invocation of `C`. + * + * A macro invocation occurring in a macro argument often also establishes a + * parent relationship. This is due to the "call-by-name" evaluation order of + * C macros, where macro arguments are first substituted textually into the + * macro body before macro expansion is again performed on the body, invoking + * the macros present in the original macro argument. For example: + * + * ``` + * 1: #define C 0 + * 2: #define P(c) c + c + * 3: static int x = P(C); + * ``` + * + * In this case, `P(C)` first expands to `C + C`, which triggers an + * invocation of `C` whose parent is the invocation of `P`. Had `c` not + * occurred in the body of `P`, there would have been no invocation of `C`. + * There is only a single invocation even though `c` occurs twice; this is an + * optimization for efficiency. + */ + MacroInvocation getParentInvocation() { + macroparent(underlyingElement(this), unresolveElement(result)) + } + + /** + * Gets the outermost `MacroAccess` along the chain of `getParentInvocation`. + * If `this` has no parent, the result will be `this` itself. + */ + MacroAccess getOutermostMacroAccess() { + if not exists(this.getParentInvocation()) + then result = this + else result = this.getParentInvocation().getOutermostMacroAccess() + } + + override string toString() { result = this.getMacro().getHead() } + + /** Gets the name of the accessed macro. */ + string getMacroName() { result = getMacro().getName() } +} + +/** + * A macro invocation (macro access that is expanded). For example: + * ``` + * #ifdef MACRO1 + * int x = MACRO2; // this line contains a MacroInvocation + * #endif + * ``` + * + * See also `MacroAccess`, which also represents macro accesses where the macro + * is checked but not expanded (such as in the first line of the example above). + */ +class MacroInvocation extends MacroAccess { + MacroInvocation() { macroinvocations(underlyingElement(this), _, _, 1) } + + override string getAPrimaryQlClass() { result = "MacroInvocation" } + + /** + * Gets an element that occurs in this macro invocation or a nested macro + * invocation. + */ + Locatable getAnExpandedElement() { + inmacroexpansion(unresolveElement(result), underlyingElement(this)) + } + + /** + * Gets an element that is (partially) affected by a macro + * invocation. This is a superset of the set of expanded elements and + * includes elements that are not completely enclosed by the expansion as + * well. + */ + Locatable getAnAffectedElement() { + inmacroexpansion(unresolveElement(result), underlyingElement(this)) or + macrolocationbind(underlyingElement(this), result.getLocation()) + } + + /** + * Gets an element that is either completely in the macro expansion, or + * (if it is a statement) 'almost' in the macro expansion (for instance + * up to a trailing semicolon). Useful for common patterns in which + * macros are almost syntactically complete elements but not quite. + */ + Locatable getAGeneratedElement() { + result = this.getAnExpandedElement() or + result.(Stmt).getGeneratingMacro() = this + } + + /** + * Gets a function that includes an expression that is affected by this macro + * invocation. If the macro expansion includes the end of one function and + * the beginning of another, this predicate will get both. + */ + Function getEnclosingFunction() { + result = this.getAnAffectedElement().(Expr).getEnclosingFunction() + } + + /** + * Gets a top-level expression associated with this macro invocation, + * if any. Note that this predicate will fail if the top-level expanded + * element is not an expression (for example if it is a statement). + * + * This macro is intended to be used with macros that expand to a complete + * expression. In other cases, it may have multiple results or no results. + */ + Expr getExpr() { + result = getAnExpandedElement() and + not result.getParent() = getAnExpandedElement() and + not result instanceof Conversion + } + + /** + * Gets the top-level statement associated with this macro invocation, if + * any. Note that this predicate will fail if the top-level expanded + * element is not a statement (for example if it is an expression). + */ + Stmt getStmt() { + result = getAnExpandedElement() and + not result.getParent() = getAnExpandedElement() + } + + /** + * Gets the `i`th _unexpanded_ argument of this macro invocation, where the + * first argument has `i = 0`. The result has been expanded for macro + * parameters but _not_ for macro invocations. This means that for macro + * invocations not inside a `#define`, which can have no macro parameters in + * their arguments, the result is equivalent to what is in the source text, + * modulo whitespace. + * + * In the following code example, the argument of the outermost invocation is + * `ID(1)` in unexpanded form and `1` in expanded form. + * + * ``` + * #define ID(x) x + * ID(ID(1)) + * ``` + * + * In the following example code, the last line contains an invocation of + * macro `A` and a child invocation of macro `ID`. The argument to `ID` is + * `1` in both unexpanded and expanded form because macro parameters (here, + * `x`) are expanded in both cases. + * + * ``` + * #define ID(x) x + * #define A(x) ID(x) + * A(1) + * ``` + * + * The `...` parameter in variadic macros counts as one parameter that always + * receives one argument, which may contain commas. + * + * Use `getExpandedArgument` to get the expanded form. + */ + string getUnexpandedArgument(int i) { + macro_argument_unexpanded(underlyingElement(this), i, result) + } + + /** + * Gets the `i`th _expanded_ argument of this macro invocation, where the + * first argument has `i = 0`. The result has been expanded for macros _and_ + * macro parameters. If the macro definition does not use this argument, the + * extractor will avoid computing the expanded form for efficiency, and the + * result will be "". + * + * See the documentation of `getUnexpandedArgument` for examples of the + * differences between expanded and unexpanded arguments. + */ + string getExpandedArgument(int i) { macro_argument_expanded(underlyingElement(this), i, result) } +} + +/** + * A top-level expression generated by a macro invocation. + * + * DEPRECATED: Use `MacroInvocation.getExpr()` directly to get an + * expression generated at the top-level of a macro invocation. Use + * `MacroInvocation.getAnAffectedElement()` to get any element generated + * by a macro invocation. + */ +deprecated class MacroInvocationExpr extends Expr { + MacroInvocationExpr() { exists(MacroInvocation i | this = i.getExpr()) } + + /** + * Gets the macro invocation of which this is the top-level expression. + */ + MacroInvocation getInvocation() { result.getExpr() = this } + + /** Gets the name of the invoked macro. */ + string getMacroName() { result = getInvocation().getMacroName() } +} + +/** + * A top-level statement generated by a macro invocation. + * + * DEPRECATED: Use `MacroInvocation.getStmt()` directly to get a + * statement generated at the top-level of a macro invocation. Use + * `MacroInvocation.getAnAffectedElement()` to get any element generated + * by a macro invocation. + */ +deprecated class MacroInvocationStmt extends Stmt { + MacroInvocationStmt() { exists(MacroInvocation i | this = i.getStmt()) } + + /** + * Gets the macro invocation of which this is the top-level statement. + */ + MacroInvocation getInvocation() { result.getStmt() = this } + + /** Gets the name of the invoked macro. */ + string getMacroName() { result = getInvocation().getMacroName() } +} + +/** Holds if `l` is the location of a macro. */ +predicate macroLocation(Location l) { macrolocationbind(_, l) } + +/** Holds if `element` is in the expansion of a macro. */ +predicate inMacroExpansion(Locatable element) { + inmacroexpansion(unresolveElement(element), _) + or + macroLocation(element.getLocation()) and + not topLevelMacroAccess(element) +} + +/** + * Holds if `ma` is a `MacroAccess` that is not nested inside another + * macro invocation. + */ +private predicate topLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) } + +/** + * Holds if `element` is in the expansion of a macro from + * a system header. + */ +predicate inSystemMacroExpansion(Locatable element) { + exists(MacroInvocation m | + element = m.getAnExpandedElement() and + not exists(m.getMacro().getLocation().getFile().getRelativePath()) + ) +} + +/** Holds if `element` is affected by a macro. */ +predicate affectedByMacro(Locatable element) { + inMacroExpansion(element) or + affectedbymacroexpansion(unresolveElement(element), _) +} + +/** Holds if there is a macro invocation on line `line` of file `f`. */ +predicate macroLine(File f, int line) { + exists(MacroInvocation mi, Location l | + l = mi.getLocation() and + l.getFile() = f and + (l.getStartLine() = line or l.getEndLine() = line) + ) +} + +/** Holds if there might be a macro invocation at location `l`. */ +predicate possibleMacroLocation(Location l) { + macroLine(l.getFile(), l.getStartLine()) or + macroLine(l.getFile(), l.getEndLine()) +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Member.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Member.qll new file mode 100644 index 00000000000..f47edbddeba --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Member.qll @@ -0,0 +1,6 @@ +/** + * DEPRECATED: import `semmle.code.cpp.Element` and/or `semmle.code.cpp.Type` directly as required. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.Type diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll new file mode 100644 index 00000000000..63c1406d8a5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/MemberFunction.qll @@ -0,0 +1,517 @@ +/** + * Provides classes for working with C++ member functions, constructors, destructors, + * and user-defined operators. + */ + +import cpp + +/** + * A C++ function declared as a member of a class [N4140 9.3]. This includes + * static member functions. For example the functions `MyStaticMemberFunction` + * and `MyMemberFunction` in: + * ``` + * class MyClass { + * public: + * void MyMemberFunction() { + * DoSomething(); + * } + * + * static void MyStaticMemberFunction() { + * DoSomething(); + * } + * }; + * ``` + */ +class MemberFunction extends Function { + MemberFunction() { this.isMember() } + + override string getAPrimaryQlClass() { + not this instanceof CopyAssignmentOperator and + not this instanceof MoveAssignmentOperator and + result = "MemberFunction" + } + + /** + * Gets the number of parameters of this function, including any implicit + * `this` parameter. + */ + override int getEffectiveNumberOfParameters() { + if isStatic() then result = getNumberOfParameters() else result = getNumberOfParameters() + 1 + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } + + /** Holds if this declaration has the lvalue ref-qualifier */ + predicate isLValueRefQualified() { hasSpecifier("&") } + + /** Holds if this declaration has the rvalue ref-qualifier */ + predicate isRValueRefQualified() { hasSpecifier("&&") } + + /** Holds if this declaration has a ref-qualifier */ + predicate isRefQualified() { isLValueRefQualified() or isRValueRefQualified() } + + /** Holds if this function overrides that function. */ + predicate overrides(MemberFunction that) { + overrides(underlyingElement(this), unresolveElement(that)) + } + + /** Gets a directly overridden function. */ + MemberFunction getAnOverriddenFunction() { this.overrides(result) } + + /** Gets a directly overriding function. */ + MemberFunction getAnOverridingFunction() { result.overrides(this) } + + /** + * Gets the declaration entry for this member function that is within the + * class body. + */ + FunctionDeclarationEntry getClassBodyDeclarationEntry() { + if strictcount(getADeclarationEntry()) = 1 + then result = getDefinition() + else ( + result = getADeclarationEntry() and result != getDefinition() + ) + } + + /** + * Gets the type of the `this` parameter associated with this member function, if any. The type + * may have `const` and/or `volatile` qualifiers, matching the function declaration. + */ + PointerType getTypeOfThis() { + member_function_this_type(underlyingElement(this), unresolveElement(result)) + } +} + +/** + * A C++ virtual function. For example the two functions called + * `myVirtualFunction` in the following code are each a + * `VirtualFunction`: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class VirtualFunction extends MemberFunction { + VirtualFunction() { this.hasSpecifier("virtual") or purefunctions(underlyingElement(this)) } + + override string getAPrimaryQlClass() { result = "VirtualFunction" } + + /** Holds if this virtual function is pure. */ + predicate isPure() { this instanceof PureVirtualFunction } + + /** + * Holds if this function was declared with the `override` specifier + * [N4140 10.3]. + */ + predicate isOverrideExplicit() { this.hasSpecifier("override") } +} + +/** + * A C++ pure virtual function [N4140 10.4]. For example the first function + * called `myVirtualFunction` in the following code: + * ``` + * class A { + * public: + * virtual void myVirtualFunction() = 0; + * }; + * + * class B: public A { + * public: + * virtual void myVirtualFunction() { + * doSomething(); + * } + * }; + * ``` + */ +class PureVirtualFunction extends VirtualFunction { + PureVirtualFunction() { purefunctions(underlyingElement(this)) } + + override string getAPrimaryQlClass() { result = "PureVirtualFunction" } +} + +/** + * A const C++ member function [N4140 9.3.1/4]. A const function has the + * `const` specifier and does not modify the state of its class. For example + * the member function `day` in the following code: + * ``` + * class MyClass { + * ... + * + * int day() const { + * return d; + * } + * + * ... + * }; + * ``` + */ +class ConstMemberFunction extends MemberFunction { + ConstMemberFunction() { this.hasSpecifier("const") } + + override string getAPrimaryQlClass() { result = "ConstMemberFunction" } +} + +/** + * A C++ constructor [N4140 12.1]. For example the function `MyClass` in the + * following code is a constructor: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class Constructor extends MemberFunction { + Constructor() { functions(underlyingElement(this), _, 2) } + + override string getAPrimaryQlClass() { result = "Constructor" } + + /** + * Holds if this constructor serves as a default constructor. + * + * This holds for constructors with zero formal parameters. It also holds + * for constructors which have a non-zero number of formal parameters, + * provided that every parameter has a default value. + */ + predicate isDefault() { forall(Parameter p | p = this.getAParameter() | p.hasInitializer()) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. + */ + ConstructorInit getAnInitializer() { result = getInitializer(_) } + + /** + * Gets an entry in the constructor's initializer list, or a + * compiler-generated action which initializes a base class or member + * variable. The index specifies the order in which the initializer is + * to be evaluated. + */ + ConstructorInit getInitializer(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A function that defines an implicit conversion. + */ +class ImplicitConversionFunction extends MemberFunction { + ImplicitConversionFunction() { + // ConversionOperator + functions(underlyingElement(this), _, 4) + or + // ConversionConstructor (deprecated) + strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and + not hasSpecifier("explicit") + } + + /** Gets the type this `ImplicitConversionFunction` takes as input. */ + Type getSourceType() { none() } // overridden in subclasses + + /** Gets the type this `ImplicitConversionFunction` converts to. */ + Type getDestType() { none() } // overridden in subclasses +} + +/** + * DEPRECATED: as of C++11 this class does not correspond perfectly with the + * language definition of a converting constructor. + * + * A C++ constructor that also defines an implicit conversion. For example the + * function `MyClass` in the following code is a `ConversionConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyOtherClass &from) { + * ... + * } + * }; + * ``` + */ +deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction { + ConversionConstructor() { + strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and + not hasSpecifier("explicit") + } + + override string getAPrimaryQlClass() { + not this instanceof CopyConstructor and + not this instanceof MoveConstructor and + result = "ConversionConstructor" + } + + /** Gets the type this `ConversionConstructor` takes as input. */ + override Type getSourceType() { result = this.getParameter(0).getType() } + + /** Gets the type this `ConversionConstructor` is a constructor of. */ + override Type getDestType() { result = this.getDeclaringType() } +} + +private predicate hasCopySignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType() +} + +private predicate hasMoveSignature(MemberFunction f) { + f.getParameter(0).getUnspecifiedType().(RValueReferenceType).getBaseType() = f.getDeclaringType() +} + +/** + * A C++ copy constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `CopyConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(const MyClass &from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a copy constructor of class `T` is a non-template + * constructor whose first parameter has type `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`, and either there are no other parameters, + * or the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a copy constructor. For such classes, `CopyConstructor` + * over-approximates the set of copy constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeCopyConstructorInInstantiation`. + */ +class CopyConstructor extends Constructor { + CopyConstructor() { + hasCopySignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + or + // or this is a template class, in which case the default values have + // not been extracted even if they exist. In that case, we assume that + // there are default values present since that is the most common case + // in real-world code. + getDeclaringType() instanceof TemplateClass + ) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "CopyConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a copy + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a copy + * constructor. + */ + predicate mayNotBeCopyConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // type-checked for each template instantiation; if an argument in an + // instantiation fails to type-check then the corresponding parameter has + // no default argument in the instantiation. + getDeclaringType() instanceof TemplateClass and + getNumberOfParameters() > 1 + } +} + +/** + * A C++ move constructor [N4140 12.8]. For example the function `MyClass` in + * the following code is a `MoveConstructor`: + * ``` + * class MyClass { + * public: + * MyClass(MyClass &&from) { + * ... + * } + * }; + * ``` + * + * As per the standard, a move constructor of class `T` is a non-template + * constructor whose first parameter is `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`, and either there are no other parameters, or + * the rest of the parameters all have default values. + * + * For template classes, it can generally not be determined until instantiation + * whether a constructor is a move constructor. For such classes, `MoveConstructor` + * over-approximates the set of move constructors; if an under-approximation is + * desired instead, see the member predicate + * `mayNotBeMoveConstructorInInstantiation`. + */ +class MoveConstructor extends Constructor { + MoveConstructor() { + hasMoveSignature(this) and + ( + // The rest of the parameters all have default values + forall(int i | i > 0 and exists(getParameter(i)) | getParameter(i).hasInitializer()) + or + // or this is a template class, in which case the default values have + // not been extracted even if they exist. In that case, we assume that + // there are default values present since that is the most common case + // in real-world code. + getDeclaringType() instanceof TemplateClass + ) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "MoveConstructor" } + + /** + * Holds if we cannot determine that this constructor will become a move + * constructor in all instantiations. Depending on template parameters of the + * enclosing class, this may become an ordinary constructor or a move + * constructor. + */ + predicate mayNotBeMoveConstructorInInstantiation() { + // In general, default arguments of template classes can only be + // type-checked for each template instantiation; if an argument in an + // instantiation fails to type-check then the corresponding parameter has + // no default argument in the instantiation. + getDeclaringType() instanceof TemplateClass and + getNumberOfParameters() > 1 + } +} + +/** + * A C++ constructor that takes no arguments ('default' constructor). This + * is the constructor that is invoked when no initializer is given. For + * example the function `MyClass` in the following code is a + * `NoArgConstructor`: + * ``` + * class MyClass { + * public: + * MyClass() { + * ... + * } + * }; + * ``` + */ +class NoArgConstructor extends Constructor { + NoArgConstructor() { this.getNumberOfParameters() = 0 } +} + +/** + * A C++ destructor [N4140 12.4]. For example the function `~MyClass` in the + * following code is a destructor: + * ``` + * class MyClass { + * public: + * ~MyClass() { + * ... + * } + * }; + * ``` + */ +class Destructor extends MemberFunction { + Destructor() { functions(underlyingElement(this), _, 3) } + + override string getAPrimaryQlClass() { result = "Destructor" } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. + */ + DestructorDestruction getADestruction() { result = getDestruction(_) } + + /** + * Gets a compiler-generated action which destructs a base class or member + * variable. The index specifies the order in which the destruction should + * be evaluated. + */ + DestructorDestruction getDestruction(int i) { + exprparents(unresolveElement(result), i, underlyingElement(this)) + } +} + +/** + * A C++ conversion operator [N4140 12.3.2]. For example the function + * `operator int` in the following code is a `ConversionOperator`: + * ``` + * class MyClass { + * public: + * operator int(); + * }; + * ``` + */ +class ConversionOperator extends MemberFunction, ImplicitConversionFunction { + ConversionOperator() { functions(underlyingElement(this), _, 4) } + + override string getAPrimaryQlClass() { result = "ConversionOperator" } + + override Type getSourceType() { result = this.getDeclaringType() } + + override Type getDestType() { result = this.getType() } +} + +/** + * A C++ copy assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `CopyAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(const MyClass &other); + * }; + * ``` + * + * As per the standard, a copy assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T`, `T&`, `const T&`, `volatile + * T&`, or `const volatile T&`. + */ +class CopyAssignmentOperator extends Operator { + CopyAssignmentOperator() { + hasName("operator=") and + ( + hasCopySignature(this) + or + // Unlike CopyConstructor, this member allows a non-reference + // parameter. + getParameter(0).getUnspecifiedType() = getDeclaringType() + ) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "CopyAssignmentOperator" } +} + +/** + * A C++ move assignment operator [N4140 12.8]. For example the function + * `operator=` in the following code is a `MoveAssignmentOperator`: + * ``` + * class MyClass { + * public: + * MyClass &operator=(MyClass &&other); + * }; + * ``` + * + * As per the standard, a move assignment operator of class `T` is a + * non-template non-static member function with the name `operator=` that + * takes exactly one parameter of type `T&&`, `const T&&`, `volatile T&&`, + * or `const volatile T&&`. + */ +class MoveAssignmentOperator extends Operator { + MoveAssignmentOperator() { + hasName("operator=") and + hasMoveSignature(this) and + not exists(this.getParameter(1)) and + not exists(getATemplateArgument()) + } + + override string getAPrimaryQlClass() { result = "MoveAssignmentOperator" } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll new file mode 100644 index 00000000000..042ee10700a --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NameQualifiers.qll @@ -0,0 +1,167 @@ +/** + * Provides classes for working with name qualifiers such as the `N::` in + * `N::f()`. + */ + +import cpp + +/** + * A C++ name qualifier, for example `N::` in the following code: + * ``` + * namespace N { + * int f() { + * ... + * } + * } + * + * int g() { + * return N::f(); + * } + * ``` + */ +class NameQualifier extends NameQualifiableElement, @namequalifier { + /** + * Gets the expression ultimately qualified by the chain of name + * qualifiers. For example, `f()` in `N1::N2::f()`. + */ + Expr getExpr() { result = getQualifiedElement+() } + + /** Gets a location for this name qualifier. */ + override Location getLocation() { namequalifiers(underlyingElement(this), _, _, result) } + + /** + * Gets the name qualifier that qualifies this name qualifier, if any. + * This is used for name qualifier chains, for example the name qualifier + * `N2::` has a name qualifier `N1::` in the chain `N1::N2::f()`. + */ + override NameQualifier getNameQualifier() { + namequalifiers(unresolveElement(result), underlyingElement(this), _, _) + } + + /** + * Gets the element qualified by this name qualifier. For example, `f()` + * in `N::f()`. + */ + NameQualifiableElement getQualifiedElement() { + namequalifiers(underlyingElement(this), unresolveElement(result), _, _) + } + + /** + * Gets the qualifying element of this name qualifier. For example, `N` + * in `N::f()`. + */ + NameQualifyingElement getQualifyingElement() { + exists(NameQualifyingElement nqe | + namequalifiers(underlyingElement(this), _, unresolveElement(nqe), _) and + if nqe instanceof SpecialNameQualifyingElement + then + exists(Access a | + a = getQualifiedElement() and + result = a.getTarget().getDeclaringType() + ) + or + exists(FunctionCall c | + c = getQualifiedElement() and + result = c.getTarget().getDeclaringType() + ) + else result = nqe + ) + } + + override string toString() { + exists(NameQualifyingElement nqe | + namequalifiers(underlyingElement(this), _, unresolveElement(nqe), _) and + result = nqe.getName() + "::" + ) + } +} + +/** + * A C++ element that can be qualified with a name. This is in practice + * either an expression or a name qualifier. For example, there are two + * name-qualifiable elements in the following code, the expression `f()` + * (which is qualified by `N::`), and the qualifier `N::` (which is not + * itself qualified in this example): + * ``` + * namespace N { + * int f() { + * ... + * } + * } + * + * int g() { + * return N::f(); + * } + * ``` + */ +class NameQualifiableElement extends Element, @namequalifiableelement { + /** + * Gets the name qualifier associated with this element. For example, the + * name qualifier of `N::f()` is `N`. + */ + NameQualifier getNameQualifier() { + namequalifiers(unresolveElement(result), underlyingElement(this), _, _) + } + + /** + * Holds if this element has a globally qualified name. For example, + * `::x` is globally qualified. It is used to refer to `x` in the global + * namespace. + */ + predicate hasGlobalQualifiedName() { + getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace + } + + /** + * Holds if this element has a `__super`-qualified name. For example: + * `__super::get()`. Note: `__super` is non-standard C++ extension, only + * supported by some C++ compilers. + */ + predicate hasSuperQualifiedName() { + exists(NameQualifier nq, SpecialNameQualifyingElement snqe | + nq = getNameQualifier*() and + namequalifiers(unresolveElement(nq), _, unresolveElement(snqe), _) and + snqe.getName() = "__super" + ) + } +} + +/** + * A C++ element that can qualify a name. For example, the namespaces `A` and + * `A::B` and the class `A::C` in the following code: + * ``` + * namespace A { + * namespace B { + * ... + * } + * + * class C { + * ... + * }; + * } + * ``` + */ +class NameQualifyingElement extends Element, @namequalifyingelement { + /** + * Gets a name qualifier for which this is the qualifying namespace or + * user-defined type. For example: class `X` is the + * `NameQualifyingElement` and `X::` is the `NameQualifier`. + */ + NameQualifier getANameQualifier() { + namequalifiers(unresolveElement(result), _, underlyingElement(this), _) + } + + /** Gets the name of this namespace or user-defined type. */ + string getName() { none() } +} + +/** + * A special name-qualifying element. For example: `__super`. + */ +library class SpecialNameQualifyingElement extends NameQualifyingElement, + @specialnamequalifyingelement { + /** Gets the name of this special qualifying element. */ + override string getName() { specialnamequalifyingelements(underlyingElement(this), result) } + + override string toString() { result = getName() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Namespace.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Namespace.qll new file mode 100644 index 00000000000..d46abc6b4db --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Namespace.qll @@ -0,0 +1,249 @@ +/** + * Provides classes for modeling namespaces, `using` directives and `using` declarations. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.Type +import semmle.code.cpp.metrics.MetricNamespace + +/** + * A C++ namespace. For example the (single) namespace `A` in the following + * code: + * ``` + * namespace A + * { + * // ... + * } + * + * // ... + * + * namespace A + * { + * // ... + * } + * ``` + * Note that namespaces are somewhat nebulous entities, as they do not in + * general have a single well-defined location in the source code. The + * related notion of a `NamespaceDeclarationEntry` is rather more concrete, + * and should be used when a location is required. For example, the `std::` + * namespace is particularly nebulous, as parts of it are defined across a + * wide range of headers. As a more extreme example, the global namespace + * is never explicitly declared, but might correspond to a large proportion + * of the source code. + */ +class Namespace extends NameQualifyingElement, @namespace { + /** + * Gets the location of the namespace. Most namespaces do not have a + * single well-defined source location, so a dummy location is returned, + * unless the namespace has exactly one declaration entry. + */ + override Location getLocation() { + if strictcount(getADeclarationEntry()) = 1 + then result = getADeclarationEntry().getLocation() + else result instanceof UnknownDefaultLocation + } + + /** Gets the simple name of this namespace. */ + override string getName() { namespaces(underlyingElement(this), result) } + + /** Holds if this element is named `name`. */ + predicate hasName(string name) { name = this.getName() } + + /** Holds if this namespace is anonymous. */ + predicate isAnonymous() { hasName("(unnamed namespace)") } + + /** Gets the name of the parent namespace, if it exists. */ + private string getParentName() { + result = this.getParentNamespace().getName() and + result != "" + } + + /** Gets the qualified name of this namespace. For example: `a::b`. */ + string getQualifiedName() { + if exists(getParentName()) + then result = getParentNamespace().getQualifiedName() + "::" + getName() + else result = getName() + } + + /** Gets the parent namespace, if any. */ + Namespace getParentNamespace() { + namespacembrs(unresolveElement(result), underlyingElement(this)) + or + not namespacembrs(_, underlyingElement(this)) and result instanceof GlobalNamespace + } + + /** Gets a child declaration of this namespace. */ + Declaration getADeclaration() { namespacembrs(underlyingElement(this), unresolveElement(result)) } + + /** Gets a child namespace of this namespace. */ + Namespace getAChildNamespace() { + namespacembrs(underlyingElement(this), unresolveElement(result)) + } + + /** Holds if the namespace is inline. */ + predicate isInline() { namespace_inline(underlyingElement(this)) } + + /** Holds if this namespace may be from source. */ + override predicate fromSource() { this.getADeclaration().fromSource() } + + /** + * Holds if this namespace is in a library. + * + * DEPRECATED: never holds. + */ + deprecated override predicate fromLibrary() { not this.fromSource() } + + /** Gets the metric namespace. */ + MetricNamespace getMetrics() { result = this } + + /** Gets a version of the `QualifiedName` that is more suitable for display purposes. */ + string getFriendlyName() { result = this.getQualifiedName() } + + final override string toString() { result = getFriendlyName() } + + /** Gets a declaration of (part of) this namespace. */ + NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this } + + /** Gets a file which declares (part of) this namespace. */ + File getAFile() { result = this.getADeclarationEntry().getLocation().getFile() } +} + +/** + * A declaration of (part of) a C++ namespace. This corresponds to a single + * `namespace N { ... }` occurrence in the source code. For example the two + * mentions of `A` in the following code: + * ``` + * namespace A + * { + * // ... + * } + * + * // ... + * + * namespace A + * { + * // ... + * } + * ``` + */ +class NamespaceDeclarationEntry extends Locatable, @namespace_decl { + /** + * Get the namespace that this declaration entry corresponds to. There + * is a one-to-many relationship between `Namespace` and + * `NamespaceDeclarationEntry`. + */ + Namespace getNamespace() { + namespace_decls(underlyingElement(this), unresolveElement(result), _, _) + } + + override string toString() { result = this.getNamespace().getFriendlyName() } + + /** + * Gets the location of the token preceding the namespace declaration + * entry's body. + * + * For named declarations, such as "namespace MyStuff { ... }", this will + * give the "MyStuff" token. + * + * For anonymous declarations, such as "namespace { ... }", this will + * give the "namespace" token. + */ + override Location getLocation() { namespace_decls(underlyingElement(this), _, result, _) } + + /** + * Gets the location of the namespace declaration entry's body. For + * example: the "{ ... }" in "namespace N { ... }". + */ + Location getBodyLocation() { namespace_decls(underlyingElement(this), _, _, result) } + + override string getAPrimaryQlClass() { result = "NamespaceDeclarationEntry" } +} + +/** + * A C++ `using` directive or `using` declaration. + */ +class UsingEntry extends Locatable, @using { + override Location getLocation() { usings(underlyingElement(this), _, result) } +} + +/** + * A C++ `using` declaration. For example: + * ``` + * using std::string; + * ``` + */ +class UsingDeclarationEntry extends UsingEntry { + UsingDeclarationEntry() { + not exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _)) + } + + /** + * Gets the declaration that is referenced by this using declaration. For + * example, `std::string` in `using std::string`. + */ + Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) } + + override string toString() { result = "using " + this.getDeclaration().getDescription() } +} + +/** + * A C++ `using` directive. For example: + * ``` + * using namespace std; + * ``` + */ +class UsingDirectiveEntry extends UsingEntry { + UsingDirectiveEntry() { + exists(Namespace n | usings(underlyingElement(this), unresolveElement(n), _)) + } + + /** + * Gets the namespace that is referenced by this using directive. For + * example, `std` in `using namespace std`. + */ + Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) } + + override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() } +} + +/** + * Holds if `g` is an instance of `GlobalNamespace`. This predicate + * is used suppress a warning in `GlobalNamespace.getADeclaration()` + * by providing a fake use of `this`. + */ +private predicate suppressWarningForUnused(GlobalNamespace g) { any() } + +/** + * The C/C++ global namespace. + */ +class GlobalNamespace extends Namespace { + GlobalNamespace() { this.hasName("") } + + override Declaration getADeclaration() { + suppressWarningForUnused(this) and + result.isTopLevel() and + not namespacembrs(_, unresolveElement(result)) + } + + /** Gets a child namespace of the global namespace. */ + override Namespace getAChildNamespace() { + suppressWarningForUnused(this) and + not namespacembrs(unresolveElement(result), _) + } + + override Namespace getParentNamespace() { none() } + + /** + * DEPRECATED: use `getName()`. + */ + deprecated string getFullName() { result = this.getName() } + + override string getFriendlyName() { result = "(global namespace)" } +} + +/** + * The C++ `std::` namespace. + */ +class StdNamespace extends Namespace { + StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NestedFields.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NestedFields.qll new file mode 100644 index 00000000000..ce67719a7e2 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/NestedFields.qll @@ -0,0 +1,69 @@ +/** + * Provides a class for reasoning about nested field accesses, for example + * the access `myLine.start.x`. + */ + +import cpp + +/** + * Gets a `Field` that is within the given `Struct`, either directly or nested + * inside one or more levels of member structs. + */ +private Field getANestedField(Struct s) { + result = s.getAField() + or + exists(NestedStruct ns | + s = ns.getDeclaringType() and + result = getANestedField(ns) + ) +} + +/** + * Unwraps a series of field accesses to determine the outer-most qualifier. + */ +private Expr getUltimateQualifier(FieldAccess fa) { + exists(Expr qualifier | qualifier = fa.getQualifier() | + result = getUltimateQualifier(qualifier) + or + not qualifier instanceof FieldAccess and result = qualifier + ) +} + +/** + * A nested field access, for example the access `myLine.start.x`. + */ +class NestedFieldAccess extends FieldAccess { + Expr ultimateQualifier; + + NestedFieldAccess() { + ultimateQualifier = getUltimateQualifier(this) and + getTarget() = getANestedField(ultimateQualifier.getType().stripType()) + } + + /** + * Gets the outermost qualifier of this nested field access. In the + * following example, the access to `myLine.start.x` has outermost qualifier + * `myLine`: + * ``` + * struct Point + * { + * float x, y; + * }; + * + * struct Line + * { + * Point start, end; + * }; + * + * void init() + * { + * Line myLine; + * + * myLine.start.x = 0.0f; + * + * // ... + * } + * ``` + */ + Expr getUltimateQualifier() { result = ultimateQualifier } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll new file mode 100644 index 00000000000..17da273d5a7 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/ObjectiveC.qll @@ -0,0 +1,196 @@ +/** + * DEPRECATED: Objective-C is no longer supported. + */ + +import semmle.code.cpp.Class +private import semmle.code.cpp.internal.ResolveClass + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C class. + */ +deprecated class ObjectiveClass extends Class { + ObjectiveClass() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C protocol. + */ +deprecated class Protocol extends Class { + Protocol() { none() } + + /** + * Holds if the type implements the protocol, either because the type + * itself does, or because it is a type conforming to the protocol. + */ + predicate isImplementedBy(Type t) { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * A type which conforms to a protocol. Use `getAProtocol` to get a + * protocol that this type conforms to. + */ +deprecated class TypeConformingToProtocol extends DerivedType { + TypeConformingToProtocol() { none() } + + /** Gets a protocol that this type conforms to. */ + Protocol getAProtocol() { none() } + + /** Gets the size of this type. */ + override int getSize() { none() } + + override int getAlignment() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@autoreleasepool` statement, for example + * `@autoreleasepool { int x; int y; }`. + */ +deprecated class AutoReleasePoolStmt extends Stmt { + AutoReleasePoolStmt() { none() } + + override string toString() { none() } + + /** Gets the body statement of this `@autoreleasepool` statement. */ + Stmt getStmt() { none() } + + override predicate mayBeImpure() { none() } + + override predicate mayBeGloballyImpure() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@synchronized statement`, for example + * `@synchronized (x) { [x complicationOperation]; }`. + */ +deprecated class SynchronizedStmt extends Stmt { + SynchronizedStmt() { none() } + + override string toString() { none() } + + /** Gets the expression which gives the object to be locked. */ + Expr getLockedObject() { none() } + + /** Gets the body statement of this `@synchronized` statement. */ + Stmt getStmt() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C for-in statement. + */ +deprecated class ForInStmt extends Loop { + ForInStmt() { none() } + + /** + * Gets the condition expression of the `while` statement that the + * `for...in` statement desugars into. + */ + override Expr getCondition() { none() } + + override Expr getControllingExpr() { none() } + + /** Gets the collection that the loop iterates over. */ + Expr getCollection() { none() } + + /** Gets the body of the loop. */ + override Stmt getStmt() { none() } + + override string toString() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C category or class extension. + */ +deprecated class Category extends Class { + Category() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C class extension. + */ +deprecated class ClassExtension extends Category { + ClassExtension() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C try statement. + */ +deprecated class ObjcTryStmt extends TryStmt { + ObjcTryStmt() { none() } + + override string toString() { none() } + + /** Gets the finally clause of this try statement, if any. */ + FinallyBlock getFinallyClause() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@finally` block. + */ +deprecated class FinallyBlock extends BlockStmt { + FinallyBlock() { none() } + + /** Gets the try statement corresponding to this finally block. */ + ObjcTryStmt getTryStmt() { none() } +} + +/** + * DEPRECATED: Objective-C is no longer supported. + * An Objective C `@property`. + */ +deprecated class Property extends Declaration { + Property() { none() } + + /** Gets the name of this property. */ + override string getName() { none() } + + /** + * Gets nothing (provided for compatibility with Declaration). + * + * For the attribute list following the `@property` keyword, use + * `getAnAttribute()`. + */ + override Specifier getASpecifier() { none() } + + /** + * Gets an attribute of this property (such as `readonly`, `nonatomic`, + * or `getter=isEnabled`). + */ + Attribute getAnAttribute() { none() } + + override Location getADeclarationLocation() { result = getLocation() } + + override Location getDefinitionLocation() { result = getLocation() } + + override Location getLocation() { none() } + + /** Gets the type of this property. */ + Type getType() { none() } + + /** + * Gets the instance method which is called to get the value of this + * property. + */ + MemberFunction getGetter() { none() } + + /** + * Gets the instance method which is called to set the value of this + * property (if it is a writable property). + */ + MemberFunction getSetter() { none() } + + /** + * Gets the instance variable which stores the property value (if this + * property was explicitly or automatically `@synthesize`d). + */ + MemberVariable getInstanceVariable() { none() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PODType03.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PODType03.qll new file mode 100644 index 00000000000..88c9a1203aa --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PODType03.qll @@ -0,0 +1,125 @@ +/** + * Provides predicates to determine whether a type is an aggregate or POD + * (Plain Old Data), as defined by C++03. + */ + +import cpp + +/** + * Holds if `t` is a scalar type, according to the rules specified in + * C++03 3.9(10): + * + * Arithmetic types (3.9.1), enumeration types, pointer types, and + * pointer to member types (3.9.2), and cv-qualified versions of these + * types (3.9.3) are collectively called scalar types. + */ +predicate isScalarType03(Type t) { + exists(Type ut | ut = t.getUnderlyingType() | + ut instanceof ArithmeticType or + ut instanceof Enum or + ut instanceof FunctionPointerType or + ut instanceof PointerToMemberType or + ut instanceof PointerType or + isScalarType03(ut.(SpecifiedType).getUnspecifiedType()) + ) +} + +/** + * Holds if `c` is an aggregate class, according to the rules specified in + * C++03 8.5.1(1): + * + * An aggregate [class] is ... a class (clause 9) with no user-declared + * constructors (12.1), no private or protected non-static data members + * (clause 11), no base classes (clause 10), and no virtual functions + * (10.3). + */ +predicate isAggregateClass03(Class c) { + not c instanceof TemplateClass and + not exists(Constructor cons | + cons.getDeclaringType() = c and + not cons.isCompilerGenerated() + ) and + not exists(Variable v | + v.getDeclaringType() = c and + not v.isStatic() + | + v.hasSpecifier("private") or + v.hasSpecifier("protected") + ) and + not exists(c.getABaseClass()) and + not exists(VirtualFunction f | f.getDeclaringType() = c) +} + +/** + * Holds if `t` is an aggregate type, according to the rules specified in + * C++03 8.5.1(1): + * + * An aggregate is an array or a class (clause 9) with no user-declared + * constructors (12.1), no private or protected non-static data members + * (clause 11), no base classes (clause 10), and no virtual functions + * (10.3). + */ +predicate isAggregateType03(Type t) { + exists(Type ut | ut = t.getUnderlyingType() | + ut instanceof ArrayType or + isAggregateClass03(ut) + ) +} + +/** + * Holds if `c` is a POD class, according to the rules specified in + * C++03 9(4): + * + * A POD-struct is an aggregate class that has no non-static data members + * of type non-POD-struct, non-POD-union (or array of such types) or + * reference, and has no user-defined copy assignment operator and no + * user-defined destructor. Similarly, a POD-union is an aggregate union + * that has no non-static data members of type non-POD-struct, + * non-POD-union (or array of such types) or reference, and has no + * user-defined copy assignment operator and no user-defined destructor. + * A POD class is a class that is either a POD-struct or a POD-union. + */ +predicate isPODClass03(Class c) { + isAggregateClass03(c) and + not exists(Variable v | + v.getDeclaringType() = c and + not v.isStatic() + | + not isPODType03(v.getType()) + or + exists(ArrayType at | + at = v.getType() and + not isPODType03(at.getBaseType()) + ) + or + v.getType() instanceof ReferenceType + ) and + not exists(CopyAssignmentOperator o | + o.getDeclaringType() = c and + not o.isCompilerGenerated() + ) and + not exists(Destructor dest | + dest.getDeclaringType() = c and + not dest.isCompilerGenerated() + ) +} + +/** + * Holds if `t` is a POD type, according to the rules specified in + * C++03 3.9(10): + * + * Scalar types, POD-struct types, POD-union types (clause 9), arrays of + * such types and cv-qualified versions of these types (3.9.3) are + * collectively called POD types. + */ +predicate isPODType03(Type t) { + exists(Type ut | ut = t.getUnderlyingType() | + isScalarType03(ut) + or + isPODClass03(ut) + or + exists(ArrayType at | at = ut and isPODType03(at.getBaseType())) + or + isPODType03(ut.(SpecifiedType).getUnspecifiedType()) + ) +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Parameter.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Parameter.qll new file mode 100644 index 00000000000..b87bfe6a4c7 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Parameter.qll @@ -0,0 +1,176 @@ +/** + * Provides a class that models parameters to functions. + */ + +import semmle.code.cpp.Location +import semmle.code.cpp.Declaration +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C/C++ function parameter or catch block parameter. For example the + * function parameter `p` and the catch block parameter `e` in the following + * code: + * ``` + * void myFunction(int p) { + * try { + * ... + * } catch (const std::exception &e) { + * ... + * } + * } + * ``` + * + * For catch block parameters, there is a one-to-one correspondence between + * the `Parameter` and its `ParameterDeclarationEntry`. + * + * For function parameters, there is a one-to-many relationship between + * `Parameter` and `ParameterDeclarationEntry`, because one function can + * have multiple declarations. + */ +class Parameter extends LocalScopeVariable, @parameter { + /** + * Gets the canonical name, or names, of this parameter. + * + * The canonical names are the first non-empty category from the + * following list: + * 1. The name given to the parameter at the function's definition or + * (for catch block parameters) at the catch block. + * 2. A name given to the parameter at a function declaration. + * 3. The name "(unnamed parameter i)" where i is the index of the parameter. + */ + override string getName() { + exists(VariableDeclarationEntry vde | + vde = getANamedDeclarationEntry() and result = vde.getName() + | + vde.isDefinition() or not getANamedDeclarationEntry().isDefinition() + ) + or + not exists(getANamedDeclarationEntry()) and + result = "(unnamed parameter " + this.getIndex().toString() + ")" + } + + override string getAPrimaryQlClass() { result = "Parameter" } + + /** + * Gets the name of this parameter, including it's type. + * + * For example: `int p`. + */ + string getTypedName() { + exists(string typeString, string nameString | + (if exists(getType().getName()) then typeString = getType().getName() else typeString = "") and + (if exists(getName()) then nameString = getName() else nameString = "") and + ( + if typeString != "" and nameString != "" + then result = typeString + " " + nameString + else result = typeString + nameString + ) + ) + } + + private VariableDeclarationEntry getANamedDeclarationEntry() { + result = getAnEffectiveDeclarationEntry() and result.getName() != "" + } + + /** + * Gets a declaration entry corresponding to this declaration. + * + * This predicate is the same as getADeclarationEntry(), except that for + * parameters of instantiated function templates, gives the declaration + * entry of the prototype instantiation of the parameter (as + * non-prototype instantiations don't have declaration entries of their + * own). + */ + private VariableDeclarationEntry getAnEffectiveDeclarationEntry() { + if getFunction().isConstructedFrom(_) + then + exists(Function prototypeInstantiation | + prototypeInstantiation.getParameter(getIndex()) = result.getVariable() and + getFunction().isConstructedFrom(prototypeInstantiation) + ) + else result = getADeclarationEntry() + } + + /** + * Gets the name of this parameter in the given block (which should be + * the body of a function with which the parameter is associated). + * + * DEPRECATED: this method was used in a previous implementation of + * getName, but is no longer in use. + */ + deprecated string getNameInBlock(BlockStmt b) { + exists(ParameterDeclarationEntry pde | + pde.getFunctionDeclarationEntry().getBlock() = b and + this.getFunction().getBlock() = b and + pde.getVariable() = this and + result = pde.getName() + ) + } + + /** + * Holds if this parameter has a name. + * + * In other words, this predicate holds precisely when the result of + * `getName()` is not "(unnamed parameter i)" (where `i` is the index + * of the parameter). + */ + predicate isNamed() { exists(getANamedDeclarationEntry()) } + + /** + * Gets the function to which this parameter belongs, if it is a function + * parameter. + */ + override Function getFunction() { + params(underlyingElement(this), unresolveElement(result), _, _) + } + + /** + * Gets the catch block to which this parameter belongs, if it is a catch + * block parameter. + */ + BlockStmt getCatchBlock() { params(underlyingElement(this), unresolveElement(result), _, _) } + + /** + * Gets the zero-based index of this parameter. + * + * For catch block parameters, this is always zero. + */ + int getIndex() { params(underlyingElement(this), _, result, _) } + + /** + * Gets the type of this parameter. + * + * Function parameters of array type are a special case in C/C++, + * as they are syntactic sugar for parameters of pointer type. The + * result is an array type for such parameters. + */ + override Type getType() { params(underlyingElement(this), _, _, unresolveElement(result)) } + + /** + * Gets the canonical location, or locations, of this parameter. + * + * 1. For catch block parameters, gets the obvious location. + * 2. For parameters of functions which have a definition, gets the + * location within the function definition. + * 3. For parameters of functions which don't have a definition, gets all + * of the declaration locations. + */ + override Location getLocation() { + exists(VariableDeclarationEntry vde | + vde = getAnEffectiveDeclarationEntry() and result = vde.getLocation() + | + vde.isDefinition() or not getAnEffectiveDeclarationEntry().isDefinition() + ) + } +} + +/** + * An `int` that is a parameter index for some function. This is needed for binding in certain cases. + */ +class ParameterIndex extends int { + ParameterIndex() { + exists(Parameter p | this = p.getIndex()) or + exists(Call c | exists(c.getArgument(this))) or // permit indexing varargs + this = -1 // used for `this` + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll new file mode 100644 index 00000000000..2389db07f2a --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Preprocessor.qll @@ -0,0 +1,294 @@ +import semmle.code.cpp.Location +import semmle.code.cpp.Element + +/** + * A C/C++ preprocessor directive. For example each of the following lines of + * code contains a `PreprocessorDirective`: + * ``` + * #pragma once + * #ifdef MYDEFINE + * #include "myfile.h" + * #line 1 "source.c" + * ``` + */ +class PreprocessorDirective extends Locatable, @preprocdirect { + override string toString() { result = "Preprocessor directive" } + + override Location getLocation() { preprocdirects(underlyingElement(this), _, result) } + + string getHead() { preproctext(underlyingElement(this), result, _) } + + /** + * Gets a preprocessor branching directive whose condition affects + * whether this directive is performed. + * + * From a lexical point of view, this returns all `#if`, `#ifdef`, + * `#ifndef`, or `#elif` directives which occur before this directive and + * have a matching `#endif` which occurs after this directive. + */ + PreprocessorBranch getAGuard() { + exists(PreprocessorEndif e, int line | + result.getEndIf() = e and + e.getFile() = getFile() and + result.getFile() = getFile() and + line = this.getLocation().getStartLine() and + result.getLocation().getStartLine() < line and + line < e.getLocation().getEndLine() + ) + } +} + +private class TPreprocessorBranchDirective = @ppd_branch or @ppd_else or @ppd_endif; + +/** + * A C/C++ preprocessor branch related directive: `#if`, `#ifdef`, + * `#ifndef`, `#elif`, `#else` or `#endif`. + */ +class PreprocessorBranchDirective extends PreprocessorDirective, TPreprocessorBranchDirective { + /** + * Gets the `#if`, `#ifdef` or `#ifndef` directive which matches this + * branching directive. + * + * If this branch directive was unbalanced, then there will be no + * result. Conversely, if the branch matches different `#if` directives + * in different translation units, then there can be more than one + * result. + */ + PreprocessorBranch getIf() { + result = this.(PreprocessorIf) or + result = this.(PreprocessorIfdef) or + result = this.(PreprocessorIfndef) or + preprocpair(unresolveElement(result), underlyingElement(this)) + } + + /** + * Gets the `#endif` directive which matches this branching directive. + * + * If this branch directive was unbalanced, then there will be no + * result. Conversely, if the branch matched different `#endif` + * directives in different translation units, then there can be more than + * one result. + */ + PreprocessorEndif getEndIf() { preprocpair(unresolveElement(getIf()), unresolveElement(result)) } + + /** + * Gets the next `#elif`, `#else` or `#endif` matching this branching + * directive. + * + * For example `somePreprocessorBranchDirective.getIf().getNext()` gets + * the second directive in the same construct as + * `somePreprocessorBranchDirective`. + */ + PreprocessorBranchDirective getNext() { + exists(PreprocessorBranch branch | + this.getIndexInBranch(branch) + 1 = result.getIndexInBranch(branch) + ) + } + + /** + * Gets the index of this branching directive within the matching #if, + * #ifdef or #ifndef. + */ + private int getIndexInBranch(PreprocessorBranch branch) { + this = + rank[result](PreprocessorBranchDirective other | + other.getIf() = branch + | + other order by other.getLocation().getStartLine() + ) + } +} + +/** + * A C/C++ preprocessor branching directive: `#if`, `#ifdef`, `#ifndef`, or + * `#elif`. + * + * A branching directive has a condition and that condition may be evaluated + * at compile-time. As a result, the preprocessor will either take the + * branch, or not take the branch. + * + * However, there are also situations in which a branch's condition isn't + * evaluated. The obvious case of this is when the directive is contained + * within a branch which wasn't taken. There is also a much more subtle + * case involving header guard branches: suitably clever compilers can + * notice that a branch is a header guard, and can then subsequently ignore + * a `#include` for the file being guarded. It is for this reason that + * `wasTaken()` always holds on header guard branches, but `wasNotToken()` + * rarely holds on header guard branches. + */ +class PreprocessorBranch extends PreprocessorBranchDirective, @ppd_branch { + /** + * Holds if at least one translation unit evaluated this directive's + * condition and subsequently took the branch. + */ + predicate wasTaken() { preproctrue(underlyingElement(this)) } + + /** + * Holds if at least one translation unit evaluated this directive's + * condition but then didn't take the branch. + * + * If `#else` is the next matching directive, then this means that the + * `#else` was taken instead. + */ + predicate wasNotTaken() { preprocfalse(underlyingElement(this)) } + + /** + * Holds if this directive was either taken by all translation units + * which evaluated it, or was not taken by any translation unit which + * evaluated it. + */ + predicate wasPredictable() { not (wasTaken() and wasNotTaken()) } +} + +/** + * A C/C++ preprocessor `#if` directive. For example there is a + * `PreprocessorIf` on the first line of the following code: + * ``` + * #if defined(MYDEFINE) + * // ... + * #endif + * ``` + * For the related notion of a directive which causes branching (which + * includes `#if`, plus also `#ifdef`, `#ifndef`, and `#elif`), see + * `PreprocessorBranch`. + */ +class PreprocessorIf extends PreprocessorBranch, @ppd_if { + override string toString() { result = "#if " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#ifdef` directive. For example there is a + * `PreprocessorIfdef` on the first line of the following code: + * ``` + * #ifdef MYDEFINE + * // ... + * #endif + * ``` + * The syntax `#ifdef X` is shorthand for `#if defined(X)`. + */ +class PreprocessorIfdef extends PreprocessorBranch, @ppd_ifdef { + override string toString() { result = "#ifdef " + this.getHead() } + + override string getAPrimaryQlClass() { result = "PreprocessorIfdef" } +} + +/** + * A C/C++ preprocessor `#ifndef` directive. For example there is a + * `PreprocessorIfndef` on the first line of the following code: + * ``` + * #ifndef MYDEFINE + * // ... + * #endif + * ``` + * The syntax `#ifndef X` is shorthand for `#if !defined(X)`. + */ +class PreprocessorIfndef extends PreprocessorBranch, @ppd_ifndef { + override string toString() { result = "#ifndef " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#else` directive. For example there is a + * `PreprocessorElse` on the fifth line of the following code: + * ``` + * #ifdef MYDEFINE1 + * // ... + * #elif MYDEFINE2 + * // ... + * #else + * // ... + * #endif + * ``` + */ +class PreprocessorElse extends PreprocessorBranchDirective, @ppd_else { + override string toString() { result = "#else" } +} + +/** + * A C/C++ preprocessor `#elif` directive. For example there is a + * `PreprocessorElif` on the third line of the following code: + * ``` + * #ifdef MYDEFINE1 + * // ... + * #elif MYDEFINE2 + * // ... + * #else + * // ... + * #endif + * ``` + */ +class PreprocessorElif extends PreprocessorBranch, @ppd_elif { + override string toString() { result = "#elif " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#endif` directive. For example there is a + * `PreprocessorEndif` on the third line of the following code: + * ``` + * #ifdef MYDEFINE + * // ... + * #endif + * ``` + */ +class PreprocessorEndif extends PreprocessorBranchDirective, @ppd_endif { + override string toString() { result = "#endif" } +} + +/** + * A C/C++ preprocessor `#warning` directive. For example: + * ``` + * #warning "This configuration is not supported." + * ``` + */ +class PreprocessorWarning extends PreprocessorDirective, @ppd_warning { + override string toString() { result = "#warning " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#error` directive. For example: + * ``` + * #error "This configuration is not implemented." + * ``` + */ +class PreprocessorError extends PreprocessorDirective, @ppd_error { + override string toString() { result = "#error " + this.getHead() } +} + +/** + * A C/C++ preprocessor `#undef` directive. For example there is a + * `PreprocessorUndef` on the second line of the following code: + * ``` + * #ifdef MYMACRO + * #undef MYMACRO + * #endif + * ``` + */ +class PreprocessorUndef extends PreprocessorDirective, @ppd_undef { + override string toString() { result = "#undef " + this.getHead() } + + /** + * Gets the name of the macro that is undefined. + */ + string getName() { result = getHead() } +} + +/** + * A C/C++ preprocessor `#pragma` directive. For example: + * ``` + * #pragma once + * ``` + */ +class PreprocessorPragma extends PreprocessorDirective, @ppd_pragma { + override string toString() { + if exists(this.getHead()) then result = "#pragma " + this.getHead() else result = "#pragma" + } +} + +/** + * A C/C++ preprocessor `#line` directive. For example: + * ``` + * #line 1 "source.c" + * ``` + */ +class PreprocessorLine extends PreprocessorDirective, @ppd_line { + override string toString() { result = "#line " + this.getHead() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Print.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Print.qll new file mode 100644 index 00000000000..f8d30f55a88 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Print.qll @@ -0,0 +1,413 @@ +import cpp +private import PrintAST + +/** + * Print function declarations only if there is a `PrintASTConfiguration` + * that requests that function, or no `PrintASTConfiguration` exists. + */ +private predicate shouldPrintDeclaration(Declaration decl) { + not decl instanceof Function + or + not exists(PrintASTConfiguration c) + or + exists(PrintASTConfiguration config | config.shouldPrintFunction(decl)) +} + +/** + * Gets a string containing the scope in which this declaration is declared. + */ +private string getScopePrefix(Declaration decl) { + decl.isMember() and result = decl.getDeclaringType().(UserDumpType).getIdentityString() + "::" + or + decl.isTopLevel() and + exists(string parentName | + parentName = decl.getNamespace().getQualifiedName() and + ( + parentName != "" and result = parentName + "::" + or + parentName = "" and result = "" + ) + ) + or + exists(UserType type | + type = decl and + type.isLocal() and + result = "(" + type.getEnclosingFunction().(DumpFunction).getIdentityString() + ")::" + ) + or + decl instanceof TemplateParameter and result = "" +} + +/** + * Gets the identity string of a type used as a parameter. Identical to `Type.getTypeIdentityString()`, except that + * it returns `...` for `UnknownType`, which is used to represent variable arguments. + */ +private string getParameterTypeString(Type parameterType) { + if parameterType instanceof UnknownType + then result = "..." + else result = parameterType.(DumpType).getTypeIdentityString() +} + +private string getTemplateArgumentString(Declaration d, int i) { + if exists(d.getTemplateArgumentKind(i)) + then + result = + d.getTemplateArgumentKind(i).(DumpType).getTypeIdentityString() + " " + + d.getTemplateArgument(i) + else result = d.getTemplateArgument(i).(DumpType).getTypeIdentityString() +} + +/** + * A `Declaration` extended to add methods for generating strings useful only for dumps and debugging. + */ +private class DumpDeclaration extends Declaration { + DumpDeclaration() { shouldPrintDeclaration(this) } + + /** + * Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for + * functions, user-defined types, global and namespace-scope variables, and member variables. + * + * This operation is very expensive, and should not be used in production queries. Consider using + * `hasQualifiedName()` for identifying known declarations in production queries. + */ + string getIdentityString() { none() } + + language[monotonicAggregates] + final string getTemplateArgumentsString() { + if exists(this.getATemplateArgument()) + then + result = + "<" + + strictconcat(int i | + exists(this.getTemplateArgument(i)) + | + getTemplateArgumentString(this, i), ", " order by i + ) + ">" + else result = "" + } +} + +/** + * A `Type` extended to add methods for generating strings useful only for dumps and debugging. + */ +private class DumpType extends Type { + /** + * Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and + * decltypes are expanded, and all symbol names are fully qualified. + * + * This operation is very expensive, and should not be used in production queries. + */ + final string getTypeIdentityString() { + // The identity string of a type is just the concatenation of the four + // components below. To create the type identity for a derived type, insert + // the declarator of the derived type between the `getDeclaratorPrefix()` + // and `getDeclaratorSuffixBeforeQualifiers()`. To create the type identity + // for a `SpecifiedType`, insert the qualifiers after + // `getDeclaratorSuffixBeforeQualifiers()`. + result = + getTypeSpecifier() + getDeclaratorPrefix() + getDeclaratorSuffixBeforeQualifiers() + + getDeclaratorSuffix() + } + + /** + * Gets the "type specifier" part of this type's name. This is generally the "leaf" type from which the type was + * constructed. + * + * Examples: + * - `int` -> `int` + * - `int*` -> `int` + * - `int (*&)(float, double) const` -> `int` + * + * This predicate is intended to be used only by the implementation of `getTypeIdentityString`. + */ + string getTypeSpecifier() { result = "" } + + /** + * Gets the portion of this type's declarator that comes before the declarator for any derived type. + * + * This predicate is intended to be used only by the implementation of `getTypeIdentityString`. + */ + string getDeclaratorPrefix() { result = "" } + + /** + * Gets the portion of this type's declarator that comes after the declarator for any derived type, but before any + * qualifiers on the current type. + * + * This predicate is intended to be used only by the implementation of `getTypeIdentityString`. + */ + string getDeclaratorSuffixBeforeQualifiers() { result = "" } + + /** + * Gets the portion of this type's declarator that comes after the declarator for any derived type and after any + * qualifiers on the current type. + * + * This predicate is intended to be used only by the implementation of `getTypeIdentityString`. + */ + string getDeclaratorSuffix() { result = "" } +} + +private class BuiltInDumpType extends DumpType, BuiltInType { + override string getTypeSpecifier() { result = toString() } +} + +private class IntegralDumpType extends BuiltInDumpType, IntegralType { + override string getTypeSpecifier() { result = getCanonicalArithmeticType().toString() } +} + +private class DerivedDumpType extends DumpType, DerivedType { + override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + + override string getDeclaratorSuffixBeforeQualifiers() { + result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + } + + override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } +} + +private class DecltypeDumpType extends DumpType, Decltype { + override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + + override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() } + + override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } +} + +private class PointerIshDumpType extends DerivedDumpType { + PointerIshDumpType() { + this instanceof PointerType or + this instanceof ReferenceType + } + + override string getDeclaratorPrefix() { + exists(string declarator | + result = getBaseType().(DumpType).getDeclaratorPrefix() + declarator and + if getBaseType().getUnspecifiedType() instanceof ArrayType + then declarator = "(" + getDeclaratorToken() + ")" + else declarator = getDeclaratorToken() + ) + } + + /** + * Gets the token used when declaring this kind of type (e.g. `*`, `&`, `&&`)/ + */ + string getDeclaratorToken() { result = "" } +} + +private class PointerDumpType extends PointerIshDumpType, PointerType { + override string getDeclaratorToken() { result = "*" } +} + +private class LValueReferenceDumpType extends PointerIshDumpType, LValueReferenceType { + override string getDeclaratorToken() { result = "&" } +} + +private class RValueReferenceDumpType extends PointerIshDumpType, RValueReferenceType { + override string getDeclaratorToken() { result = "&&" } +} + +private class PointerToMemberDumpType extends DumpType, PointerToMemberType { + override string getTypeSpecifier() { result = getBaseType().(DumpType).getTypeSpecifier() } + + override string getDeclaratorPrefix() { + exists(string declarator, string parenDeclarator, Type baseType | + declarator = getClass().(DumpType).getTypeIdentityString() + "::*" and + result = getBaseType().(DumpType).getDeclaratorPrefix() + " " + parenDeclarator and + baseType = getBaseType().getUnspecifiedType() and + if baseType instanceof ArrayType or baseType instanceof RoutineType + then parenDeclarator = "(" + declarator + else parenDeclarator = declarator + ) + } + + override string getDeclaratorSuffixBeforeQualifiers() { + exists(Type baseType | + baseType = getBaseType().getUnspecifiedType() and + if baseType instanceof ArrayType or baseType instanceof RoutineType + then result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + else result = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + ) + } + + override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } +} + +private class ArrayDumpType extends DerivedDumpType, ArrayType { + override string getDeclaratorPrefix() { result = getBaseType().(DumpType).getDeclaratorPrefix() } + + override string getDeclaratorSuffixBeforeQualifiers() { + if exists(getArraySize()) + then + result = + "[" + getArraySize().toString() + "]" + + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + else result = "[]" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + } +} + +private class FunctionPointerIshDumpType extends DerivedDumpType, FunctionPointerIshType { + override string getDeclaratorSuffixBeforeQualifiers() { + result = ")" + getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + } + + override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } + + override string getDeclaratorPrefix() { + result = getBaseType().(DumpType).getDeclaratorPrefix() + "(" + getDeclaratorToken() + } + + /** + * Gets the token used when declaring this kind of type (e.g. `*`, `&`, `^`)/ + */ + string getDeclaratorToken() { result = "" } +} + +private class FunctionPointerDumpType extends FunctionPointerIshDumpType, FunctionPointerType { + override string getDeclaratorToken() { result = "*" } +} + +private class FunctionReferenceDumpType extends FunctionPointerIshDumpType, FunctionReferenceType { + override string getDeclaratorToken() { result = "&" } +} + +private class BlockDumpType extends FunctionPointerIshDumpType, BlockType { + override string getDeclaratorToken() { result = "^" } +} + +private class RoutineDumpType extends DumpType, RoutineType { + override string getTypeSpecifier() { result = getReturnType().(DumpType).getTypeSpecifier() } + + override string getDeclaratorPrefix() { + result = getReturnType().(DumpType).getDeclaratorPrefix() + } + + language[monotonicAggregates] + override string getDeclaratorSuffixBeforeQualifiers() { + result = + "(" + + concat(int i | + exists(getParameterType(i)) + | + getParameterTypeString(getParameterType(i)), ", " order by i + ) + ")" + } + + override string getDeclaratorSuffix() { + result = + getReturnType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + + getReturnType().(DumpType).getDeclaratorSuffix() + } +} + +private class SpecifiedDumpType extends DerivedDumpType, SpecifiedType { + override string getDeclaratorPrefix() { + exists(string basePrefix | + basePrefix = getBaseType().(DumpType).getDeclaratorPrefix() and + if getBaseType().getUnspecifiedType() instanceof RoutineType + then result = basePrefix + else result = basePrefix + " " + getSpecifierString() + ) + } + + override string getDeclaratorSuffixBeforeQualifiers() { + exists(string baseSuffix | + baseSuffix = getBaseType().(DumpType).getDeclaratorSuffixBeforeQualifiers() and + if getBaseType().getUnspecifiedType() instanceof RoutineType + then result = baseSuffix + " " + getSpecifierString() + else result = baseSuffix + ) + } + + override string getDeclaratorSuffix() { result = getBaseType().(DumpType).getDeclaratorSuffix() } +} + +private class UserDumpType extends DumpType, DumpDeclaration, UserType { + override string getIdentityString() { + exists(string simpleName | + ( + if this instanceof Closure + then + // Parenthesize the name of the lambda because it's freeform text similar to + // "lambda [] type at line 12, col. 40" + // Use `min(getSimpleName())` to work around an extractor bug where a lambda can have different names + // from different compilation units. + simpleName = "(" + min(getSimpleName()) + ")" + else simpleName = getSimpleName() + ) and + result = getScopePrefix(this) + simpleName + getTemplateArgumentsString() + ) + } + + override string getTypeSpecifier() { result = getIdentityString() } +} + +private class DumpProxyClass extends UserDumpType, ProxyClass { + override string getIdentityString() { result = getName() } +} + +private class DumpVariable extends DumpDeclaration, Variable { + override string getIdentityString() { + exists(DumpType type | + (this instanceof MemberVariable or this instanceof GlobalOrNamespaceVariable) and + type = this.getType() and + result = + type.getTypeSpecifier() + type.getDeclaratorPrefix() + " " + getScopePrefix(this) + + this.getName() + this.getTemplateArgumentsString() + + type.getDeclaratorSuffixBeforeQualifiers() + type.getDeclaratorSuffix() + ) + } +} + +private class DumpFunction extends DumpDeclaration, Function { + override string getIdentityString() { + result = + getType().(DumpType).getTypeSpecifier() + getType().(DumpType).getDeclaratorPrefix() + " " + + getScopePrefix(this) + getName() + getTemplateArgumentsString() + + getDeclaratorSuffixBeforeQualifiers() + getDeclaratorSuffix() + } + + language[monotonicAggregates] + private string getDeclaratorSuffixBeforeQualifiers() { + result = + "(" + + concat(int i | + exists(getParameter(i).getType()) + | + getParameterTypeString(getParameter(i).getType()), ", " order by i + ) + ")" + getQualifierString() + } + + private string getQualifierString() { + if exists(getACVQualifier()) + then + result = " " + strictconcat(string qualifier | qualifier = getACVQualifier() | qualifier, " ") + else result = "" + } + + private string getACVQualifier() { + result = getASpecifier().getName() and + result = ["const", "volatile"] + } + + private string getDeclaratorSuffix() { + result = + getType().(DumpType).getDeclaratorSuffixBeforeQualifiers() + + getType().(DumpType).getDeclaratorSuffix() + } +} + +/** + * Gets a string that uniquely identifies this declaration, suitable for use when debugging queries. Only holds for + * functions, user-defined types, global and namespace-scope variables, and member variables. + * + * This operation is very expensive, and should not be used in production queries. Consider using `hasName()` or + * `hasQualifiedName()` for identifying known declarations in production queries. + */ +string getIdentityString(Declaration decl) { result = decl.(DumpDeclaration).getIdentityString() } + +/** + * Gets a string that uniquely identifies this type, suitable for use when debugging queries. All typedefs and + * decltypes are expanded, and all symbol names are fully qualified. + * + * This operation is very expensive, and should not be used in production queries. + */ +string getTypeIdentityString(Type type) { result = type.(DumpType).getTypeIdentityString() } diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.ql b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.ql new file mode 100644 index 00000000000..e4c53030da5 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.ql @@ -0,0 +1,21 @@ +/** + * @name Print AST + * @description Outputs a representation of the Abstract Syntax Tree. + * @id cpp/print-ast + * @kind graph + */ + +import cpp +import PrintAST + +/** + * Temporarily tweak this class or make a copy to control which functions are + * printed. + */ +class Cfg extends PrintASTConfiguration { + /** + * TWEAK THIS PREDICATE AS NEEDED. + * Holds if the AST for `func` should be printed. + */ + override predicate shouldPrintFunction(Function func) { any() } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.qll new file mode 100644 index 00000000000..86b39a285b0 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/PrintAST.qll @@ -0,0 +1,884 @@ +/** + * Provides queries to pretty-print a C++ AST as a graph. + * + * By default, this will print the AST for all functions in the database. To change this behavior, + * extend `PrintASTConfiguration` and override `shouldPrintFunction` to hold for only the functions + * you wish to view the AST for. + */ + +import cpp +private import semmle.code.cpp.Print + +private newtype TPrintASTConfiguration = MkPrintASTConfiguration() + +/** + * The query can extend this class to control which functions are printed. + */ +class PrintASTConfiguration extends TPrintASTConfiguration { + /** + * Gets a textual representation of this `PrintASTConfiguration`. + */ + string toString() { result = "PrintASTConfiguration" } + + /** + * Holds if the AST for `func` should be printed. By default, holds for all + * functions. + */ + predicate shouldPrintFunction(Function func) { any() } +} + +private predicate shouldPrintFunction(Function func) { + exists(PrintASTConfiguration config | config.shouldPrintFunction(func)) +} + +bindingset[s] +private string escapeString(string s) { + result = + s.replaceAll("\\", "\\\\") + .replaceAll("\n", "\\n") + .replaceAll("\r", "\\r") + .replaceAll("\t", "\\t") +} + +/** + * Due to extractor issues with ODR violations, a given AST may wind up with + * multiple locations. This predicate returns a single location - the one whose + * string representation comes first in lexicographical order. + */ +private Location getRepresentativeLocation(Locatable ast) { + result = min(Location loc | loc = ast.getLocation() | loc order by loc.toString()) +} + +/** + * Computes the sort keys to sort the given AST node by location. An AST without + * a location gets an empty file name and a zero line and column number. + */ +private predicate locationSortKeys(Locatable ast, string file, int line, int column) { + if exists(getRepresentativeLocation(ast)) + then + exists(Location loc | + loc = getRepresentativeLocation(ast) and + file = loc.getFile().toString() and + line = loc.getStartLine() and + column = loc.getStartColumn() + ) + else ( + file = "" and + line = 0 and + column = 0 + ) +} + +private Function getEnclosingFunction(Locatable ast) { + result = ast.(Expr).getEnclosingFunction() + or + result = ast.(Stmt).getEnclosingFunction() + or + result = ast.(Initializer).getExpr().getEnclosingFunction() + or + result = ast.(Parameter).getFunction() + or + result = ast +} + +/** + * Most nodes are just a wrapper around `Locatable`, but we do synthesize new + * nodes for things like parameter lists and constructor init lists. + */ +private newtype TPrintASTNode = + TASTNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or + TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) { + // We create a unique node for each pair of (stmt, entry), to avoid having one node with + // multiple parents due to extractor bug CPP-413. + stmt.getADeclarationEntry() = entry and + shouldPrintFunction(stmt.getEnclosingFunction()) + } or + TParametersNode(Function func) { shouldPrintFunction(func) } or + TConstructorInitializersNode(Constructor ctor) { + ctor.hasEntryPoint() and + shouldPrintFunction(ctor) + } or + TDestructorDestructionsNode(Destructor dtor) { + dtor.hasEntryPoint() and + shouldPrintFunction(dtor) + } + +/** + * A node in the output tree. + */ +class PrintASTNode extends TPrintASTNode { + /** + * Gets a textual representation of this node in the PrintAST output tree. + */ + abstract string toString(); + + /** + * Gets the child node at index `childIndex`. Child indices must be unique, + * but need not be contiguous. + */ + abstract PrintASTNode getChildInternal(int childIndex); + + /** + * Gets the child node at index `childIndex`. + * Adds edges to fully converted expressions, that are not part of the + * regular parent/child relation traversal. + */ + final PrintASTNode getChild(int childIndex) { + // The exact value of `childIndex` doesn't matter, as long as we preserve the correct order. + result = + rank[childIndex](PrintASTNode child, int nonConvertedIndex, boolean isConverted | + childAndAccessorPredicate(child, _, nonConvertedIndex, isConverted) + | + // Unconverted children come first, then sort by original child index within each group. + child order by isConverted, nonConvertedIndex + ) + } + + /** + * Gets the node for the `.getFullyConverted()` version of the child originally at index + * `childIndex`, if that node has any conversions. + */ + private PrintASTNode getConvertedChild(int childIndex) { + exists(Expr expr | + expr = getChildInternal(childIndex).(ASTNode).getAST() and + expr.getFullyConverted() instanceof Conversion and + result.(ASTNode).getAST() = expr.getFullyConverted() and + not expr instanceof Conversion + ) + } + + /** + * Gets the child access predicate for the `.getFullyConverted()` version of the child originally + * at index `childIndex`, if that node has any conversions. + */ + private string getConvertedChildAccessorPredicate(int childIndex) { + exists(getConvertedChild(childIndex)) and + result = getChildAccessorPredicateInternal(childIndex) + ".getFullyConverted()" + } + + /** + * Holds if this node should be printed in the output. By default, all nodes + * within a function are printed, but the query can override + * `PrintASTConfiguration.shouldPrintFunction` to filter the output. + */ + final predicate shouldPrint() { shouldPrintFunction(getEnclosingFunction()) } + + /** + * Gets the children of this node. + */ + final PrintASTNode getAChild() { result = getChild(_) } + + /** + * Gets the parent of this node, if any. + */ + final PrintASTNode getParent() { result.getAChild() = this } + + /** + * Gets the location of this node in the source code. + */ + abstract Location getLocation(); + + /** + * Gets the value of the property of this node, where the name of the property + * is `key`. + */ + string getProperty(string key) { + key = "semmle.label" and + result = toString() + } + + /** + * Holds if there is a child node `child` for original child index `nonConvertedIndex` with + * predicate name `childPredicate`. If the original child at that index has any conversions, there + * will be two result tuples for this predicate: one with the original child and predicate, with + * `isConverted = false`, and the other with the `.getFullyConverted()` version of the child and + * predicate, with `isConverted = true`. For a child without any conversions, there will be only + * one result tuple, with `isConverted = false`. + */ + private predicate childAndAccessorPredicate( + PrintASTNode child, string childPredicate, int nonConvertedIndex, boolean isConverted + ) { + child = getChildInternal(nonConvertedIndex) and + childPredicate = getChildAccessorPredicateInternal(nonConvertedIndex) and + isConverted = false + or + child = getConvertedChild(nonConvertedIndex) and + childPredicate = getConvertedChildAccessorPredicate(nonConvertedIndex) and + isConverted = true + } + + /** + * Gets the QL predicate that can be used to access the child at `childIndex`. + * May not always return a QL predicate, see for example `FunctionNode`. + */ + final string getChildAccessorPredicate(int childIndex) { + // The exact value of `childIndex` doesn't matter, as long as we preserve the correct order. + result = + rank[childIndex](string childPredicate, int nonConvertedIndex, boolean isConverted | + childAndAccessorPredicate(_, childPredicate, nonConvertedIndex, isConverted) + | + // Unconverted children come first, then sort by original child index within each group. + childPredicate order by isConverted, nonConvertedIndex + ) + } + + /** + * Gets the QL predicate that can be used to access the child at `childIndex`. + * INTERNAL DO NOT USE: Does not contain accessors for the synthesized nodes for conversions. + */ + abstract string getChildAccessorPredicateInternal(int childIndex); + + /** + * Gets the `Function` that contains this node. + */ + private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() } +} + +/** + * Class that restricts the elements that we compute `qlClass` for. + */ +private class PrintableElement extends Element { + PrintableElement() { + exists(TASTNode(this)) + or + exists(TDeclarationEntryNode(_, this)) + or + this instanceof Type + } + + pragma[noinline] + string getAPrimaryQlClass0() { result = getAPrimaryQlClass() } +} + +/** + * Retrieves the canonical QL class(es) for entity `el` + */ +private string qlClass(PrintableElement el) { + result = "[" + concat(el.getAPrimaryQlClass0(), ",") + "] " + // Alternative implementation -- do not delete. It is useful for QL class discovery. + //result = "["+ concat(el.getAQlClass(), ",") + "] " +} + +/** + * A node representing an AST node. + */ +abstract class BaseASTNode extends PrintASTNode { + Locatable ast; + + override string toString() { result = qlClass(ast) + ast.toString() } + + final override Location getLocation() { result = getRepresentativeLocation(ast) } + + /** + * Gets the AST represented by this node. + */ + final Locatable getAST() { result = ast } +} + +/** + * A node representing an AST node other than a `DeclarationEntry`. + */ +abstract class ASTNode extends BaseASTNode, TASTNode { + ASTNode() { this = TASTNode(ast) } +} + +/** + * A node representing an `Expr`. + */ +class ExprNode extends ASTNode { + Expr expr; + + ExprNode() { expr = ast } + + override ASTNode getChildInternal(int childIndex) { result.getAST() = expr.getChild(childIndex) } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "Value" and + result = qlClass(expr) + getValue() + or + key = "Type" and + result = qlClass(expr.getType()) + expr.getType().toString() + or + key = "ValueCategory" and + result = expr.getValueCategoryString() + } + + override string getChildAccessorPredicateInternal(int childIndex) { + result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST()) + } + + /** + * Gets the value of this expression, if it is a constant. + */ + string getValue() { result = expr.getValue() } +} + +/** + * A node representing a `StringLiteral`. + */ +class StringLiteralNode extends ExprNode { + StringLiteralNode() { expr instanceof StringLiteral } + + override string toString() { result = escapeString(expr.getValue()) } + + override string getValue() { result = "\"" + escapeString(expr.getValue()) + "\"" } +} + +/** + * A node representing a `Conversion`. + */ +class ConversionNode extends ExprNode { + Conversion conv; + + ConversionNode() { conv = expr } + + override ASTNode getChildInternal(int childIndex) { + childIndex = 0 and + result.getAST() = conv.getExpr() and + conv.getExpr() instanceof Conversion + } +} + +/** + * A node representing a `Cast`. + */ +class CastNode extends ConversionNode { + Cast cast; + + CastNode() { cast = conv } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "Conversion" and + result = "[" + qlConversion(cast) + "] " + cast.getSemanticConversionString() + } +} + +/** + * A node representing a `StmtExpr`. + */ +class StmtExprNode extends ExprNode { + override StmtExpr expr; + + override ASTNode getChildInternal(int childIndex) { + childIndex = 0 and + result.getAST() = expr.getStmt() + } +} + +/** + * A node representing a `DeclarationEntry`. + */ +class DeclarationEntryNode extends BaseASTNode, TDeclarationEntryNode { + override DeclarationEntry ast; + DeclStmt declStmt; + + DeclarationEntryNode() { this = TDeclarationEntryNode(declStmt, ast) } + + override PrintASTNode getChildInternal(int childIndex) { none() } + + override string getChildAccessorPredicateInternal(int childIndex) { none() } + + override string getProperty(string key) { + result = BaseASTNode.super.getProperty(key) + or + key = "Type" and + result = qlClass(ast.getType()) + ast.getType().toString() + } +} + +/** + * A node representing a `VariableDeclarationEntry`. + */ +class VariableDeclarationEntryNode extends DeclarationEntryNode { + override VariableDeclarationEntry ast; + + override ASTNode getChildInternal(int childIndex) { + childIndex = 0 and + result.getAST() = ast.getVariable().getInitializer() + } + + override string getChildAccessorPredicateInternal(int childIndex) { + childIndex = 0 and + result = "getVariable().getInitializer()" + } +} + +/** + * A node representing a `Stmt`. + */ +class StmtNode extends ASTNode { + Stmt stmt; + + StmtNode() { stmt = ast } + + override BaseASTNode getChildInternal(int childIndex) { + exists(Locatable child | + child = stmt.getChild(childIndex) and + ( + result.getAST() = child.(Expr) or + result.getAST() = child.(Stmt) + ) + ) + } + + override string getChildAccessorPredicateInternal(int childIndex) { + result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST()) + } +} + +/** + * A node representing a `DeclStmt`. + */ +class DeclStmtNode extends StmtNode { + DeclStmt declStmt; + + DeclStmtNode() { declStmt = stmt } + + override DeclarationEntryNode getChildInternal(int childIndex) { + exists(DeclarationEntry entry | + declStmt.getDeclarationEntry(childIndex) = entry and + result = TDeclarationEntryNode(declStmt, entry) + ) + } +} + +/** + * A node representing a `Parameter`. + */ +class ParameterNode extends ASTNode { + Parameter param; + + ParameterNode() { param = ast } + + final override PrintASTNode getChildInternal(int childIndex) { none() } + + final override string getChildAccessorPredicateInternal(int childIndex) { none() } + + final override string getProperty(string key) { + result = super.getProperty(key) + or + key = "Type" and + result = qlClass(param.getType()) + param.getType().toString() + } +} + +/** + * A node representing an `Initializer`. + */ +class InitializerNode extends ASTNode { + Initializer init; + + InitializerNode() { init = ast } + + override ASTNode getChildInternal(int childIndex) { + childIndex = 0 and + result.getAST() = init.getExpr() + } + + override string getChildAccessorPredicateInternal(int childIndex) { + childIndex = 0 and + result = "getExpr()" + } +} + +/** + * A node representing the parameters of a `Function`. + */ +class ParametersNode extends PrintASTNode, TParametersNode { + Function func; + + ParametersNode() { this = TParametersNode(func) } + + final override string toString() { result = "" } + + final override Location getLocation() { result = getRepresentativeLocation(func) } + + override ASTNode getChildInternal(int childIndex) { + result.getAST() = func.getParameter(childIndex) + } + + override string getChildAccessorPredicateInternal(int childIndex) { + exists(getChildInternal(childIndex)) and + result = "getParameter(" + childIndex.toString() + ")" + } + + /** + * Gets the `Function` for which this node represents the parameters. + */ + final Function getFunction() { result = func } +} + +/** + * A node representing the initializer list of a `Constructor`. + */ +class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializersNode { + Constructor ctor; + + ConstructorInitializersNode() { this = TConstructorInitializersNode(ctor) } + + final override string toString() { result = "" } + + final override Location getLocation() { result = getRepresentativeLocation(ctor) } + + final override ASTNode getChildInternal(int childIndex) { + result.getAST() = ctor.getInitializer(childIndex) + } + + final override string getChildAccessorPredicateInternal(int childIndex) { + exists(getChildInternal(childIndex)) and + result = "getInitializer(" + childIndex.toString() + ")" + } + + /** + * Gets the `Constructor` for which this node represents the initializer list. + */ + final Constructor getConstructor() { result = ctor } +} + +/** + * A node representing the destruction list of a `Destructor`. + */ +class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNode { + Destructor dtor; + + DestructorDestructionsNode() { this = TDestructorDestructionsNode(dtor) } + + final override string toString() { result = "" } + + final override Location getLocation() { result = getRepresentativeLocation(dtor) } + + final override ASTNode getChildInternal(int childIndex) { + result.getAST() = dtor.getDestruction(childIndex) + } + + final override string getChildAccessorPredicateInternal(int childIndex) { + exists(getChildInternal(childIndex)) and + result = "getDestruction(" + childIndex.toString() + ")" + } + + /** + * Gets the `Destructor` for which this node represents the destruction list. + */ + final Destructor getDestructor() { result = dtor } +} + +/** + * A node representing a `Function`. + */ +class FunctionNode extends ASTNode { + Function func; + + FunctionNode() { func = ast } + + override string toString() { result = qlClass(func) + getIdentityString(func) } + + override PrintASTNode getChildInternal(int childIndex) { + childIndex = 0 and + result.(ParametersNode).getFunction() = func + or + childIndex = 1 and + result.(ConstructorInitializersNode).getConstructor() = func + or + childIndex = 2 and + result.(ASTNode).getAST() = func.getEntryPoint() + or + childIndex = 3 and + result.(DestructorDestructionsNode).getDestructor() = func + } + + override string getChildAccessorPredicateInternal(int childIndex) { + childIndex = 0 and result = "" + or + childIndex = 1 and result = "" + or + childIndex = 2 and result = "getEntryPoint()" + or + childIndex = 3 and result = "" + } + + private int getOrder() { + this = + rank[result](FunctionNode node, Function function, string file, int line, int column | + node.getAST() = function and + locationSortKeys(function, file, line, column) + | + node order by file, line, column, getIdentityString(function) + ) + } + + override string getProperty(string key) { + result = super.getProperty(key) + or + key = "semmle.order" and result = getOrder().toString() + } + + /** + * Gets the `Function` this node represents. + */ + final Function getFunction() { result = func } +} + +private string getChildAccessorWithoutConversions(Locatable parent, Element child) { + shouldPrintFunction(getEnclosingFunction(parent)) and + ( + exists(Stmt s | s = parent | + namedStmtChildPredicates(s, child, result) + or + not namedStmtChildPredicates(s, child, _) and + exists(int n | s.getChild(n) = child and result = "getChild(" + n + ")") + ) + or + exists(Expr expr | expr = parent | + namedExprChildPredicates(expr, child, result) + or + not namedExprChildPredicates(expr, child, _) and + exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")") + ) + ) +} + +private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) { + shouldPrintFunction(getEnclosingFunction(s)) and + ( + exists(int n | s.(BlockStmt).getStmt(n) = e and pred = "getStmt(" + n + ")") + or + s.(ComputedGotoStmt).getExpr() = e and pred = "getExpr()" + or + s.(ConstexprIfStmt).getCondition() = e and pred = "getCondition()" + or + s.(ConstexprIfStmt).getThen() = e and pred = "getThen()" + or + s.(ConstexprIfStmt).getElse() = e and pred = "getElse()" + or + s.(IfStmt).getCondition() = e and pred = "getCondition()" + or + s.(IfStmt).getThen() = e and pred = "getThen()" + or + s.(IfStmt).getElse() = e and pred = "getElse()" + or + s.(SwitchStmt).getExpr() = e and pred = "getExpr()" + or + s.(SwitchStmt).getStmt() = e and pred = "getStmt()" + or + s.(DoStmt).getCondition() = e and pred = "getCondition()" + or + s.(DoStmt).getStmt() = e and pred = "getStmt()" + or + s.(ForStmt).getInitialization() = e and pred = "getInitialization()" + or + s.(ForStmt).getCondition() = e and pred = "getCondition()" + or + s.(ForStmt).getUpdate() = e and pred = "getUpdate()" + or + s.(ForStmt).getStmt() = e and pred = "getStmt()" + or + s.(RangeBasedForStmt).getChild(0) = e and pred = "getChild(0)" + or + s.(RangeBasedForStmt).getBeginEndDeclaration() = e and pred = "getBeginEndDeclaration()" + or + s.(RangeBasedForStmt).getCondition() = e and pred = "getCondition()" + or + s.(RangeBasedForStmt).getUpdate() = e and pred = "getUpdate()" + or + s.(RangeBasedForStmt).getChild(4) = e and pred = "getChild(4)" + or + s.(RangeBasedForStmt).getStmt() = e and pred = "getStmt()" + or + s.(WhileStmt).getCondition() = e and pred = "getCondition()" + or + s.(WhileStmt).getStmt() = e and pred = "getStmt()" + or + exists(int n | + s.(DeclStmt).getDeclarationEntry(n) = e and pred = "getDeclarationEntry(" + n.toString() + ")" + ) + or + // EmptyStmt does not have children + s.(ExprStmt).getExpr() = e and pred = "getExpr()" + or + s.(Handler).getBlock() = e and pred = "getBlock()" + or + s.(JumpStmt).getTarget() = e and pred = "getTarget()" + or + s.(MicrosoftTryStmt).getStmt() = e and pred = "getStmt()" + or + s.(MicrosoftTryExceptStmt).getCondition() = e and pred = "getCondition()" + or + s.(MicrosoftTryExceptStmt).getExcept() = e and pred = "getExcept()" + or + s.(MicrosoftTryFinallyStmt).getFinally() = e and pred = "getFinally()" + or + s.(ReturnStmt).getExpr() = e and pred = "getExpr()" + or + s.(SwitchCase).getExpr() = e and pred = "getExpr()" + or + s.(SwitchCase).getEndExpr() = e and pred = "getEndExpr()" + or + s.(TryStmt).getStmt() = e and pred = "getStmt()" + or + s.(VlaDimensionStmt).getDimensionExpr() = e and pred = "getDimensionExpr()" + ) +} + +private predicate namedExprChildPredicates(Expr expr, Element ele, string pred) { + shouldPrintFunction(expr.getEnclosingFunction()) and + ( + expr.(Access).getTarget() = ele and pred = "getTarget()" + or + expr.(VariableAccess).getQualifier() = ele and pred = "getQualifier()" + or + exists(Field f | + expr.(ClassAggregateLiteral).getFieldExpr(f) = ele and + pred = "getFieldExpr(" + f.toString() + ")" + ) + or + exists(int n | + expr.(ArrayOrVectorAggregateLiteral).getElementExpr(n) = ele and + pred = "getElementExpr(" + n.toString() + ")" + ) + or + expr.(AlignofExprOperator).getExprOperand() = ele and pred = "getExprOperand()" + or + expr.(ArrayExpr).getArrayBase() = ele and pred = "getArrayBase()" + or + expr.(ArrayExpr).getArrayOffset() = ele and pred = "getArrayOffset()" + or + expr.(AssumeExpr).getOperand() = ele and pred = "getOperand()" + or + expr.(BuiltInComplexOperation).getRealOperand() = ele and pred = "getRealOperand()" + or + expr.(BuiltInComplexOperation).getImaginaryOperand() = ele and pred = "getImaginaryOperand()" + or + expr.(BuiltInVarArg).getVAList() = ele and pred = "getVAList()" + or + expr.(BuiltInVarArgCopy).getDestinationVAList() = ele and pred = "getDestinationVAList()" + or + expr.(BuiltInVarArgCopy).getSourceVAList() = ele and pred = "getSourceVAList()" + or + expr.(BuiltInVarArgsEnd).getVAList() = ele and pred = "getVAList()" + or + expr.(BuiltInVarArgsStart).getVAList() = ele and pred = "getVAList()" + or + expr.(BuiltInVarArgsStart).getLastNamedParameter() = ele and pred = "getLastNamedParameter()" + or + expr.(Call).getQualifier() = ele and pred = "getQualifier()" + or + exists(int n | expr.(Call).getArgument(n) = ele and pred = "getArgument(" + n.toString() + ")") + or + expr.(ExprCall).getExpr() = ele and pred = "getExpr()" + or + expr.(OverloadedArrayExpr).getArrayBase() = ele and pred = "getArrayBase()" + or + expr.(OverloadedArrayExpr).getArrayOffset() = ele and pred = "getArrayOffset()" + or + expr.(OverloadedPointerDereferenceExpr).getExpr() = ele and pred = "getExpr()" + or + expr.(CommaExpr).getLeftOperand() = ele and pred = "getLeftOperand()" + or + expr.(CommaExpr).getRightOperand() = ele and pred = "getRightOperand()" + or + expr.(ConditionDeclExpr).getVariableAccess() = ele and pred = "getVariableAccess()" + or + expr.(ConstructorFieldInit).getExpr() = ele and pred = "getExpr()" + or + expr.(Conversion).getExpr() = ele and pred = "getExpr()" + or + expr.(DeleteArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()" + or + expr.(DeleteArrayExpr).getDestructorCall() = ele and pred = "getDestructorCall()" + or + expr.(DeleteArrayExpr).getExpr() = ele and pred = "getExpr()" + or + expr.(DeleteExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()" + or + expr.(DeleteExpr).getDestructorCall() = ele and pred = "getDestructorCall()" + or + expr.(DeleteExpr).getExpr() = ele and pred = "getExpr()" + or + expr.(DestructorFieldDestruction).getExpr() = ele and pred = "getExpr()" + or + expr.(FoldExpr).getInitExpr() = ele and pred = "getInitExpr()" + or + expr.(FoldExpr).getPackExpr() = ele and pred = "getPackExpr()" + or + expr.(LambdaExpression).getInitializer() = ele and pred = "getInitializer()" + or + expr.(NewOrNewArrayExpr).getAllocatorCall() = ele and pred = "getAllocatorCall()" + or + expr.(NewOrNewArrayExpr).getAlignmentArgument() = ele and pred = "getAlignmentArgument()" + or + expr.(NewArrayExpr).getInitializer() = ele and pred = "getInitializer()" + or + expr.(NewArrayExpr).getExtent() = ele and pred = "getExtent()" + or + expr.(NewExpr).getInitializer() = ele and pred = "getInitializer()" + or + expr.(NoExceptExpr).getExpr() = ele and pred = "getExpr()" + or + expr.(Assignment).getLValue() = ele and pred = "getLValue()" + or + expr.(Assignment).getRValue() = ele and pred = "getRValue()" + or + not expr instanceof RelationalOperation and + expr.(BinaryOperation).getLeftOperand() = ele and + pred = "getLeftOperand()" + or + not expr instanceof RelationalOperation and + expr.(BinaryOperation).getRightOperand() = ele and + pred = "getRightOperand()" + or + expr.(RelationalOperation).getGreaterOperand() = ele and pred = "getGreaterOperand()" + or + expr.(RelationalOperation).getLesserOperand() = ele and pred = "getLesserOperand()" + or + expr.(ConditionalExpr).getCondition() = ele and pred = "getCondition()" + or + // If ConditionalExpr is in two-operand form, getThen() = getCondition() holds + not expr.(ConditionalExpr).isTwoOperand() and + expr.(ConditionalExpr).getThen() = ele and + pred = "getThen()" + or + expr.(ConditionalExpr).getElse() = ele and pred = "getElse()" + or + expr.(UnaryOperation).getOperand() = ele and pred = "getOperand()" + or + expr.(SizeofExprOperator).getExprOperand() = ele and pred = "getExprOperand()" + or + expr.(StmtExpr).getStmt() = ele and pred = "getStmt()" + or + expr.(ThrowExpr).getExpr() = ele and pred = "getExpr()" + or + expr.(TypeidOperator).getExpr() = ele and pred = "getExpr()" + ) +} + +/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */ +query predicate nodes(PrintASTNode node, string key, string value) { + node.shouldPrint() and + value = node.getProperty(key) +} + +/** + * Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the + * given `value`. + */ +query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) { + exists(int childIndex | + source.shouldPrint() and + target.shouldPrint() and + target = source.getChild(childIndex) and + ( + key = "semmle.label" and value = source.getChildAccessorPredicate(childIndex) + or + key = "semmle.order" and value = childIndex.toString() + ) + ) +} + +/** Holds if property `key` of the graph has the given `value`. */ +query predicate graphProperties(string key, string value) { + key = "semmle.graphKind" and value = "tree" +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Specifier.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Specifier.qll new file mode 100644 index 00000000000..4a425b690f4 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Specifier.qll @@ -0,0 +1,318 @@ +/** + * Provides classes for modeling specifiers and attributes. + */ + +import semmle.code.cpp.Element +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C/C++ specifier: `friend`, `auto`, `register`, `static`, `extern`, + * `mutable`, `inline`, `virtual`, or `explicit`. + */ +class Specifier extends Element, @specifier { + /** Gets a dummy location for the specifier. */ + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } + + override string getAPrimaryQlClass() { result = "Specifier" } + + /** Gets the name of this specifier. */ + string getName() { specifiers(underlyingElement(this), result) } + + /** Holds if the name of this specifier is `name`. */ + predicate hasName(string name) { name = this.getName() } + + override string toString() { result = this.getName() } +} + +/** + * A C/C++ function specifier: `inline`, `virtual`, or `explicit`. + */ +class FunctionSpecifier extends Specifier { + FunctionSpecifier() { + this.hasName("inline") or + this.hasName("virtual") or + this.hasName("explicit") + } + + override string getAPrimaryQlClass() { result = "FunctionSpecifier" } +} + +/** + * A C/C++ storage class specifier: `auto`, `register`, `static`, `extern`, + * or `mutable". + */ +class StorageClassSpecifier extends Specifier { + StorageClassSpecifier() { + this.hasName("auto") or + this.hasName("register") or + this.hasName("static") or + this.hasName("extern") or + this.hasName("mutable") + } + + override string getAPrimaryQlClass() { result = "StorageClassSpecifier" } +} + +/** + * A C++ access specifier: `public`, `protected`, or `private`. + */ +class AccessSpecifier extends Specifier { + AccessSpecifier() { + this.hasName("public") or + this.hasName("protected") or + this.hasName("private") + } + + /** + * Gets the visibility of a field with access specifier `this` if it is + * directly inherited with access specifier `baseAccess`. For example: + * + * ``` + * class A { protected int f; }; + * class B : private A {}; + * ``` + * + * In this example, `this` is `protected`, `baseAccess` is `private`, and + * `result` is `private` because the visibility of field `f` in class `B` + * is `private`. + * + * This method encodes the rules of N4140 11.2/1, tabulated here: + * + * ``` + * `this` | `baseAccess` | `result` + * (access in base) | (base class specifier) | (access in derived) + * ---------------------------------------------------------- + * private | private | N/A + * private | protected | N/A + * private | public | N/A + * protected | private | private + * protected | protected | protected + * protected | public | protected + * public | private | private + * public | protected | protected + * public | public | public + * ``` + */ + AccessSpecifier accessInDirectDerived(AccessSpecifier baseAccess) { + this.getName() != "private" and + ( + // Alphabetically, "private" < "protected" < "public". This disjunction + // encodes that `result` is the minimum access of `this` and + // `baseAccess`. + baseAccess.getName() < this.getName() and result = baseAccess + or + baseAccess.getName() >= this.getName() and result = this + ) + } + + override string getAPrimaryQlClass() { result = "AccessSpecifier" } +} + +/** + * An attribute introduced by GNU's `__attribute__((name))` syntax, + * Microsoft's `__declspec(name)` syntax, Microsoft's `[name]` syntax, the + * C++11 standard `[[name]]` syntax, or the C++11 `alignas` syntax. + */ +class Attribute extends Element, @attribute { + /** + * Gets the name of this attribute. + * + * As examples, this is "noreturn" for `__attribute__((__noreturn__))`, + * "fallthrough" for `[[clang::fallthrough]]`, and "dllimport" for + * `__declspec(dllimport)`. + * + * Note that the name does not include the namespace. For example, the + * name of `[[clang::fallthrough]]` is "fallthrough". + */ + string getName() { attributes(underlyingElement(this), _, result, _, _) } + + override Location getLocation() { attributes(underlyingElement(this), _, _, _, result) } + + /** Holds if the name of this attribute is `name`. */ + predicate hasName(string name) { name = this.getName() } + + override string toString() { result = this.getName() } + + /** Gets the `i`th argument of the attribute. */ + AttributeArgument getArgument(int i) { result.getAttribute() = this and result.getIndex() = i } + + /** Gets an argument of the attribute. */ + AttributeArgument getAnArgument() { result = getArgument(_) } +} + +/** + * An attribute introduced by GNU's `__attribute__((name))` syntax, for + * example: `__attribute__((__noreturn__))`. + */ +class GnuAttribute extends Attribute, @gnuattribute { } + +/** + * An attribute introduced by the C++11 standard `[[name]]` syntax, for + * example: `[[clang::fallthrough]]`. + */ +class StdAttribute extends Attribute, @stdattribute { + /** + * Gets the namespace of this attribute. + * + * As examples, this is "" for `[[carries_dependency]]`, and "clang" for + * `[[clang::fallthrough]]`. + */ + string getNamespace() { attributes(underlyingElement(this), _, _, result, _) } + + /** + * Holds if this attribute has the given namespace and name. + */ + predicate hasQualifiedName(string namespace, string name) { + namespace = getNamespace() and hasName(name) + } +} + +/** + * An attribute introduced by Microsoft's `__declspec(name)` syntax. For + * example the attribute on the following declaration: + * ``` + * __declspec(dllimport) void myFunction(); + * ``` + */ +class Declspec extends Attribute, @declspec { } + +/** + * An attribute introduced by Microsoft's "[name]" syntax, for example "[SA_Pre(Deref=1,Access=SA_Read)]". + */ +class MicrosoftAttribute extends Attribute, @msattribute { + AttributeArgument getNamedArgument(string name) { + result = getAnArgument() and result.getName() = name + } +} + +/** + * A C++11 `alignas` construct. For example the attribute in the following + * code: + * ``` + * struct alignas(16) MyStruct { + * int x; + * }; + * ``` + * Though it doesn't use the attribute syntax, `alignas(...)` is presented + * as an `Attribute` for consistency with the `[[align(...)]]` attribute. + */ +class AlignAs extends Attribute, @alignas { + override string toString() { result = "alignas(...)" } +} + +/** + * A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))` + * that declares a function to accept a `printf` style format string. For example the attribute + * on the following declaration: + * ``` + * int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2))); + * ``` + */ +class FormatAttribute extends GnuAttribute { + FormatAttribute() { getName() = "format" } + + /** + * Gets the archetype of this format attribute, for example + * `"printf"`. + */ + string getArchetype() { result = getArgument(0).getValueText() } + + /** + * Gets the index in (1-based) format attribute notation associated + * with the first argument of the function. + */ + private int firstArgumentNumber() { + if exists(MemberFunction f | f.getAnAttribute() = this and not f.isStatic()) + then + // 1 is `this`, so the first parameter is 2 + result = 2 + else result = 1 + } + + /** + * Gets the (0-based) index of the format string, + * according to this attribute. + */ + int getFormatIndex() { result = getArgument(1).getValueInt() - firstArgumentNumber() } + + /** + * Gets the (0-based) index of the first format argument (if any), + * according to this attribute. + */ + int getFirstFormatArgIndex() { + exists(int val | + val = getArgument(2).getValueInt() and + result = val - firstArgumentNumber() and + not val = 0 // indicates a `vprintf` style format function with arguments not directly available. + ) + } + + override string getAPrimaryQlClass() { result = "FormatAttribute" } +} + +/** + * An argument to an `Attribute`. For example the argument "dllimport" on the + * attribute in the following code: + * ``` + * __declspec(dllimport) void myFunction(); + * ``` + */ +class AttributeArgument extends Element, @attribute_arg { + /** + * Gets the name of this argument, if it is a named argument. Named + * arguments are a Microsoft feature, so only a `MicrosoftAttribute` can + * have a named argument. + */ + string getName() { attribute_arg_name(underlyingElement(this), result) } + + /** + * Gets the text for the value of this argument, if its value is + * a string or a number. + */ + string getValueText() { attribute_arg_value(underlyingElement(this), result) } + + /** + * Gets the value of this argument, if its value is integral. + */ + int getValueInt() { result = getValueText().toInt() } + + /** + * Gets the value of this argument, if its value is a type. + */ + Type getValueType() { attribute_arg_type(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets the attribute to which this is an argument. + */ + Attribute getAttribute() { + attribute_args(underlyingElement(this), _, unresolveElement(result), _, _) + } + + /** + * Gets the zero-based index of this argument in the containing + * attribute's argument list. + */ + int getIndex() { attribute_args(underlyingElement(this), _, _, result, _) } + + override Location getLocation() { attribute_args(underlyingElement(this), _, _, _, result) } + + override string toString() { + if exists(@attribute_arg_empty self | self = underlyingElement(this)) + then result = "empty argument" + else + exists(string prefix, string tail | + (if exists(getName()) then prefix = getName() + "=" else prefix = "") and + ( + if exists(@attribute_arg_type self | self = underlyingElement(this)) + then tail = getValueType().getName() + else tail = getValueText() + ) and + result = prefix + tail + ) + } +} + +private predicate suppressUnusedThis(Specifier s) { any() } diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Struct.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Struct.qll new file mode 100644 index 00000000000..50a208894b4 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Struct.qll @@ -0,0 +1,75 @@ +/** + * Provides classes for modeling `struct`s. + */ + +import semmle.code.cpp.Type +import semmle.code.cpp.Class + +/** + * A C/C++ structure or union. For example, the types `MyStruct` and `MyUnion` + * in: + * ``` + * struct MyStruct { + * int x, y, z; + * }; + * + * union MyUnion { + * int i; + * float f; + * }; + * ``` + */ +class Struct extends Class { + Struct() { usertypes(underlyingElement(this), _, 1) or usertypes(underlyingElement(this), _, 3) } + + override string getAPrimaryQlClass() { result = "Struct" } + + override string explain() { result = "struct " + this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts +} + +/** + * A C/C++ struct that is directly enclosed by a function. For example, the type + * `MyLocalStruct` in: + * ``` + * void myFunction() { + * struct MyLocalStruct { + * int x, y, z; + * }; + * } + * ``` + */ +class LocalStruct extends Struct { + LocalStruct() { isLocal() } + + override string getAPrimaryQlClass() { not this instanceof LocalUnion and result = "LocalStruct" } +} + +/** + * A C/C++ nested struct. See 11.12. For example, the type `MyNestedStruct` in: + * ``` + * class MyClass { + * public: + * struct MyNestedStruct { + * int x, y, z; + * }; + * }; + * ``` + */ +class NestedStruct extends Struct { + NestedStruct() { this.isMember() } + + override string getAPrimaryQlClass() { + not this instanceof NestedUnion and result = "NestedStruct" + } + + /** Holds if this member is private. */ + predicate isPrivate() { this.hasSpecifier("private") } + + /** Holds if this member is protected. */ + predicate isProtected() { this.hasSpecifier("protected") } + + /** Holds if this member is public. */ + predicate isPublic() { this.hasSpecifier("public") } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/TestFile.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/TestFile.qll new file mode 100644 index 00000000000..b9e3fe3a614 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/TestFile.qll @@ -0,0 +1,89 @@ +/** + * Provides classes for identifying files that contain test cases. It is often + * desirable to exclude these files from analysis. + */ + +import semmle.code.cpp.File + +/** + * The `gtest/gtest.h` file. + */ +private class GoogleTestHeader extends File { + GoogleTestHeader() { + getBaseName() = "gtest.h" and + getParentContainer().getBaseName() = "gtest" + } +} + +/** + * A test using the Google Test library. + */ +private class GoogleTest extends MacroInvocation { + GoogleTest() { + // invocation of a macro from Google Test. + this.getMacro().getFile() instanceof GoogleTestHeader + } +} + +/** + * The `boost/test` directory. + */ +private class BoostTestFolder extends Folder { + BoostTestFolder() { + getBaseName() = "test" and + getParentContainer().getBaseName() = "boost" + } +} + +/** + * A test using the Boost Test library. + */ +private class BoostTest extends MacroInvocation { + BoostTest() { + // invocation of a macro from Boost Test. + this.getMacro().getFile().getParentContainer+() instanceof BoostTestFolder + } +} + +/** + * The `cppunit` directory. + */ +private class CppUnitFolder extends Folder { + CppUnitFolder() { getBaseName() = "cppunit" } +} + +/** + * A class from the `cppunit` directory. + */ +private class CppUnitClass extends Class { + CppUnitClass() { + getFile().getParentContainer+() instanceof CppUnitFolder and + getNamespace().getParentNamespace*().getName() = "CppUnit" + } +} + +/** + * A test using the CppUnit library. + */ +private class CppUnitTest extends Element { + CppUnitTest() { + // class with a base class from cppunit. + this.(Class).getABaseClass*() instanceof CppUnitClass and + // class itself is not a part of cppunit. + not this instanceof CppUnitClass + or + // any member function of a test is also test code + this.(Function).getDeclaringType() instanceof CppUnitTest + } +} + +/** + * A file that contains one or more test cases. + */ +class TestFile extends File { + TestFile() { + exists(GoogleTest test | test.getFile() = this) or + exists(BoostTest test | test.getFile() = this) or + exists(CppUnitTest test | test.getFile() = this) + } +} diff --git a/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Type.qll b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Type.qll new file mode 100644 index 00000000000..bf3defd4f40 --- /dev/null +++ b/repo-tests/codeql/cpp/ql/lib/semmle/code/cpp/Type.qll @@ -0,0 +1,1745 @@ +/** + * Provides a hierarchy of classes for modeling C/C++ types. + */ + +import semmle.code.cpp.Element +import semmle.code.cpp.Function +private import semmle.code.cpp.internal.ResolveClass + +/** + * A C/C++ type. + * + * This QL class represents the root of the C/C++ type hierarchy. + */ +class Type extends Locatable, @type { + Type() { isType(underlyingElement(this)) } + + /** + * Gets the name of this type. + */ + string getName() { none() } + + /** + * Holds if this type is called `name`. + */ + predicate hasName(string name) { name = this.getName() } + + /** + * Holds if this declaration has a specifier called `name`, recursively looking + * through `typedef` and `decltype`. For example, in the context of + * `typedef const int *restrict t`, the type `volatile t` has specifiers + * `volatile` and `restrict` but not `const` since the `const` is attached to + * the type being pointed to rather than the pointer itself. + */ + // This predicate should not be overridden, but it cannot be declared `final` + // because there is a similarly-named predicate in Declaration, and UserType + // inherits from both Type and Declaration and must override it to resolve + // the ambiguity. + predicate hasSpecifier(string name) { this.getASpecifier().hasName(name) } + + /** + * Gets a specifier of this type, recursively looking through `typedef` and + * `decltype`. For example, in the context of `typedef const int *restrict + * t`, the type `volatile t` has specifiers `volatile` and `restrict` but not + * `const` since the `const` is attached to the type being pointed to rather + * than the pointer itself. + */ + // This predicate should not be overridden, but it cannot be declared `final` + // because there is a similarly-named predicate in Declaration, and UserType + // inherits from both Type and Declaration and must override it to resolve + // the ambiguity. + Specifier getASpecifier() { + typespecifiers(underlyingElement(this), unresolveElement(result)) + or + result = this.internal_getAnAdditionalSpecifier() + } + + /** + * Gets an attribute of this type. + */ + Attribute getAnAttribute() { typeattributes(underlyingElement(this), unresolveElement(result)) } + + /** + * Internal -- should be `protected` when QL supports such a flag. Subtypes + * override this to recursively get specifiers that are not attached directly + * to this `@type` in the database but arise through type aliases such as + * `typedef` and `decltype`. + */ + Specifier internal_getAnAdditionalSpecifier() { none() } + + /** + * Holds if this type is const. + */ + predicate isConst() { this.hasSpecifier("const") } + + /** + * Holds if this type is volatile. + */ + predicate isVolatile() { this.hasSpecifier("volatile") } + + /** + * Holds if this type refers to type `t` (by default, + * a type always refers to itself). + */ + predicate refersTo(Type t) { refersToDirectly*(t) } + + /** + * Holds if this type refers to type `t` directly. + */ + predicate refersToDirectly(Type t) { none() } + + /** + * Gets this type after typedefs have been resolved. + * + * The result of this predicate will be the type itself, except in the case of a TypedefType or a Decltype, + * in which case the result will be type which results from (possibly recursively) resolving typedefs. + */ + Type getUnderlyingType() { result = this } + + /** + * Gets this type after specifiers have been deeply stripped and typedefs have been resolved. + * + * For example, starting with `const i64* const` in the context of `typedef long long i64;`, this predicate will return `long long*`. + */ + pragma[nomagic] + Type getUnspecifiedType() { unspecifiedtype(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets this type after any top-level specifiers and typedefs have been stripped. + * + * For example, starting with `const i64* const`, this predicate will return `const i64*`. + */ + Type stripTopLevelSpecifiers() { result = this } + + /** + * Gets the size of this type in bytes. + */ + int getSize() { + builtintypes(underlyingElement(this), _, _, result, _, _) or + pointerishsize(underlyingElement(this), result, _) or + usertypesize(underlyingElement(this), result, _) + } + + /** + * Gets the alignment of this type in bytes. + */ + int getAlignment() { + builtintypes(underlyingElement(this), _, _, _, _, result) or + pointerishsize(underlyingElement(this), _, result) or + usertypesize(underlyingElement(this), _, result) + } + + /** + * Gets the pointer indirection level of this type. + */ + int getPointerIndirectionLevel() { result = 0 } + + /** + * Gets a detailed string representation explaining the AST of this type + * (with all specifiers and nested constructs such as pointers). This is + * intended to help debug queries and is a very expensive operation; not + * to be used in production queries. + * + * An example output is "const {pointer to {const {char}}}". + */ + string explain() { result = "type" } // Concrete base impl to allow filters on Type + + /** + * Holds if this type is constant and only contains constant types. + * For instance, a `char *const` is a constant type, but not deeply constant, + * because while the pointer can't be modified the character can. The type + * `const char *const*` is a deeply constant type though - both the pointer + * and what it points to are immutable. + */ + predicate isDeeplyConst() { this.isConst() and this.isDeeplyConstBelow() } + + /** + * Holds if this type is constant and only contains constant types, excluding + * the type itself. It is implied by Type.isDeeplyConst() and is just used to + * implement that predicate. + * For example, `const char *const` is deeply constant and deeply constant below, + * but `const char *` is only deeply constant below (the pointer can be changed, + * but not the underlying char). `char *const` is neither (it is just `const`). + */ + predicate isDeeplyConstBelow() { none() } // Concrete base impl to allow filters on Type + + /** + * Gets as many places as possible where this type is used by name in the source after macros have been replaced + * (in particular, therefore, this will find type name uses caused by macros). Note that all type name uses within + * instantiations are currently excluded - this is too draconian in the absence of indexing prototype instantiations + * of functions, and is likely to improve in the future. At present, the method takes the conservative approach of + * giving valid type name uses, but not necessarily *all* type name uses. + */ + Element getATypeNameUse() { + // An explicit cast to a type referring to T uses T. We exclude casts within instantiations, + // since they do not appear directly in the source. + exists(Cast c | + not c.isImplicit() and + c.getType().refersTo(this) and + result = c and + not c.getEnclosingFunction().isConstructedFrom(_) + ) + or + // A class derivation from a type referring to T uses T. We exclude class derivations within + // instantiations, since they do not appear directly in the source. + exists(ClassDerivation cd | + cd.getBaseType().refersTo(this) and + result = cd and + not cd.getDerivedClass() instanceof ClassTemplateInstantiation + ) + or + // A new, new array, or placement new expression with a type that refers to T uses T. + // We exclude news within instantiations, since they do not appear directly in the source. + exists(Expr e | + ( + e instanceof NewArrayExpr or + e instanceof NewExpr + ) and + e.getType().refersTo(this) and + result = e and + not e.getEnclosingFunction().isConstructedFrom(_) + ) + or + // The declaration of a function that returns a type referring to T uses T. We exclude + // declarations of function template instantiations, since their return types do not + // appear directly in the source. We also exclude constructors and destructors, since + // they are indexed with a dummy return type of void that does not appear in the source. + exists(FunctionDeclarationEntry fde, Type t | + (if exists(fde.getTypedefType()) then t = fde.getTypedefType() else t = fde.getType()) and + t.refersTo(this) and + result = fde and + not fde.getDeclaration().isConstructedFrom(_) and + not fde.getDeclaration() instanceof Constructor and + not fde.getDeclaration() instanceof Destructor + ) + or + // A function call that provides an explicit template argument that refers to T uses T. + // We exclude calls within instantiations, since they do not appear directly in the source. + exists(FunctionCall c | + c.getAnExplicitTemplateArgument().(Type).refersTo(this) and + result = c and + not c.getEnclosingFunction().isConstructedFrom(_) + ) + or + // Qualifying an expression with a type that refers to T uses T. We exclude qualifiers + // within instantiations, since they do not appear directly in the source. + exists(NameQualifier nq | + nq.getQualifyingElement().(Type).refersTo(this) and + result = nq and + not nq.getExpr().getEnclosingFunction().isConstructedFrom(_) + ) + or + // Calculating the size of a type that refers to T uses T. We exclude sizeofs within + // instantiations, since they do not appear directly in the source. + exists(SizeofTypeOperator soto | + soto.getTypeOperand().refersTo(this) and + result = soto and + not soto.getEnclosingFunction().isConstructedFrom(_) + ) + or + // A typedef of a type that refers to T uses T. + exists(TypeDeclarationEntry tde | + tde.getDeclaration().(TypedefType).getBaseType().refersTo(this) and + result = tde + ) + or + // Using something declared within a type that refers to T uses T. + exists(UsingDeclarationEntry ude | + ude.getDeclaration().getDeclaringType().refersTo(this) and + result = ude + ) + or + // The declaration of a variable with a type that refers to T uses T. We exclude declarations within + // instantiations, since those do not appear directly in the source. + exists(VariableDeclarationEntry vde | + vde.getType().refersTo(this) and + result = vde and + not exists(LocalScopeVariable sv | + sv = vde.getDeclaration() and sv.getFunction().isConstructedFrom(_) + ) and + not exists(MemberVariable mv | + mv = vde.getDeclaration() and mv.getDeclaringType() instanceof ClassTemplateInstantiation + ) + ) + } + + /** + * Holds if this type involves a reference. + */ + predicate involvesReference() { none() } + + /** + * Holds if this type involves a template parameter. + */ + predicate involvesTemplateParameter() { none() } + + /** + * Gets this type with any typedefs resolved. For example, given + * `typedef C T`, this would resolve `const T&` to `const C&`. + * Note that this will only work if the resolved type actually appears + * on its own elsewhere in the program. + */ + Type resolveTypedefs() { result = this } + + /** + * Gets the type stripped of pointers, references and cv-qualifiers, and resolving typedefs. + * For example, given `typedef const C& T`, `stripType` returns `C`. + */ + Type stripType() { result = this } + + override Location getLocation() { + suppressUnusedThis(this) and + result instanceof UnknownDefaultLocation + } +} + +/** + * A C/C++ built-in primitive type (int, float, void, and so on). See 4.1.1. + * In the following example, `unsigned int` and `double` denote primitive + * built-in types: + * ``` + * double a; + * unsigned int ua[40]; + * typedef double LargeFloat; + * ``` + */ +class BuiltInType extends Type, @builtintype { + override string toString() { result = this.getName() } + + override string getName() { builtintypes(underlyingElement(this), result, _, _, _, _) } + + override string explain() { result = this.getName() } + + override predicate isDeeplyConstBelow() { any() } // No subparts +} + +/** + * An erroneous type. This type has no corresponding C/C++ syntax. + * + * `ErroneousType` is the type of `ErrorExpr`, which in turn refers to an illegal + * language construct. In the example below, a temporary (`0`) cannot be bound + * to an lvalue reference (`int &`): + * ``` + * int &intref = 0; + * ``` + */ +class ErroneousType extends BuiltInType { + ErroneousType() { builtintypes(underlyingElement(this), _, 1, _, _, _) } + + override string getAPrimaryQlClass() { result = "ErroneousType" } +} + +/** + * The unknown type. This type has no corresponding C/C++ syntax. + * + * Unknown types usually occur inside _uninstantiated_ template functions. + * In the example below, the expressions `x.a` and `x.b` have unknown type + * in the _uninstantiated_ template. + * ``` + * template + * bool check(T x) { + * if (x.a == x.b) + * abort(); + * } + * ``` + */ +class UnknownType extends BuiltInType { + UnknownType() { builtintypes(underlyingElement(this), _, 2, _, _, _) } + + override string getAPrimaryQlClass() { result = "UnknownType" } +} + +private predicate isArithmeticType(@builtintype type, int kind) { + builtintypes(type, _, kind, _, _, _) and + kind >= 4 and + kind != 34 // Exclude decltype(nullptr) +} + +/** + * The C/C++ arithmetic types. See 4.1.1. + * + * This includes primitive types on which arithmetic, bitwise or logical + * operations may be performed. Examples of arithmetic types include + * `char`, `int`, `float`, and `bool`. + */ +class ArithmeticType extends BuiltInType { + ArithmeticType() { isArithmeticType(underlyingElement(this), _) } + + override string getAPrimaryQlClass() { result = "ArithmeticType" } +} + +private predicate isIntegralType(@builtintype type, int kind) { + isArithmeticType(type, kind) and + ( + kind < 24 + or + kind = 33 + or + 35 <= kind and kind <= 37 + or + kind = 43 + or + kind = 44 + or + kind = 51 + ) +} + +/** + * A C/C++ integral or `enum` type. + * + * The definition of "integral type" in the C++ standard excludes `enum` types, + * but because an `enum` type holds a value of its underlying integral type, + * it is often useful to have a common category that includes both integral + * and `enum` types. + * + * In the following example, `a`, `b` and `c` are all declared with an + * integral or `enum` type: + * ``` + * unsigned long a; + * enum e1 { val1, val2 } b; + * enum class e2: short { val3, val4 } c; + * ``` + */ +class IntegralOrEnumType extends Type { + IntegralOrEnumType() { + // Integral type + isIntegralType(underlyingElement(this), _) + or + // Enum type + ( + usertypes(underlyingElement(this), _, 4) or + usertypes(underlyingElement(this), _, 13) + ) + } +} + +/** + * Maps between different integral types of the same size. + * + * original: The original type. Can be any integral type kind. + * canonical: The canonical form of the type + * - plain T -> T + * - signed T -> T (except signed char -> signed char) + * - unsigned T -> unsigned T + * unsigned: The explicitly unsigned form of the type. + * signed: The explicitly signed form of the type. + */ +private predicate integralTypeMapping(int original, int canonical, int unsigned, int signed) { + original = 4 and canonical = 4 and unsigned = -1 and signed = -1 // bool + or + original = 5 and canonical = 5 and unsigned = 6 and signed = 7 // char + or + original = 6 and canonical = 6 and unsigned = 6 and signed = 7 // unsigned char + or + original = 7 and canonical = 7 and unsigned = 6 and signed = 7 // signed char + or + original = 8 and canonical = 8 and unsigned = 9 and signed = 10 // short + or + original = 9 and canonical = 9 and unsigned = 9 and signed = 10 // unsigned short + or + original = 10 and canonical = 8 and unsigned = 9 and signed = 10 // signed short + or + original = 11 and canonical = 11 and unsigned = 12 and signed = 13 // int + or + original = 12 and canonical = 12 and unsigned = 12 and signed = 13 // unsigned int + or + original = 13 and canonical = 11 and unsigned = 12 and signed = 13 // signed int + or + original = 14 and canonical = 14 and unsigned = 15 and signed = 16 // long + or + original = 15 and canonical = 15 and unsigned = 15 and signed = 16 // unsigned long + or + original = 16 and canonical = 14 and unsigned = 15 and signed = 16 // signed long + or + original = 17 and canonical = 17 and unsigned = 18 and signed = 19 // long long + or + original = 18 and canonical = 18 and unsigned = 18 and signed = 19 // unsigned long long + or + original = 19 and canonical = 17 and unsigned = 18 and signed = 19 // signed long long + or + original = 33 and canonical = 33 and unsigned = -1 and signed = -1 // wchar_t + or + original = 35 and canonical = 35 and unsigned = 36 and signed = 37 // __int128 + or + original = 36 and canonical = 36 and unsigned = 36 and signed = 37 // unsigned __int128 + or + original = 37 and canonical = 35 and unsigned = 36 and signed = 37 // signed __int128 + or + original = 43 and canonical = 43 and unsigned = -1 and signed = -1 // char16_t + or + original = 44 and canonical = 44 and unsigned = -1 and signed = -1 // char32_t + or + original = 51 and canonical = 51 and unsigned = -1 and signed = -1 // char8_t +} + +/** + * The C/C++ integral types. See 4.1.1. These are types that are represented + * as integers of varying sizes. Both `enum` types and floating-point types + * are excluded. + * + * In the following examples, `a`, `b` and `c` are declared using integral + * types: + * ``` + * unsigned int a; + * long long b; + * char c; + * ``` + */ +class IntegralType extends ArithmeticType, IntegralOrEnumType { + int kind; + + IntegralType() { isIntegralType(underlyingElement(this), kind) } + + /** Holds if this integral type is signed. */ + predicate isSigned() { builtintypes(underlyingElement(this), _, _, _, -1, _) } + + /** Holds if this integral type is unsigned. */ + predicate isUnsigned() { builtintypes(underlyingElement(this), _, _, _, 1, _) } + + /** Holds if this integral type is explicitly signed. */ + predicate isExplicitlySigned() { + builtintypes(underlyingElement(this), _, 7, _, _, _) or + builtintypes(underlyingElement(this), _, 10, _, _, _) or + builtintypes(underlyingElement(this), _, 13, _, _, _) or + builtintypes(underlyingElement(this), _, 16, _, _, _) or + builtintypes(underlyingElement(this), _, 19, _, _, _) or + builtintypes(underlyingElement(this), _, 37, _, _, _) + } + + /** Holds if this integral type is explicitly unsigned. */ + predicate isExplicitlyUnsigned() { + builtintypes(underlyingElement(this), _, 6, _, _, _) or + builtintypes(underlyingElement(this), _, 9, _, _, _) or + builtintypes(underlyingElement(this), _, 12, _, _, _) or + builtintypes(underlyingElement(this), _, 15, _, _, _) or + builtintypes(underlyingElement(this), _, 18, _, _, _) or + builtintypes(underlyingElement(this), _, 36, _, _, _) + } + + /** Holds if this integral type is implicitly signed. */ + predicate isImplicitlySigned() { + builtintypes(underlyingElement(this), _, 5, _, -1, _) or + builtintypes(underlyingElement(this), _, 8, _, -1, _) or + builtintypes(underlyingElement(this), _, 11, _, -1, _) or + builtintypes(underlyingElement(this), _, 14, _, -1, _) or + builtintypes(underlyingElement(this), _, 17, _, -1, _) or + builtintypes(underlyingElement(this), _, 35, _, -1, _) + } + + /** + * Gets the unsigned type corresponding to this integral type. For + * example on a `short`, this would give the type `unsigned short`. + */ + IntegralType getUnsigned() { + exists(int unsignedKind | + integralTypeMapping(kind, _, unsignedKind, _) and + builtintypes(unresolveElement(result), _, unsignedKind, _, _, _) + ) + } + + /** + * Gets the canonical type corresponding to this integral type. + * + * For a plain type, this gives the same type (e.g. `short` -> `short`). + * For an explicitly unsigned type, this gives the same type (e.g. `unsigned short` -> `unsigned short`). + * For an explicitly signed type, this gives the plain version of that type (e.g. `signed short` -> `short`), except + * that `signed char` -> `signed char`. + */ + IntegralType getCanonicalArithmeticType() { + exists(int canonicalKind | + integralTypeMapping(kind, canonicalKind, _, _) and + builtintypes(unresolveElement(result), _, canonicalKind, _, _, _) + ) + } +} + +/** + * The C/C++ boolean type. See 4.2. This is the C `_Bool` type + * or the C++ `bool` type. For example: + * ``` + * extern bool a, b; // C++ + * _Bool c, d; // C + * ``` + */ +class BoolType extends IntegralType { + BoolType() { builtintypes(underlyingElement(this), _, 4, _, _, _) } + + override string getAPrimaryQlClass() { result = "BoolType" } +} + +/** + * The C/C++ character types. See 4.3. This includes the `char`, + * `signed char` and `unsigned char` types, all of which are + * distinct from one another. For example: + * ``` + * char a, b; + * signed char c, d; + * unsigned char e, f; + * ``` + */ +class CharType extends IntegralType { + CharType() { builtintypes(underlyingElement(this), _, [5, 6, 7], _, _, _) } +} + +/** + * The C/C++ `char` type (which is distinct from `signed char` and + * `unsigned char`). For example: + * ``` + * char a, b; + * ``` + */ +class PlainCharType extends CharType { + PlainCharType() { builtintypes(underlyingElement(this), _, 5, _, _, _) } + + override string getAPrimaryQlClass() { result = "PlainCharType" } +} + +/** + * The C/C++ `unsigned char` type (which is distinct from plain `char` + * even when `char` is `unsigned` by default). + * ``` + * unsigned char e, f; + * ``` + */ +class UnsignedCharType extends CharType { + UnsignedCharType() { builtintypes(underlyingElement(this), _, 6, _, _, _) } + + override string getAPrimaryQlClass() { result = "UnsignedCharType" } +} + +/** + * The C/C++ `signed char` type (which is distinct from plain `char` + * even when `char` is `signed` by default). + * ``` + * signed char c, d; + * ``` + */ +class SignedCharType extends CharType { + SignedCharType() { builtintypes(underlyingElement(this), _, 7, _, _, _) } + + override string getAPrimaryQlClass() { result = "SignedCharType" } +} + +/** + * The C/C++ short types. See 4.3. This includes `short`, `signed short` + * and `unsigned short`. + * ``` + * signed short ss; + * ``` + */ +class ShortType extends IntegralType { + ShortType() { + builtintypes(underlyingElement(this), _, 8, _, _, _) or + builtintypes(underlyingElement(this), _, 9, _, _, _) or + builtintypes(underlyingElement(this), _, 10, _, _, _) + } + + override string getAPrimaryQlClass() { result = "ShortType" } +} + +/** + * The C/C++ integer types. See 4.4. This includes `int`, `signed int` + * and `unsigned int`. + * ``` + * unsigned int ui; + * ``` + */ +class IntType extends IntegralType { + IntType() { + builtintypes(underlyingElement(this), _, 11, _, _, _) or + builtintypes(underlyingElement(this), _, 12, _, _, _) or + builtintypes(underlyingElement(this), _, 13, _, _, _) + } + + override string getAPrimaryQlClass() { result = "IntType" } +} + +/** + * The C/C++ long types. See 4.4. This includes `long`, `signed long` + * and `unsigned long`. + * ``` + * long l; + * ``` + */ +class LongType extends IntegralType { + LongType() { + builtintypes(underlyingElement(this), _, 14, _, _, _) or + builtintypes(underlyingElement(this), _, 15, _, _, _) or + builtintypes(underlyingElement(this), _, 16, _, _, _) + } + + override string getAPrimaryQlClass() { result = "LongType" } +} + +/** + * The C/C++ long long types. See 4.4. This includes `long long`, `signed long long` + * and `unsigned long long`. + * ``` + * signed long long sll; + * ``` + */ +class LongLongType extends IntegralType { + LongLongType() { + builtintypes(underlyingElement(this), _, 17, _, _, _) or + builtintypes(underlyingElement(this), _, 18, _, _, _) or + builtintypes(underlyingElement(this), _, 19, _, _, _) + } + + override string getAPrimaryQlClass() { result = "LongLongType" } +} + +/** + * The GNU C __int128 primitive types. They are not part of standard C/C++. + * + * This includes `__int128`, `signed __int128` and `unsigned __int128`. + * ``` + * unsigned __int128 ui128; + * ``` + */ +class Int128Type extends IntegralType { + Int128Type() { + builtintypes(underlyingElement(this), _, 35, _, _, _) or + builtintypes(underlyingElement(this), _, 36, _, _, _) or + builtintypes(underlyingElement(this), _, 37, _, _, _) + } + + override string getAPrimaryQlClass() { result = "Int128Type" } +} + +private newtype TTypeDomain = + TRealDomain() or + TComplexDomain() or + TImaginaryDomain() + +/** + * The type domain of a floating-point type. One of `RealDomain`, `ComplexDomain`, or + * `ImaginaryDomain`. + */ +class TypeDomain extends TTypeDomain { + /** Gets a textual representation of this type domain. */ + string toString() { none() } +} + +/** + * The type domain of a floating-point type that represents a real number. + */ +class RealDomain extends TypeDomain, TRealDomain { + final override string toString() { result = "real" } +} + +/** + * The type domain of a floating-point type that represents a complex number. + */ +class ComplexDomain extends TypeDomain, TComplexDomain { + final override string toString() { result = "complex" } +} + +/** + * The type domain of a floating-point type that represents an imaginary number. + */ +class ImaginaryDomain extends TypeDomain, TImaginaryDomain { + final override string toString() { result = "imaginary" } +} + +/** + * Data for floating-point types. + * + * kind: The original type kind. Can be any floating-point type kind. + * base: The numeric base of the number's representation. Can be 2 (binary) or 10 (decimal). + * domain: The type domain of the type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`. + * realKind: The type kind of the corresponding real type. For example, the corresponding real type + * of `_Complex double` is `double`. + * extended: `true` if the number is an extended-precision floating-point number, such as + * `_Float32x`. + */ +private predicate floatingPointTypeMapping( + int kind, int base, TTypeDomain domain, int realKind, boolean extended +) { + // float + kind = 24 and base = 2 and domain = TRealDomain() and realKind = 24 and extended = false + or + // double + kind = 25 and base = 2 and domain = TRealDomain() and realKind = 25 and extended = false + or + // long double + kind = 26 and base = 2 and domain = TRealDomain() and realKind = 26 and extended = false + or + // _Complex float + kind = 27 and base = 2 and domain = TComplexDomain() and realKind = 24 and extended = false + or + // _Complex double + kind = 28 and base = 2 and domain = TComplexDomain() and realKind = 25 and extended = false + or + // _Complex long double + kind = 29 and base = 2 and domain = TComplexDomain() and realKind = 26 and extended = false + or + // _Imaginary float + kind = 30 and base = 2 and domain = TImaginaryDomain() and realKind = 24 and extended = false + or + // _Imaginary double + kind = 31 and base = 2 and domain = TImaginaryDomain() and realKind = 25 and extended = false + or + // _Imaginary long double + kind = 32 and base = 2 and domain = TImaginaryDomain() and realKind = 26 and extended = false + or + // __float128 + kind = 38 and base = 2 and domain = TRealDomain() and realKind = 38 and extended = false + or + // _Complex __float128 + kind = 39 and base = 2 and domain = TComplexDomain() and realKind = 38 and extended = false + or + // _Decimal32 + kind = 40 and base = 10 and domain = TRealDomain() and realKind = 40 and extended = false + or + // _Decimal64 + kind = 41 and base = 10 and domain = TRealDomain() and realKind = 41 and extended = false + or + // _Decimal128 + kind = 42 and base = 10 and domain = TRealDomain() and realKind = 42 and extended = false + or + // _Float32 + kind = 45 and base = 2 and domain = TRealDomain() and realKind = 45 and extended = false + or + // _Float32x + kind = 46 and base = 2 and domain = TRealDomain() and realKind = 46 and extended = true + or + // _Float64 + kind = 47 and base = 2 and domain = TRealDomain() and realKind = 47 and extended = false + or + // _Float64x + kind = 48 and base = 2 and domain = TRealDomain() and realKind = 48 and extended = true + or + // _Float128 + kind = 49 and base = 2 and domain = TRealDomain() and realKind = 49 and extended = false + or + // _Float128x + kind = 50 and base = 2 and domain = TRealDomain() and realKind = 50 and extended = true +} + +/** + * The C/C++ floating point types. See 4.5. This includes `float`, `double` and `long double`, the + * fixed-size floating-point types like `_Float32`, the extended-precision floating-point types like + * `_Float64x`, and the decimal floating-point types like `_Decimal32`. It also includes the complex + * and imaginary versions of all of these types. + */ +class FloatingPointType extends ArithmeticType { + final int base; + final TypeDomain domain; + final int realKind; + final boolean extended; + + FloatingPointType() { + exists(int kind | + builtintypes(underlyingElement(this), _, kind, _, _, _) and + floatingPointTypeMapping(kind, base, domain, realKind, extended) + ) + } + + /** Gets the numeric base of this type's representation: 2 (binary) or 10 (decimal). */ + final int getBase() { result = base } + + /** + * Gets the type domain of this type. Can be `RealDomain`, `ComplexDomain`, or `ImaginaryDomain`. + */ + final TypeDomain getDomain() { result = domain } + + /** + * Gets the corresponding real type of this type. For example, the corresponding real type of + * `_Complex double` is `double`. + */ + final RealNumberType getRealType() { + builtintypes(unresolveElement(result), _, realKind, _, _, _) + } + + /** Holds if this type is an extended precision floating-point type, such as `_Float32x`. */ + final predicate isExtendedPrecision() { extended = true } +} + +/** + * A floating-point type representing a real number. + */ +class RealNumberType extends FloatingPointType { + RealNumberType() { domain instanceof RealDomain } +} + +/** + * A floating-point type representing a complex number. + */ +class ComplexNumberType extends FloatingPointType { + ComplexNumberType() { domain instanceof ComplexDomain } +} + +/** + * A floating-point type representing an imaginary number. + */ +class ImaginaryNumberType extends FloatingPointType { + ImaginaryNumberType() { domain instanceof ImaginaryDomain } +} + +/** + * A floating-point type whose representation is base 2. + */ +class BinaryFloatingPointType extends FloatingPointType { + BinaryFloatingPointType() { base = 2 } +} + +/** + * A floating-point type whose representation is base 10. + */ +class DecimalFloatingPointType extends FloatingPointType { + DecimalFloatingPointType() { base = 10 } +} + +/** + * The C/C++ `float` type. + * ``` + * float f; + * ``` + */ +class FloatType extends RealNumberType, BinaryFloatingPointType { + FloatType() { builtintypes(underlyingElement(this), _, 24, _, _, _) } + + override string getAPrimaryQlClass() { result = "FloatType" } +} + +/** + * The C/C++ `double` type. + * ``` + * double d; + * ``` + */ +class DoubleType extends RealNumberType, BinaryFloatingPointType { + DoubleType() { builtintypes(underlyingElement(this), _, 25, _, _, _) } + + override string getAPrimaryQlClass() { result = "DoubleType" } +} + +/** + * The C/C++ `long double` type. + * ``` + * long double ld; + * ``` + */ +class LongDoubleType extends RealNumberType, BinaryFloatingPointType { + LongDoubleType() { builtintypes(underlyingElement(this), _, 26, _, _, _) } + + override string getAPrimaryQlClass() { result = "LongDoubleType" } +} + +/** + * The GNU C `__float128` primitive type. This is not standard C/C++. + * ``` + * __float128 f128; + * ``` + */ +class Float128Type extends RealNumberType, BinaryFloatingPointType { + Float128Type() { builtintypes(underlyingElement(this), _, 38, _, _, _) } + + override string getAPrimaryQlClass() { result = "Float128Type" } +} + +/** + * The GNU C `_Decimal32` primitive type. This is not standard C/C++. + * ``` + * _Decimal32 d32; + * ``` + */ +class Decimal32Type extends RealNumberType, DecimalFloatingPointType { + Decimal32Type() { builtintypes(underlyingElement(this), _, 40, _, _, _) } + + override string getAPrimaryQlClass() { result = "Decimal32Type" } +} + +/** + * The GNU C `_Decimal64` primitive type. This is not standard C/C++. + * ``` + * _Decimal64 d64; + * ``` + */ +class Decimal64Type extends RealNumberType, DecimalFloatingPointType { + Decimal64Type() { builtintypes(underlyingElement(this), _, 41, _, _, _) } + + override string getAPrimaryQlClass() { result = "Decimal64Type" } +} + +/** + * The GNU C `_Decimal128` primitive type. This is not standard C/C++. + * ``` + * _Decimal128 d128; + * ``` + */ +class Decimal128Type extends RealNumberType, DecimalFloatingPointType { + Decimal128Type() { builtintypes(underlyingElement(this), _, 42, _, _, _) } + + override string getAPrimaryQlClass() { result = "Decimal128Type" } +} + +/** + * The C/C++ `void` type. See 4.7. + * ``` + * void foo(); + * ``` + */ +class VoidType extends BuiltInType { + VoidType() { builtintypes(underlyingElement(this), _, 3, _, _, _) } + + override string getAPrimaryQlClass() { result = "VoidType" } +} + +/** + * The C/C++ wide character type. + * + * Note that on some platforms `wchar_t` doesn't exist as a built-in + * type but a typedef is provided. Consider using the `Wchar_t` QL + * class to include these types. + * ``` + * wchar_t wc; + * ``` + */ +class WideCharType extends IntegralType { + WideCharType() { builtintypes(underlyingElement(this), _, 33, _, _, _) } + + override string getAPrimaryQlClass() { result = "WideCharType" } +} + +/** + * The C/C++ `char8_t` type. This is available starting with C++20. + * ``` + * char8_t c8; + * ``` + */ +class Char8Type extends IntegralType { + Char8Type() { builtintypes(underlyingElement(this), _, 51, _, _, _) } + + override string getAPrimaryQlClass() { result = "Char8Type" } +} + +/** + * The C/C++ `char16_t` type. This is available starting with C11 and C++11. + * ``` + * char16_t c16; + * ``` + */ +class Char16Type extends IntegralType { + Char16Type() { builtintypes(underlyingElement(this), _, 43, _, _, _) } + + override string getAPrimaryQlClass() { result = "Char16Type" } +} + +/** + * The C/C++ `char32_t` type. This is available starting with C11 and C++11. + * ``` + * char32_t c32; + * ``` + */ +class Char32Type extends IntegralType { + Char32Type() { builtintypes(underlyingElement(this), _, 44, _, _, _) } + + override string getAPrimaryQlClass() { result = "Char32Type" } +} + +/** + * The (primitive) type of the C++11 `nullptr` constant. It is a + * distinct type, denoted by `decltype(nullptr)`, that is not itself a pointer + * type or a pointer to member type. The `` header usually defines + * the `std::nullptr_t` type as follows: + * ``` + * typedef decltype(nullptr) nullptr_t; + * ``` + */ +class NullPointerType extends BuiltInType { + NullPointerType() { builtintypes(underlyingElement(this), _, 34, _, _, _) } + + override string getAPrimaryQlClass() { result = "NullPointerType" } +} + +/** + * A C/C++ derived type. + * + * These are pointer and reference types, array and GNU vector types, and `const` and `volatile` types. + * In all cases, the type is formed from a single base type. For example: + * ``` + * int *pi; + * int &ri = *pi; + * const float fa[40]; + * ``` + */ +class DerivedType extends Type, @derivedtype { + override string toString() { result = this.getName() } + + override string getName() { derivedtypes(underlyingElement(this), result, _, _) } + + /** + * Gets the base type of this derived type. + * + * This predicate strips off one level of decoration from a type. For example, it returns `char*` for the PointerType `char**`, + * `const int` for the ReferenceType `const int&`, and `long` for the SpecifiedType `volatile long`. + */ + Type getBaseType() { derivedtypes(underlyingElement(this), _, _, unresolveElement(result)) } + + override predicate refersToDirectly(Type t) { t = this.getBaseType() } + + override predicate involvesReference() { getBaseType().involvesReference() } + + override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + + override Type stripType() { result = getBaseType().stripType() } + + /** + * Holds if this type has the `__autoreleasing` specifier or if it points to + * a type with the `__autoreleasing` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isAutoReleasing() { + this.hasSpecifier("__autoreleasing") or + this.(PointerType).getBaseType().hasSpecifier("__autoreleasing") + } + + /** + * Holds if this type has the `__strong` specifier or if it points to + * a type with the `__strong` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isStrong() { + this.hasSpecifier("__strong") or + this.(PointerType).getBaseType().hasSpecifier("__strong") + } + + /** + * Holds if this type has the `__unsafe_unretained` specifier or if it points + * to a type with the `__unsafe_unretained` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isUnsafeRetained() { + this.hasSpecifier("__unsafe_unretained") or + this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained") + } + + /** + * Holds if this type has the `__weak` specifier or if it points to + * a type with the `__weak` specifier. + * + * DEPRECATED: use `hasSpecifier` directly instead. + */ + deprecated predicate isWeak() { + this.hasSpecifier("__weak") or + this.(PointerType).getBaseType().hasSpecifier("__weak") + } +} + +/** + * An instance of the C++11 `decltype` operator. For example: + * ``` + * int a; + * decltype(a) b; + * ``` + */ +class Decltype extends Type, @decltype { + override string getAPrimaryQlClass() { result = "Decltype" } + + /** + * The expression whose type is being obtained by this decltype. + */ + Expr getExpr() { decltypes(underlyingElement(this), unresolveElement(result), _, _) } + + /** + * The type immediately yielded by this decltype. + */ + Type getBaseType() { decltypes(underlyingElement(this), _, unresolveElement(result), _) } + + /** + * Whether an extra pair of parentheses around the expression would change the semantics of this decltype. + * + * The following example shows the effect of an extra pair of parentheses: + * ``` + * struct A { double x; }; + * const A* a = new A(); + * decltype( a->x ); // type is double + * decltype((a->x)); // type is const double& + * ``` + * Please consult the C++11 standard for more details. + */ + predicate parenthesesWouldChangeMeaning() { decltypes(underlyingElement(this), _, _, true) } + + override Type getUnderlyingType() { result = getBaseType().getUnderlyingType() } + + override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() } + + override Type stripType() { result = getBaseType().stripType() } + + override Type resolveTypedefs() { result = getBaseType().resolveTypedefs() } + + override Location getLocation() { result = getExpr().getLocation() } + + override string toString() { result = "decltype(...)" } + + override string getName() { none() } + + override int getSize() { result = getBaseType().getSize() } + + override int getAlignment() { result = getBaseType().getAlignment() } + + override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + + override string explain() { + result = "decltype resulting in {" + this.getBaseType().explain() + "}" + } + + override predicate involvesReference() { getBaseType().involvesReference() } + + override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } + + override Specifier internal_getAnAdditionalSpecifier() { + result = this.getBaseType().getASpecifier() + } +} + +/** + * A C/C++ pointer type. See 4.9.1. + * ``` + * void *ptr; + * void **ptr2 = &ptr; + * ``` + */ +class PointerType extends DerivedType { + PointerType() { derivedtypes(underlyingElement(this), _, 1, _) } + + override string getAPrimaryQlClass() { result = "PointerType" } + + override int getPointerIndirectionLevel() { + result = 1 + this.getBaseType().getPointerIndirectionLevel() + } + + override string explain() { result = "pointer to {" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } + + override Type resolveTypedefs() { + result.(PointerType).getBaseType() = getBaseType().resolveTypedefs() + } +} + +/** + * A C++ reference type. See 4.9.1. + * + * For C++11 code bases, this includes both _lvalue_ references (`&`) and _rvalue_ references (`&&`). + * To distinguish between them, use the LValueReferenceType and RValueReferenceType QL classes. + */ +class ReferenceType extends DerivedType { + ReferenceType() { + derivedtypes(underlyingElement(this), _, 2, _) or derivedtypes(underlyingElement(this), _, 8, _) + } + + override string getAPrimaryQlClass() { result = "ReferenceType" } + + override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + + override string explain() { result = "reference to {" + this.getBaseType().explain() + "}" } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // No such thing as a const reference type + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } + + override predicate involvesReference() { any() } + + override Type resolveTypedefs() { + result.(ReferenceType).getBaseType() = getBaseType().resolveTypedefs() + } +} + +/** + * A C++11 lvalue reference type (e.g. `int &`). + * ``` + * int a; + * int& b = a; + * ``` + */ +class LValueReferenceType extends ReferenceType { + LValueReferenceType() { derivedtypes(underlyingElement(this), _, 2, _) } + + override string getAPrimaryQlClass() { result = "LValueReferenceType" } +} + +/** + * A C++11 rvalue reference type (e.g., `int &&`). It is used to + * implement "move" semantics for object construction and assignment. + * ``` + * class C { + * E e; + * C(C&& from): e(std::move(from.e)) { } + * }; + * ``` + */ +class RValueReferenceType extends ReferenceType { + RValueReferenceType() { derivedtypes(underlyingElement(this), _, 8, _) } + + override string getAPrimaryQlClass() { result = "RValueReferenceType" } + + override string explain() { result = "rvalue " + super.explain() } +} + +/** + * A type with specifiers. + * ``` + * const int a; + * volatile char v; + * ``` + */ +class SpecifiedType extends DerivedType { + SpecifiedType() { derivedtypes(underlyingElement(this), _, 3, _) } + + override string getAPrimaryQlClass() { result = "SpecifiedType" } + + override int getSize() { result = this.getBaseType().getSize() } + + override int getAlignment() { result = this.getBaseType().getAlignment() } + + override int getPointerIndirectionLevel() { + result = this.getBaseType().getPointerIndirectionLevel() + } + + /** + * INTERNAL: Do not use. + * + * Gets all the specifiers of this type as a string in a fixed order (the order + * only depends on the specifiers, not on the source program). This is intended + * for debugging queries only and is an expensive operation. + */ + string getSpecifierString() { result = concat(this.getASpecifier().getName(), " ") } + + override string explain() { + result = this.getSpecifierString() + " {" + this.getBaseType().explain() + "}" + } + + override predicate isDeeplyConst() { + this.getASpecifier().getName() = "const" and this.getBaseType().isDeeplyConstBelow() + } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConstBelow() } + + override Specifier internal_getAnAdditionalSpecifier() { + result = this.getBaseType().getASpecifier() + } + + override Type resolveTypedefs() { + result.(SpecifiedType).getBaseType() = getBaseType().resolveTypedefs() and + result.getASpecifier() = getASpecifier() + } + + override Type stripTopLevelSpecifiers() { result = getBaseType().stripTopLevelSpecifiers() } +} + +/** + * A C/C++ array type. See 4.9.1. + * ``` + * char table[32]; + * ``` + */ +class ArrayType extends DerivedType { + ArrayType() { derivedtypes(underlyingElement(this), _, 4, _) } + + override string getAPrimaryQlClass() { result = "ArrayType" } + + /** + * Holds if this array is declared to be of a constant size. See + * `getArraySize` and `getByteSize` to get the size of the array. + */ + predicate hasArraySize() { arraysizes(underlyingElement(this), _, _, _) } + + /** + * Gets the number of elements in this array. Only has a result for arrays declared to be of a + * constant size. See `getByteSize` for getting the number of bytes. + */ + int getArraySize() { arraysizes(underlyingElement(this), result, _, _) } + + /** + * Gets the byte size of this array. Only has a result for arrays declared to be of a constant + * size. See `getArraySize` for getting the number of elements. + */ + int getByteSize() { arraysizes(underlyingElement(this), _, result, _) } + + override int getAlignment() { arraysizes(underlyingElement(this), _, _, result) } + + /** + * Gets the byte size of this array. Only has a result for arrays declared to be of a constant + * size. This predicate is a synonym for `getByteSize`. See `getArraySize` for getting the number + * of elements. + */ + override int getSize() { result = this.getByteSize() } + + override string explain() { + if exists(this.getArraySize()) + then + result = + "array of " + this.getArraySize().toString() + " {" + this.getBaseType().explain() + "}" + else result = "array of {" + this.getBaseType().explain() + "}" + } + + override predicate isDeeplyConst() { this.getBaseType().isDeeplyConst() } // No such thing as a const array type + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A GNU/Clang vector type. + * + * In both Clang and GNU compilers, vector types can be introduced using the + * `__attribute__((vector_size(byte_size)))` syntax. The Clang compiler also + * allows vector types to be introduced using the `ext_vector_type`, + * `neon_vector_type`, and `neon_polyvector_type` attributes (all of which take + * an element count rather than a byte size). + * + * In the example below, both `v4si` and `float4` are GNU vector types: + * ``` + * typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) )); + * typedef float float4 __attribute__((ext_vector_type(4))); + * ``` + */ +class GNUVectorType extends DerivedType { + GNUVectorType() { derivedtypes(underlyingElement(this), _, 5, _) } + + /** + * Get the number of elements in this vector type. + * + * For vector types declared using Clang's ext_vector_type, neon_vector_type, + * or neon_polyvector_type attribute, this is the value which appears within + * the attribute. For vector types declared using the vector_size attribute, + * the number of elements is the value in the attribute divided by the size + * of a single element. + */ + int getNumElements() { arraysizes(underlyingElement(this), result, _, _) } + + override string getAPrimaryQlClass() { result = "GNUVectorType" } + + /** + * Gets the size, in bytes, of this vector type. + * + * For vector types declared using the vector_size attribute, this is the + * value which appears within the attribute. For vector types declared using + * Clang's ext_vector_type, neon_vector_type, or neon_polyvector_type + * attribute, the byte size is the value in the attribute multiplied by the + * byte size of a single element. + */ + override int getSize() { arraysizes(underlyingElement(this), _, result, _) } + + override int getAlignment() { arraysizes(underlyingElement(this), _, _, result) } + + override string explain() { + result = "GNU " + getNumElements() + " element vector of {" + this.getBaseType().explain() + "}" + } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A C/C++ pointer to a function. See 7.7. + * ``` + * int(* pointer)(const void *element1, const void *element2); + * ``` + */ +class FunctionPointerType extends FunctionPointerIshType { + FunctionPointerType() { derivedtypes(underlyingElement(this), _, 6, _) } + + override string getAPrimaryQlClass() { result = "FunctionPointerType" } + + override int getPointerIndirectionLevel() { result = 1 } + + override string explain() { + result = "pointer to {" + this.getBaseType().(RoutineType).explain() + "}" + } +} + +/** + * A C++ reference to a function. + * ``` + * int(& reference)(const void *element1, const void *element2); + * ``` + */ +class FunctionReferenceType extends FunctionPointerIshType { + FunctionReferenceType() { derivedtypes(underlyingElement(this), _, 7, _) } + + override string getAPrimaryQlClass() { result = "FunctionReferenceType" } + + override int getPointerIndirectionLevel() { result = getBaseType().getPointerIndirectionLevel() } + + override string explain() { + result = "reference to {" + this.getBaseType().(RoutineType).explain() + "}" + } +} + +/** + * A block type, for example, `int(^)(char, float)`. + * + * Block types (along with blocks themselves) are a language extension + * supported by Clang, and by Apple's branch of GCC. + * ``` + * int(^ block)(const char *element1, const char *element2) + * = ^int (const char *element1, const char *element2) { return element1 - element 2; } + * ``` + */ +class BlockType extends FunctionPointerIshType { + BlockType() { derivedtypes(underlyingElement(this), _, 10, _) } + + override int getPointerIndirectionLevel() { result = 0 } + + override string explain() { + result = "block of {" + this.getBaseType().(RoutineType).explain() + "}" + } +} + +/** + * A C/C++ pointer to a function, a C++ function reference, or a clang/Apple block. + * + * See `FunctionPointerType`, `FunctionReferenceType` and `BlockType` for more information. + */ +class FunctionPointerIshType extends DerivedType { + FunctionPointerIshType() { + derivedtypes(underlyingElement(this), _, 6, _) or + derivedtypes(underlyingElement(this), _, 7, _) or + derivedtypes(underlyingElement(this), _, 10, _) + } + + /** the return type of this function pointer type */ + Type getReturnType() { + exists(RoutineType t | + derivedtypes(underlyingElement(this), _, _, unresolveElement(t)) and + result = t.getReturnType() + ) + } + + /** the type of the ith argument of this function pointer type */ + Type getParameterType(int i) { + exists(RoutineType t | + derivedtypes(underlyingElement(this), _, _, unresolveElement(t)) and + result = t.getParameterType(i) + ) + } + + /** the type of an argument of this function pointer type */ + Type getAParameterType() { + exists(RoutineType t | + derivedtypes(underlyingElement(this), _, _, unresolveElement(t)) and + result = t.getAParameterType() + ) + } + + /** the number of arguments of this function pointer type */ + int getNumberOfParameters() { result = count(int i | exists(this.getParameterType(i))) } + + override predicate involvesTemplateParameter() { + getReturnType().involvesTemplateParameter() or + getAParameterType().involvesTemplateParameter() + } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A C++ pointer to data member. See 15.5. + * ``` + * class C { public: int m; }; + * int C::* p = &C::m; // pointer to data member m of class C + * class C c; + * int val = c.*p; // access data member + * ``` + */ +class PointerToMemberType extends Type, @ptrtomember { + /** a printable representation of this named element */ + override string toString() { result = this.getName() } + + override string getAPrimaryQlClass() { result = "PointerToMemberType" } + + /** the name of this type */ + override string getName() { result = "..:: *" } + + /** the base type of this pointer to member type */ + Type getBaseType() { ptrtomembers(underlyingElement(this), unresolveElement(result), _) } + + /** the class referred by this pointer to member type */ + Type getClass() { ptrtomembers(underlyingElement(this), _, unresolveElement(result)) } + + override predicate refersToDirectly(Type t) { + t = this.getBaseType() or + t = this.getClass() + } + + override int getPointerIndirectionLevel() { + result = 1 + this.getBaseType().getPointerIndirectionLevel() + } + + override string explain() { + result = + "pointer to member of " + this.getClass().toString() + " with type {" + + this.getBaseType().explain() + "}" + } + + override predicate involvesTemplateParameter() { getBaseType().involvesTemplateParameter() } + + override predicate isDeeplyConstBelow() { this.getBaseType().isDeeplyConst() } +} + +/** + * A C/C++ routine type. Conceptually, this is what results from stripping + * away the pointer from a function pointer type. It can also occur in C++ + * code, for example the base type of `myRoutineType` in the following code: + * ``` + * using myRoutineType = int(int); + * + * myRoutineType *fp = 0; + * ``` + */ +class RoutineType extends Type, @routinetype { + /** a printable representation of this named element */ + override string toString() { result = this.getName() } + + override string getAPrimaryQlClass() { result = "RoutineType" } + + override string getName() { result = "..()(..)" } + + /** + * Gets the type of the `n`th parameter to this routine. + */ + Type getParameterType(int n) { + routinetypeargs(underlyingElement(this), n, unresolveElement(result)) + } + + /** + * Gets the type of a parameter to this routine. + */ + Type getAParameterType() { routinetypeargs(underlyingElement(this), _, unresolveElement(result)) } + + /** + * Gets the return type of this routine. + */ + Type getReturnType() { routinetypes(underlyingElement(this), unresolveElement(result)) } + + /** + * Holds if this function type has "C" language linkage. + * + * This includes any function declared in a C source file, or explicitly marked as having "C" linkage: + * ``` + * extern "C" void f(); + * extern "C" { + * void g(); + * } + * ``` + */ + predicate hasCLinkage() { this.hasSpecifier("c_linkage") } + + override string explain() { + result = + "function returning {" + this.getReturnType().explain() + "} with arguments (" + + this.explainParameters(0) + ")" + } + + /** + * Gets a string with the `explain()` values for the parameters of + * this function, for instance "int,int". + * + * The integer argument is the index of the first parameter to explain. + */ + private string explainParameters(int i) { + i = 0 and result = "" and not exists(this.getAParameterType()) + or + ( + exists(this.getParameterType(i)) and + if i < max(int j | exists(this.getParameterType(j))) + then + // Not the last one + result = this.getParameterType(i).explain() + "," + this.explainParameters(i + 1) + else + // Last parameter + result = this.getParameterType(i).explain() + ) + } + + override predicate refersToDirectly(Type t) { + t = this.getReturnType() or + t = this.getAParameterType() + } + + override predicate isDeeplyConst() { none() } // Current limitation: no such thing as a const routine type + + override predicate isDeeplyConstBelow() { none() } // Current limitation: no such thing as a const routine type + + override predicate involvesTemplateParameter() { + getReturnType().involvesTemplateParameter() or + getAParameterType().involvesTemplateParameter() + } +} + +/** + * A C++ `typename` (or `class`) template parameter. + * + * In the example below, `T` is a template parameter: + * ``` + * template + * class C { }; + * ``` + */ +class TemplateParameter extends UserType { + TemplateParameter() { + usertypes(underlyingElement(this), _, 7) or usertypes(underlyingElement(this), _, 8) + } + + override string getAPrimaryQlClass() { result = "TemplateParameter" } + + override predicate involvesTemplateParameter() { any() } +} + +/** + * A C++ template template parameter. + * + * In the example below, `T` is a template template parameter (although its name + * may be omitted): + * ``` + * template