Skip to content

Rust: Add ComparisonOperation library. #19535

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 22, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions rust/ql/lib/codeql/rust/elements/ComparisonOperation.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Provides classes for comparison operations.
*/

private import codeql.rust.elements.BinaryExpr
private import codeql.rust.elements.Operation

/**
* A comparison operation, such as `==`, `<`, or `>=`.
*/
abstract private class ComparisonOperationImpl extends Operation { }

final class ComparisonOperation = ComparisonOperationImpl;

/**
* An equality comparison operation, `==` or `!=`.
*/
abstract private class EqualityOperationImpl extends BinaryExpr, ComparisonOperationImpl { }

final class EqualityOperation = EqualityOperationImpl;

/**
* The equal comparison operation, `==`.
*/
final class EqualsOperation extends EqualityOperationImpl {
EqualsOperation() { this.getOperatorName() = "==" }
}

/**
* The not equal comparison operation, `!=`.
*/
final class NotEqualsOperation extends EqualityOperationImpl {
NotEqualsOperation() { this.getOperatorName() = "!=" }
}

/**
* A relational comparison operation, that is, one of `<=`, `<`, `>`, or `>=`.
*/
abstract private class RelationalOperationImpl extends BinaryExpr, ComparisonOperationImpl {
/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational expression, that is, the side that is larger
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
abstract Expr getGreaterOperand();

/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
* of this relational expression, that is, the side that is smaller
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
abstract Expr getLesserOperand();
}

final class RelationalOperation = RelationalOperationImpl;

/**
* The less than comparison operation, `<`.
*/
final class LessThanOperation extends RelationalOperationImpl {
LessThanOperation() { this.getOperatorName() = "<" }

override Expr getGreaterOperand() { result = this.getRhs() }

override Expr getLesserOperand() { result = this.getLhs() }
}

/**
* The greater than comparison operation, `>`.
*/
final class GreaterThanOperation extends RelationalOperationImpl {
GreaterThanOperation() { this.getOperatorName() = ">" }

override Expr getGreaterOperand() { result = this.getLhs() }

override Expr getLesserOperand() { result = this.getRhs() }
}

/**
* The less than or equal comparison operation, `<=`.
*/
final class LessOrEqualsOperation extends RelationalOperationImpl {
LessOrEqualsOperation() { this.getOperatorName() = "<=" }

override Expr getGreaterOperand() { result = this.getRhs() }

override Expr getLesserOperand() { result = this.getLhs() }
}

/**
* The greater than or equal comparison operation, `>=`.
*/
final class GreaterOrEqualsOperation extends RelationalOperationImpl {
GreaterOrEqualsOperation() { this.getOperatorName() = ">=" }

override Expr getGreaterOperand() { result = this.getLhs() }

override Expr getLesserOperand() { result = this.getRhs() }
}
4 changes: 4 additions & 0 deletions rust/ql/lib/codeql/rust/elements/LogicalOperation.qll
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* Provides classes for logical operations.
*/

private import codeql.rust.elements.Expr
private import codeql.rust.elements.BinaryExpr
private import codeql.rust.elements.PrefixExpr
Original file line number Diff line number Diff line change
@@ -43,51 +43,23 @@ module UncontrolledAllocationSize {
}
}

/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational expression, that is, the side that is larger
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
private Expr getGreaterOperand(BinaryExpr op) {
op.getOperatorName() = ["<", "<="] and
result = op.getRhs()
or
op.getOperatorName() = [">", ">="] and
result = op.getLhs()
}

/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
* of this relational expression, that is, the side that is smaller
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
private Expr getLesserOperand(BinaryExpr op) {
op.getOperatorName() = ["<", "<="] and
result = op.getLhs()
or
op.getOperatorName() = [">", ">="] and
result = op.getRhs()
}

