Merge pull request #568 from calumgrant/cs/index-out-of-bounds

C#: Fix false-positives in cs/index-out-of-bounds
This commit is contained in:
Tom Hvitved 2018-11-29 18:40:05 +01:00 коммит произвёл GitHub
Родитель 11ca7b74a3 6a1ab51d66
Коммит 8bd8975795
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 122 добавлений и 11 удалений

Просмотреть файл

@ -0,0 +1,19 @@
# Improvements to C# analysis
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| *@name of query (Query ID)*| *Impact on results* | *How/why the query has changed* |
| Off-by-one comparison against container length (cs/index-out-of-bounds) | Fewer false positives | Results have been removed when there are additional guards on the index. |
## Changes to code extraction
## Changes to QL libraries
## Changes to the autobuilder

Просмотреть файл

@ -14,16 +14,53 @@
import csharp
import semmle.code.csharp.controlflow.Guards
import semmle.code.csharp.commons.ComparisonTest
from RelationalOperation cmp, Variable array, Variable index, ElementAccess ea, VariableAccess indexAccess
// Look for `index <= array.Length` or `array.Length >= index`
where (cmp instanceof GEExpr or cmp instanceof LEExpr)
and cmp.getGreaterOperand() = any(PropertyAccess pa | pa.getQualifier() = array.getAnAccess() and pa.getTarget().hasName("Length"))
and cmp.getLesserOperand() = index.getAnAccess()
/** A comparison of an index variable with the length of an array. */
class IndexGuard extends ComparisonTest {
VariableAccess indexAccess;
Variable array;
IndexGuard() {
this.getFirstArgument() = indexAccess and
this.getSecondArgument() = any(PropertyAccess lengthAccess |
lengthAccess.getQualifier() = array.getAnAccess() and
lengthAccess.getTarget().hasName("Length")
)
}
/** Holds if this comparison applies to array `arr` and index `ind`. */
predicate controls(Variable arr, Variable ind) {
arr = array and
ind.getAnAccess() = indexAccess
}
/** Holds if this comparison guards `expr`. */
predicate guards(GuardedExpr expr, boolean condition) {
expr.isGuardedBy(this.getExpr(), indexAccess, condition)
}
/** Holds if this comparison is an incorrect `<=` or equivalent. */
predicate isIncorrect() {
this.getComparisonKind().isLessThanEquals()
}
}
from IndexGuard incorrectGuard, Variable array, Variable index, ElementAccess ea, GuardedExpr indexAccess
where
// Look for `index <= array.Length` or `array.Length >= index`
incorrectGuard.controls(array, index) and
incorrectGuard.isIncorrect() and
// Look for `array[index]`
and ea.getQualifier() = array.getAnAccess()
and ea.getIndex(0) = indexAccess
and indexAccess = index.getAnAccess()
ea.getQualifier() = array.getAnAccess() and
ea.getIndex(0) = indexAccess and
indexAccess = index.getAnAccess() and
// Where the index access is guarded by the comparison
and indexAccess.(GuardedExpr).isGuardedBy(cmp, index.getAnAccess(), true)
select cmp, "Off-by-one index comparison against length leads to possible out of bounds $@.", ea, ea.toString()
incorrectGuard.guards(indexAccess, true) and
// And there are no other guards
not exists(IndexGuard validGuard |
not validGuard.isIncorrect() and
validGuard.controls(array, index) and
validGuard.guards(indexAccess, _)
)
select incorrectGuard, "Off-by-one index comparison against length leads to possible out of bounds $@.", ea, ea.toString()

Просмотреть файл

@ -2,7 +2,7 @@ using System;
class Test
{
static void Main(string[] args)
void Test1(string[] args)
{
// BAD: Loop upper bound is off-by-one
for (int i = 0; i <= args.Length; i++)
@ -34,6 +34,11 @@ class Test
{
Console.WriteLine(args[j]);
}
}
void Test2(string[] args)
{
int j = 0;
// GOOD: Correct terminating value
if (args.Length > j)
@ -41,4 +46,54 @@ class Test
Console.WriteLine(args[j]);
}
}
void Test3(string[] args)
{
// GOOD: Guarded by ternary operator.
for (int i = 0; i <= args.Length; i++)
{
string s = i < args.Length ? args[i] : "";
}
}
void Test4(string[] args)
{
int j = 0;
// GOOD: Guarded by ternary operator.
if( j <= args.Length )
{
string s = j < args.Length ? args[j] : "";
}
}
void Test5(string[] args)
{
// GOOD: A valid test of Length.
for (int i = 0; i != args.Length; i++)
{
Console.WriteLine(args[i]);
}
}
void Test6(string[] args)
{
int j = 0;
// GOOD: There is another correct test.
if( j <= args.Length )
{
if (j == args.Length) return;
Console.WriteLine(args[j]);
}
}
void Test7(string[] args)
{
// GOOD: Guarded by ||.
for (int i = 0; i <= args.Length; i++)
{
bool b = i == args.Length || args[i] == "x";
}
}
}