Files
codeql/cpp/ql/src/Best Practices/Magic Constants/MagicConstants.qll
2019-04-12 10:43:24 +02:00

445 lines
11 KiB
Plaintext

import cpp
import semmle.code.cpp.AutogeneratedFile
/*
* Counting nontrivial literal occurrences
*/
predicate trivialPositiveIntValue(string s) {
// Small numbers
s = [0 .. 20].toString() or
// Popular powers of two (decimal)
s = "16" or
s = "24" or
s = "32" or
s = "64" or
s = "128" or
s = "256" or
s = "512" or
s = "1024" or
s = "2048" or
s = "4096" or
s = "16384" or
s = "32768" or
s = "65536" or
s = "1048576" or
s = "2147483648" or
s = "4294967296" or
// Popular powers of two, minus one (decimal)
s = "15" or
s = "31" or
s = "63" or
s = "127" or
s = "255" or
s = "511" or
s = "1023" or
s = "2047" or
s = "4095" or
s = "16383" or
s = "32767" or
s = "65535" or
s = "1048577" or
s = "2147483647" or
s = "4294967295" or
// Popular powers of two (32-bit hex)
s = "0x00000001" or
s = "0x00000002" or
s = "0x00000004" or
s = "0x00000008" or
s = "0x00000010" or
s = "0x00000020" or
s = "0x00000040" or
s = "0x00000080" or
s = "0x00000100" or
s = "0x00000200" or
s = "0x00000400" or
s = "0x00000800" or
s = "0x00001000" or
s = "0x00002000" or
s = "0x00004000" or
s = "0x00008000" or
s = "0x00010000" or
s = "0x00020000" or
s = "0x00040000" or
s = "0x00080000" or
s = "0x00100000" or
s = "0x00200000" or
s = "0x00400000" or
s = "0x00800000" or
s = "0x01000000" or
s = "0x02000000" or
s = "0x04000000" or
s = "0x08000000" or
s = "0x10000000" or
s = "0x20000000" or
s = "0x40000000" or
s = "0x80000000" or
// Popular powers of two, minus one (32-bit hex)
s = "0x00000001" or
s = "0x00000003" or
s = "0x00000007" or
s = "0x0000000f" or
s = "0x0000001f" or
s = "0x0000003f" or
s = "0x0000007f" or
s = "0x000000ff" or
s = "0x000001ff" or
s = "0x000003ff" or
s = "0x000007ff" or
s = "0x00000fff" or
s = "0x00001fff" or
s = "0x00003fff" or
s = "0x00007fff" or
s = "0x0000ffff" or
s = "0x0001ffff" or
s = "0x0003ffff" or
s = "0x0007ffff" or
s = "0x000fffff" or
s = "0x001fffff" or
s = "0x003fffff" or
s = "0x007fffff" or
s = "0x00ffffff" or
s = "0x01ffffff" or
s = "0x03ffffff" or
s = "0x07ffffff" or
s = "0x0fffffff" or
s = "0x1fffffff" or
s = "0x3fffffff" or
s = "0x7fffffff" or
s = "0xffffffff" or
// Popular powers of two (16-bit hex)
s = "0x0001" or
s = "0x0002" or
s = "0x0004" or
s = "0x0008" or
s = "0x0010" or
s = "0x0020" or
s = "0x0040" or
s = "0x0080" or
s = "0x0100" or
s = "0x0200" or
s = "0x0400" or
s = "0x0800" or
s = "0x1000" or
s = "0x2000" or
s = "0x4000" or
s = "0x8000" or
// Popular powers of two, minus one (16-bit hex)
s = "0x0001" or
s = "0x0003" or
s = "0x0007" or
s = "0x000f" or
s = "0x001f" or
s = "0x003f" or
s = "0x007f" or
s = "0x00ff" or
s = "0x01ff" or
s = "0x03ff" or
s = "0x07ff" or
s = "0x0fff" or
s = "0x1fff" or
s = "0x3fff" or
s = "0x7fff" or
s = "0xffff" or
// Popular powers of two (8-bit hex)
s = "0x01" or
s = "0x02" or
s = "0x04" or
s = "0x08" or
s = "0x10" or
s = "0x20" or
s = "0x40" or
s = "0x80" or
// Popular powers of two, minus one (8-bit hex)
s = "0x01" or
s = "0x03" or
s = "0x07" or
s = "0x0f" or
s = "0x1f" or
s = "0x3f" or
s = "0x7f" or
s = "0xff" or
s = "0x00" or
// Powers of ten
s = "10" or
s = "100" or
s = "1000" or
s = "10000" or
s = "100000" or
s = "1000000" or
s = "10000000" or
s = "100000000" or
s = "1000000000"
}
predicate trivialIntValue(string s) {
trivialPositiveIntValue(s)
or
exists(string pos | trivialPositiveIntValue(pos) and s = "-" + pos)
}
predicate trivialLongValue(string s) { exists(string v | trivialIntValue(v) and s = v + "L") }
predicate intTrivial(Literal lit) { exists(string v | trivialIntValue(v) and v = lit.getValue()) }
predicate longTrivial(Literal lit) { exists(string v | trivialLongValue(v) and v = lit.getValue()) }
predicate powerOfTen(float f) {
f = 10 or
f = 100 or
f = 1000 or
f = 10000 or
f = 100000 or
f = 1000000 or
f = 10000000 or
f = 100000000 or
f = 1000000000
}
predicate floatTrivial(Literal lit) {
lit.getType() instanceof FloatingPointType and
exists(string value, float f |
lit.getValue() = value and
f = value.toFloat() and
(f.abs() <= 20.0 or powerOfTen(f))
)
}
predicate charLiteral(Literal lit) { lit instanceof CharLiteral }
Type literalType(Literal literal) { result = literal.getType() }
predicate stringType(DerivedType t) {
t.getBaseType() instanceof CharType
or
exists(SpecifiedType constCharType |
t.getBaseType() = constCharType and
constCharType.isConst() and
constCharType.getBaseType() instanceof CharType
)
}
predicate numberType(Type t) { t instanceof FloatingPointType or t instanceof IntegralType }
predicate stringLiteral(Literal literal) { literal instanceof StringLiteral }
predicate stringTrivial(Literal lit) {
stringLiteral(lit) and
lit.getValue().length() < 8
}
predicate joiningStringTrivial(Literal lit) {
// We want to be more lenient with string literals that are being
// joined together, because replacing sentence fragments with named
// constants could actually result in code that is harder to
// understand (which is against the spirit of these queries).
stringLiteral(lit) and
exists(FunctionCall fc |
(
fc.getTarget().getName() = "operator+" or
fc.getTarget().getName() = "operator<<"
) and
fc.getAnArgument().getAChild*() = lit
) and
lit.getValue().length() < 16
}
predicate small(Literal lit) { lit.getValue().length() <= 1 }
predicate trivial(Literal lit) {
charLiteral(lit) or
intTrivial(lit) or
floatTrivial(lit) or
stringTrivial(lit) or
joiningStringTrivial(lit) or
longTrivial(lit) or
small(lit)
}
private predicate isReferenceTo(Variable ref, Variable to) {
exists(VariableAccess a |
ref.getInitializer().getExpr().getConversion().(ReferenceToExpr).getExpr() = a and
a.getTarget() = to
)
}
private predicate variableNotModifiedAfterInitializer(Variable v) {
not exists(VariableAccess a | a.getTarget() = v and a.isModified()) and
not exists(AddressOfExpr e | e.getAddressable() = v) and
forall(Variable v2 | isReferenceTo(v2, v) | variableNotModifiedAfterInitializer(v2))
}
predicate literalIsConstantInitializer(Literal literal, Variable f) {
f.getInitializer().getExpr() = literal and
variableNotModifiedAfterInitializer(f) and
not f instanceof Parameter
}
predicate literalIsEnumInitializer(Literal literal) {
exists(EnumConstant ec | ec.getInitializer().getExpr() = literal)
}
predicate literalInArrayInitializer(Literal literal) {
exists(AggregateLiteral arrayInit | arrayInitializerChild(arrayInit, literal))
}
predicate arrayInitializerChild(AggregateLiteral parent, Expr e) {
e = parent
or
exists(Expr mid | arrayInitializerChild(parent, mid) and e.getParent() = mid)
}
// i.e. not a constant folded expression
predicate literallyLiteral(Literal lit) {
lit
.getValueText()
.regexpMatch(".*\".*|\\s*+[-+]?+\\s*+(0[xob][0-9a-fA-F]|[0-9])[0-9a-fA-F,._]*+([eE][-+]?+[0-9,._]*+)?+\\s*+[a-zA-Z]*+\\s*+")
}
predicate nonTrivialValue(string value, Literal literal) {
value = literal.getValue() and
not trivial(literal) and
not literalIsConstantInitializer(literal, _) and
not literalIsEnumInitializer(literal) and
not literalInArrayInitializer(literal) and
not literal.isAffectedByMacro() and
literallyLiteral(literal)
}
predicate valueOccurrenceCount(string value, int n) {
n = strictcount(Location loc |
exists(Literal lit | lit.getLocation() = loc | nonTrivialValue(value, lit)) and
// Exclude generated files (they do not have the same maintainability
// concerns as ordinary source files)
not loc.getFile() instanceof AutogeneratedFile
) and
n > 20
}
predicate occurenceCount(Literal lit, string value, int n) {
valueOccurrenceCount(value, n) and
value = lit.getValue() and
nonTrivialValue(_, lit)
}
/*
* Literals repeated frequently
*/
predicate check(Literal lit, string value, int n, File f) {
// Check that the literal is nontrivial
not trivial(lit) and
// Check that it is repeated a number of times
occurenceCount(lit, value, n) and
n > 20 and
f = lit.getFile() and
// Exclude generated files
not f instanceof AutogeneratedFile
}
predicate checkWithFileCount(string value, int overallCount, int fileCount, File f) {
fileCount = strictcount(Location loc |
exists(Literal lit | lit.getLocation() = loc | check(lit, value, overallCount, f))
)
}
predicate start(Literal lit, int startLine) {
exists(Location l | l = lit.getLocation() and startLine = l.getStartLine())
}
predicate firstOccurrence(Literal lit, string value, int n) {
exists(File f, int fileCount |
checkWithFileCount(value, n, fileCount, f) and
fileCount < 100 and
check(lit, value, n, f) and
not exists(Literal lit2, int start1, int start2 |
check(lit2, value, n, f) and
start(lit, start1) and
start(lit2, start2) and
start2 < start1
)
)
}
predicate magicConstant(Literal e, string msg) {
exists(string value, int n |
firstOccurrence(e, value, n) and
msg = "Magic constant: literal '" + value + "' is repeated " + n.toString() +
" times and should be encapsulated in a constant."
)
}
/*
* Literals where there is a defined constant with the same value
*/
predicate relevantVariable(Variable f, string value) {
exists(Literal lit |
not trivial(lit) and value = lit.getValue() and literalIsConstantInitializer(lit, f)
)
}
predicate relevantCallable(Function f, string value) {
exists(Literal lit |
not trivial(lit) and value = lit.getValue() and lit.getEnclosingFunction() = f
)
}
predicate isVisible(Variable field, Function fromCallable) {
exists(string value |
//public fields
relevantVariable(field, value) and
field.(MemberVariable).isPublic() and
relevantCallable(fromCallable, value)
or
//in same class
relevantVariable(field, value) and
exists(Type t |
t = field.getDeclaringType() and
t = fromCallable.getDeclaringType()
) and
relevantCallable(fromCallable, value)
or
//in subclass and not private
relevantVariable(field, value) and
not field.(MemberVariable).isPrivate() and
exists(Class sup, Class sub |
sup = field.getDeclaringType() and
sub.getABaseClass+() = sup and
sub = fromCallable.getDeclaringType()
) and
relevantCallable(fromCallable, value)
)
}
predicate canUseFieldInsteadOfLiteral(Variable constField, Literal magicLiteral) {
exists(Literal initLiteral |
literalIsConstantInitializer(initLiteral, constField) and
not trivial(initLiteral) and
not constField.getType().hasName("boolean") and
exists(string value |
value = initLiteral.getValue() and
magicLiteral.getValue() = value
) and
constField.getType() = magicLiteral.getType() and
not literalIsConstantInitializer(magicLiteral, _) and
exists(Function c |
c = magicLiteral.getEnclosingFunction() and
(
constField.isTopLevel() and
(not constField.isStatic() or constField.getFile() = c.getFile())
or
isVisible(constField, c)
)
)
)
}
predicate literalInsteadOfConstant(
Literal magicLiteral, string message, Variable constField, string linkText
) {
canUseFieldInsteadOfLiteral(constField, magicLiteral) and
message = "Literal value '" + magicLiteral.getValue() + "' used instead of constant $@." and
linkText = constField.getName()
}