/**
* Holds if comparison `g` having result `branch` indicates an upper bound for the sub-expression
* `node`. For example when the comparison `x < 10` is true, we have an upper bound for `x`.
*/
private predicate isUpperBoundCheck(CfgNodes::AstCfgNode g, Cfg::CfgNode node, boolean branch) {
exists(BinaryExpr cmp | g = cmp.getACfgNode() |
node = getLesserOperand(cmp).getACfgNode() and
node = cmp.(RelationalOperation).getLesserOperand().getACfgNode() and
branch = true
or
node = getGreaterOperand(cmp).getACfgNode() and
node = cmp.(RelationalOperation).getGreaterOperand().getACfgNode() and
branch = false
or
cmp.getOperatorName() = "==" and
cmp instanceof EqualsOperation and
[cmp.getLhs(), cmp.getRhs()].getACfgNode() = node and
branch = true
or
cmp.getOperatorName() = "!=" and
cmp instanceof NotEqualsOperation and
[cmp.getLhs(), cmp.getRhs()].getACfgNode() = node and
branch = false
)
1 change: 1 addition & 0 deletions rust/ql/lib/rust.qll
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import codeql.Locations
import codeql.files.FileSystem
import codeql.rust.elements.Operation
import codeql.rust.elements.AssignmentOperation
import codeql.rust.elements.ComparisonOperation
import codeql.rust.elements.LiteralExprExt
import codeql.rust.elements.LogicalOperation
import codeql.rust.elements.AsyncBlockExpr
30 changes: 29 additions & 1 deletion rust/ql/test/library-tests/operations/Operations.ql
Original file line number Diff line number Diff line change
@@ -13,10 +13,30 @@ string describe(Expr op) {
op instanceof LogicalOperation and result = "LogicalOperation"
or
op instanceof RefExpr and result = "RefExpr"
or
op instanceof ComparisonOperation and result = "ComparisonOperation"
or
op instanceof EqualityOperation and result = "EqualityOperation"
or
op instanceof EqualsOperation and result = "EqualsOperation"
or
op instanceof NotEqualsOperation and result = "NotEqualsOperation"
or
op instanceof RelationalOperation and result = "RelationalOperation"
or
op instanceof LessThanOperation and result = "LessThanOperation"
or
op instanceof GreaterThanOperation and result = "GreaterThanOperation"
or
op instanceof LessOrEqualsOperation and result = "LessOrEqualsOperation"
or
op instanceof GreaterOrEqualsOperation and result = "GreaterOrEqualsOperation"
}

module OperationsTest implements TestSig {
string getARelevantTag() { result = describe(_) or result = ["Op", "Operands"] }
string getARelevantTag() {
result = describe(_) or result = ["Op", "Operands", "Greater", "Lesser"]
}

predicate hasActualResult(Location location, string element, string tag, string value) {
exists(Expr op |
@@ -33,6 +53,14 @@ module OperationsTest implements TestSig {
op instanceof Operation and
tag = "Operands" and
value = count(op.(Operation).getAnOperand()).toString()
or
op instanceof RelationalOperation and
tag = "Greater" and
value = op.(RelationalOperation).getGreaterOperand().toString()
or
op instanceof RelationalOperation and
tag = "Lesser" and
value = op.(RelationalOperation).getLesserOperand().toString()
)
)
}
12 changes: 6 additions & 6 deletions rust/ql/test/library-tests/operations/test.rs
Original file line number Diff line number Diff line change
@@ -11,12 +11,12 @@ fn test_operations(
x = y; // $ Operation Op== Operands=2 AssignmentOperation BinaryExpr

// comparison operations
x == y; // $ Operation Op=== Operands=2 BinaryExpr
x != y; // $ Operation Op=!= Operands=2 BinaryExpr
x < y; // $ Operation Op=< Operands=2 BinaryExpr
x <= y; // $ Operation Op=<= Operands=2 BinaryExpr
x > y; // $ Operation Op=> Operands=2 BinaryExpr
x >= y; // $ Operation Op=>= Operands=2 BinaryExpr
x == y; // $ Operation Op=== Operands=2 BinaryExpr ComparisonOperation EqualityOperation EqualsOperation
x != y; // $ Operation Op=!= Operands=2 BinaryExpr ComparisonOperation EqualityOperation NotEqualsOperation
x < y; // $ Operation Op=< Operands=2 BinaryExpr ComparisonOperation RelationalOperation LessThanOperation Greater=y Lesser=x
x <= y; // $ Operation Op=<= Operands=2 BinaryExpr ComparisonOperation RelationalOperation LessOrEqualsOperation Greater=y Lesser=x
x > y; // $ Operation Op=> Operands=2 BinaryExpr ComparisonOperation RelationalOperation GreaterThanOperation Greater=x Lesser=y
x >= y; // $ Operation Op=>= Operands=2 BinaryExpr ComparisonOperation RelationalOperation GreaterOrEqualsOperation Greater=x Lesser=y

// arithmetic operations
x + y; // $ Operation Op=+ Operands=2 BinaryExpr