Take the following two examples:
static File safe11() throws IOException {
File temp = null;
if (temp == null) {
while (true) {
temp = File.createTempFile("test", "directory");
if (temp.delete() && temp.mkdir()) {
break;
}
}
}
return temp;
}
File safe12temp;
File safe12() throws IOException {
if (safe12temp == null) {
while (true) {
safe12temp = File.createTempFile("test", "directory");
if (safe12temp.delete() && safe12temp.mkdir()) {
break;
}
}
}
return safe12temp;
}
Let's take File.createTempFile as a source of taint, and _.mkdir() is a guard.
/**
* All methods on the class `java.io.File` that create directories.
*/
class MethodFileCreatesDirs extends Method {
MethodFileCreatesDirs() {
getDeclaringType() instanceof TypeFile and
hasName(["mkdir", "mkdirs"])
}
}
/**
* An expression that will create a directory without throwing an exception if a file/directory already exists.
*/
private predicate isNonThrowingDirectoryCreationExpression(Expr expr, MethodAccess creationCall) {
creationCall.getMethod() instanceof MethodFileCreatesDirs and creationCall.getQualifier() = expr
}
private class DirectoryCreationBarrierGuard extends DataFlow::BarrierGuard {
DirectoryCreationBarrierGuard() { isNonThrowingDirectoryCreationExpression(_, this) }
override predicate checks(Expr e, boolean branch) {
this.controls(e, branch)
}
}
Running a query for DirectoryCreationBarrierGuard will find the safe12temp.mkdir() and temp.mkdir().
However, when quick evaluating the DirectoryCreationBarrierGuard::checks method, it will not find that safe12temp.mkdir() is a check for return safe12temp and temp.mkdir() is a check for return safe12temp;.
Interesting note: as soon as the outer null check is removed, codeQL does correctly identify the guard controls the return. For example, this is detected as correctly guarded:
File safe12temp;
File safe12() throws IOException {
while (true) {
safe12temp = File.createTempFile("test", "directory");
if (safe12temp.delete() && safe12temp.mkdir()) {
break;
}
}
return safe12temp;
}
Practical example:
https://github.com/apache/hive/blob/cba74fa91d4035aee2c39dcf929a0ae480609540/ql/src/java/org/apache/hadoop/hive/ql/exec/spark/HiveKVResultCache.java#L89-L103
The above code is flagged as a false positive result when executing this new query: #4473
Take the following two examples:
Let's take
File.createTempFileas a source of taint, and_.mkdir()is a guard.Running a query for
DirectoryCreationBarrierGuardwill find thesafe12temp.mkdir()andtemp.mkdir().However, when quick evaluating the
DirectoryCreationBarrierGuard::checksmethod, it will not find thatsafe12temp.mkdir()is a check forreturn safe12tempandtemp.mkdir()is a check forreturn safe12temp;.Interesting note: as soon as the outer
nullcheck is removed, codeQL does correctly identify the guard controls the return. For example, this is detected as correctly guarded:Practical example:
https://github.com/apache/hive/blob/cba74fa91d4035aee2c39dcf929a0ae480609540/ql/src/java/org/apache/hadoop/hive/ql/exec/spark/HiveKVResultCache.java#L89-L103
The above code is flagged as a false positive result when executing this new query: #4473