зеркало из https://github.com/github/codeql.git
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:
Коммит
8bd8975795
|
@ -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 csharp
|
||||||
import semmle.code.csharp.controlflow.Guards
|
import semmle.code.csharp.controlflow.Guards
|
||||||
|
import semmle.code.csharp.commons.ComparisonTest
|
||||||
|
|
||||||
from RelationalOperation cmp, Variable array, Variable index, ElementAccess ea, VariableAccess indexAccess
|
/** A comparison of an index variable with the length of an array. */
|
||||||
// Look for `index <= array.Length` or `array.Length >= index`
|
class IndexGuard extends ComparisonTest {
|
||||||
where (cmp instanceof GEExpr or cmp instanceof LEExpr)
|
VariableAccess indexAccess;
|
||||||
and cmp.getGreaterOperand() = any(PropertyAccess pa | pa.getQualifier() = array.getAnAccess() and pa.getTarget().hasName("Length"))
|
Variable array;
|
||||||
and cmp.getLesserOperand() = index.getAnAccess()
|
|
||||||
|
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]`
|
// Look for `array[index]`
|
||||||
and ea.getQualifier() = array.getAnAccess()
|
ea.getQualifier() = array.getAnAccess() and
|
||||||
and ea.getIndex(0) = indexAccess
|
ea.getIndex(0) = indexAccess and
|
||||||
and indexAccess = index.getAnAccess()
|
indexAccess = index.getAnAccess() and
|
||||||
// Where the index access is guarded by the comparison
|
// Where the index access is guarded by the comparison
|
||||||
and indexAccess.(GuardedExpr).isGuardedBy(cmp, index.getAnAccess(), true)
|
incorrectGuard.guards(indexAccess, true) and
|
||||||
select cmp, "Off-by-one index comparison against length leads to possible out of bounds $@.", ea, ea.toString()
|
// 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
|
class Test
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
void Test1(string[] args)
|
||||||
{
|
{
|
||||||
// BAD: Loop upper bound is off-by-one
|
// BAD: Loop upper bound is off-by-one
|
||||||
for (int i = 0; i <= args.Length; i++)
|
for (int i = 0; i <= args.Length; i++)
|
||||||
|
@ -34,6 +34,11 @@ class Test
|
||||||
{
|
{
|
||||||
Console.WriteLine(args[j]);
|
Console.WriteLine(args[j]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Test2(string[] args)
|
||||||
|
{
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
// GOOD: Correct terminating value
|
// GOOD: Correct terminating value
|
||||||
if (args.Length > j)
|
if (args.Length > j)
|
||||||
|
@ -41,4 +46,54 @@ class Test
|
||||||
Console.WriteLine(args[j]);
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче