diff --git a/ql/src/codeql_ql/ast/Ast.qll b/ql/src/codeql_ql/ast/Ast.qll index 7921c6c441b..73622380526 100644 --- a/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/src/codeql_ql/ast/Ast.qll @@ -283,6 +283,8 @@ class CharPred extends TCharPred, Predicate { or pred_name = directMember("getBody") and result = this.getBody() } + + ClassType getDeclaringType() { result.getDeclaration() = getParent() } } /** @@ -600,7 +602,7 @@ class Call extends TCall, Expr { none() // overriden in sublcasses. } - Predicate getTarget() { resolveCall(this, result) } + PredicateOrBuiltin getTarget() { resolveCall(this, result) } override Type getType() { result = this.getTarget().getReturnType() } diff --git a/ql/src/codeql_ql/ast/internal/Builtins.qll b/ql/src/codeql_ql/ast/internal/Builtins.qll new file mode 100644 index 00000000000..1f9638ed63c --- /dev/null +++ b/ql/src/codeql_ql/ast/internal/Builtins.qll @@ -0,0 +1,60 @@ +predicate isBuiltinClassless(string sig) { + sig = + [ + "predicate any()", "predicate none()", "predicate toUrl(string, int, int, int, string)", + "predicate toUrl(string, int, int, int, int, string)" + ] +} + +predicate isBuiltinClassless(string ret, string name, string args) { + exists(string sig, string re | re = "(\\w+) (\\w+)\\(([\\w, ]*)\\)" | + isBuiltinClassless(sig) and + ret = sig.regexpCapture(re, 1) and + name = sig.regexpCapture(re, 2) and + args = sig.regexpCapture(re, 3) + ) +} + +predicate isBuiltinMember(string sig) { + sig = + [ + "boolean boolean.booleanAnd(boolean)", "boolean boolean.booleanOr(boolean)", + "boolean boolean.booleanXor(boolean)", "boolean boolean.booleanNot()", + "string boolean.toString()", "float float.abs()", "float float.acos()", "float float.atan()", + "int float.ceil()", "float float.copySign(float)", "float float.cos()", "float float.cosh()", + "float float.exp()", "int float.floor()", "float float.log()", "float float.log(float)", + "float float.log2()", "float float.log10()", "float float.maximum(float)", + "float float.minimum(float)", "float float.nextAfter(float)", "float float.nextDown()", + "float float.nextUp()", "float float.pow(float)", "float float.signum()", "float float.sin()", + "float float.sinh()", "float float.sqrt()", "float float.tan()", "float float.tanh()", + "string float.toString()", "float float.ulp()", "int int.abs()", "int int.bitAnd(int)", + "int int.bitOr(int)", "int int.bitNot()", "int int.bitXor(int)", "int int.bitShiftLeft(int)", + "int int.bitShiftRight(int)", "int int.bitShiftRightSigned(int)", "int int.gcd(int)", + "string int.toString()", "string string.charAt(int)", "int string.indexOf(string)", + "int string.indexOf(string, int, int)", "predicate string.isLowercase()", + "predicate string.isUppercase()", "int string.length()", "predicate string.matches(string)", + "string string.prefix(int)", "string regexpCapture(string, int)", + "string regexpFind(string, int, int)", "predicate string.regexpMatch(string)", + "string string.regexpReplaceAll(string, string)", "string string.replaceAll(string, string)", + "string string.splitAt(string)", "string string.splitAt(string, int)", + "string string.substring(int, int)", "string string.suffix(int)", "date string.toDate()", + "float string.toFloat()", "int string.toInt()", "string string.toString()", + "string string.toLowerCase()", "string string.toUpperCase()", "string string.trim()", + "int date.daysTo(date)", "int date.getDay()", "int date.getHours()", "int date.getMinutes()", + "int date.getMonth()", "int date.getSeconds()", "int date.getYear()", + "string date.toString()", "string date.toISO()" + ] +} + +predicate isBuiltinMember(string qual, string ret, string name, string args) { + exists(string sig, string re | re = "(\\w+) (\\w+)\\.(\\w+)\\(([\\w, ]*)\\)" | + isBuiltinMember(sig) and + ret = sig.regexpCapture(re, 1) and + qual = sig.regexpCapture(re, 2) and + name = sig.regexpCapture(re, 3) and + args = sig.regexpCapture(re, 4) + ) +} + +bindingset[args] +string getArgType(string args, int i) { result = args.splitAt(",", i).trim() } diff --git a/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/src/codeql_ql/ast/internal/Predicate.qll index 2c71e09f46e..bdce9a97cda 100644 --- a/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -1,4 +1,5 @@ import ql +private import Builtins private import codeql_ql.ast.internal.Module private predicate definesPredicate(FileOrModule m, string name, ClasslessPredicate p, boolean public) { @@ -38,7 +39,7 @@ predicate resolvePredicateExpr(PredicateExpr pe, ClasslessPredicate p) { ) } -private predicate resolvePredicateCall(PredicateCall pc, Predicate p) { +private predicate resolvePredicateCall(PredicateCall pc, PredicateOrBuiltin p) { exists(Class c, ClassType t | c = pc.getParent*() and t = c.getType() and @@ -53,24 +54,125 @@ private predicate resolvePredicateCall(PredicateCall pc, Predicate p) { m = pc.getQualifier().getResolvedModule() and public = true | - definesPredicate(m, pc.getPredicateName(), p, public) and - count(p.getParameter(_)) = pc.getNumberOfArguments() + definesPredicate(m, pc.getPredicateName(), p.getDeclaration(), public) and + p.getArity() = pc.getNumberOfArguments() ) } -private predicate resolveMemberCall(MemberCall mc, Predicate p) { - exists(ClassType t | +private predicate resolveMemberCall(MemberCall mc, PredicateOrBuiltin p) { + exists(Type t | t = mc.getBase().getType() and p = t.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) ) } -predicate resolveCall(Call c, Predicate p) { +predicate resolveCall(Call c, PredicateOrBuiltin p) { resolvePredicateCall(c, p) or resolveMemberCall(c, p) } +private newtype TPredOrBuiltin = + TPred(Predicate p) or + TBuiltinClassless(string ret, string name, string args) { isBuiltinClassless(ret, name, args) } or + TBuiltinMember(string qual, string ret, string name, string args) { + isBuiltinMember(qual, ret, name, args) + } + +class PredicateOrBuiltin extends TPredOrBuiltin { + string getName() { none() } + + string toString() { result = getName() } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + if exists(getDeclaration()) + then + getDeclaration() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + else ( + filepath = "" and + startline = 0 and + startcolumn = 0 and + endline = 0 and + endcolumn = 0 + ) + } + + Predicate getDeclaration() { none() } + + Type getDeclaringType() { none() } + + Type getParameterType(int i) { none() } + + Type getReturnType() { none() } + + int getArity() { result = count(getParameterType(_)) } + + predicate isPrivate() { none() } +} + +private class DefinedPredicate extends PredicateOrBuiltin, TPred { + Predicate decl; + + DefinedPredicate() { this = TPred(decl) } + + override Predicate getDeclaration() { result = decl } + + override string getName() { result = decl.getName() } + + override Type getReturnType() { result = decl.getReturnType() } + + override Type getParameterType(int i) { result = decl.getParameter(i).getType() } + + override Type getDeclaringType() { + result = decl.(ClassPredicate).getDeclaringType() + or + result = decl.(CharPred).getDeclaringType() + } + + override predicate isPrivate() { + decl.(ClassPredicate).isPrivate() or decl.(ClassPredicate).isPrivate() + } +} + +private class TBuiltin = TBuiltinClassless or TBuiltinMember; + +class BuiltinPredicate extends PredicateOrBuiltin, TBuiltin { } + +private class BuiltinClassless extends BuiltinPredicate, TBuiltinClassless { + string name; + string ret; + string args; + + BuiltinClassless() { this = TBuiltinClassless(ret, name, args) } + + override string getName() { result = name } + + override PrimitiveType getReturnType() { result.getName() = ret } + + override PrimitiveType getParameterType(int i) { result.getName() = getArgType(args, i) } +} + +private class BuiltinMember extends BuiltinPredicate, TBuiltinMember { + string name; + string qual; + string ret; + string args; + + BuiltinMember() { this = TBuiltinMember(qual, ret, name, args) } + + override string getName() { result = name } + + override PrimitiveType getReturnType() { result.getName() = ret } + + override PrimitiveType getParameterType(int i) { result.getName() = getArgType(args, i) } + + override PrimitiveType getDeclaringType() { result.getName() = qual } +} + module PredConsistency { query predicate noResolvePredicateExpr(PredicateExpr pe) { not resolvePredicateExpr(pe, _) and @@ -88,8 +190,8 @@ module PredConsistency { resolvePredicateExpr(pe, p) } - query predicate multipleResolveCall(Call call, int c, Predicate p) { - c = strictcount(Predicate p0 | resolveCall(call, p0)) and + query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { + c = strictcount(PredicateOrBuiltin p0 | resolveCall(call, p0)) and c > 1 and resolveCall(call, p) } diff --git a/ql/src/codeql_ql/ast/internal/Type.qll b/ql/src/codeql_ql/ast/internal/Type.qll index 33538a91a80..63a837545cd 100644 --- a/ql/src/codeql_ql/ast/internal/Type.qll +++ b/ql/src/codeql_ql/ast/internal/Type.qll @@ -2,6 +2,7 @@ import ql private import codeql_ql.ast.internal.AstNodes as AstNodes private import codeql_ql.ast.internal.TreeSitter private import codeql_ql.ast.internal.Module +private import codeql_ql.ast.internal.Predicate private newtype TType = TClass(Class c) { isActualClass(c) } or @@ -59,6 +60,13 @@ class Type extends TType { endcolumn = 0 ) } + + PredicateOrBuiltin getClassPredicate(string name, int arity) { + result = classPredCandidate(this, name, arity) and + not exists(PredicateOrBuiltin other | other = classPredCandidate(this, name, arity) | + other.getDeclaringType().getASuperType+() = result.getDeclaringType() + ) + } } class ClassType extends Type, TClass { @@ -78,13 +86,6 @@ class ClassType extends Type, TClass { result = super.getAnInternalSuperType() } - ClassPredicate getClassPredicate(string name, int arity) { - result = classPredCandidate(this, name, arity) and - not exists(ClassPredicate other | other = classPredCandidate(this, name, arity) | - other.getDeclaringType().getASuperType+() = result.getDeclaringType() - ) - } - VarDecl getField(string name) { result = fieldCandidate(this, name) and not exists(VarDecl other | other = fieldCandidate(this, name) | @@ -93,26 +94,27 @@ class ClassType extends Type, TClass { } } -private ClassPredicate declaredPred(ClassType ty, string name, int arity) { - result = ty.getDeclaration().getAClassPredicate() and +private PredicateOrBuiltin declaredPred(Type ty, string name, int arity) { + result.getDeclaringType() = ty and result.getName() = name and result.getArity() = arity } -private ClassPredicate classPredCandidate(ClassType ty, string name, int arity) { +private PredicateOrBuiltin classPredCandidate(Type ty, string name, int arity) { result = declaredPred(ty, name, arity) or not exists(declaredPred(ty, name, arity)) and result = inherClassPredCandidate(ty, name, arity) } -private ClassPredicate inherClassPredCandidate(ClassType ty, string name, int arity) { - result = classPredCandidate(ty.getASuperType(), name, arity) and +private PredicateOrBuiltin inherClassPredCandidate(Type ty, string name, int arity) { + result = classPredCandidate(ty.getAnInternalSuperType(), name, arity) and not result.isPrivate() } predicate predOverrides(ClassPredicate sub, ClassPredicate sup) { - sup = inherClassPredCandidate(sub.getDeclaringType(), sub.getName(), sub.getArity()) + sup = + inherClassPredCandidate(sub.getDeclaringType(), sub.getName(), sub.getArity()).getDeclaration() } private VarDecl declaredField(ClassType ty, string name) { @@ -322,7 +324,7 @@ module TyConsistency { query predicate exprNoType(Expr e) { not exists(e.getType()) and - not exists(Predicate p | + not exists(PredicateOrBuiltin p | p = e.(Call).getTarget() and not exists(p.getReturnType()) ) and diff --git a/ql/src/ide-contextual-queries/Definitions.qll b/ql/src/ide-contextual-queries/Definitions.qll index 2864ae5fca6..deb977fe672 100644 --- a/ql/src/ide-contextual-queries/Definitions.qll +++ b/ql/src/ide-contextual-queries/Definitions.qll @@ -49,7 +49,7 @@ private predicate resolveVar(VarAccess va, VarDecl decl, string kind) { } private predicate resolveCall(Call c, Predicate p, string kind) { - p = c.getTarget() and + p = c.getTarget().getDeclaration() and kind = "call" }