mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'master' into python-cwe-312
This commit is contained in:
@@ -3,7 +3,6 @@ import semmle.python.Operations
|
||||
import semmle.python.Variables
|
||||
import semmle.python.AstGenerated
|
||||
import semmle.python.AstExtended
|
||||
import semmle.python.AST
|
||||
import semmle.python.Function
|
||||
import semmle.python.Module
|
||||
import semmle.python.Class
|
||||
@@ -12,7 +11,6 @@ import semmle.python.Stmts
|
||||
import semmle.python.Exprs
|
||||
import semmle.python.Keywords
|
||||
import semmle.python.Comprehensions
|
||||
import semmle.python.Lists
|
||||
import semmle.python.Flow
|
||||
import semmle.python.Metrics
|
||||
import semmle.python.Constants
|
||||
@@ -28,7 +26,6 @@ import semmle.python.types.Version
|
||||
import semmle.python.types.Descriptors
|
||||
import semmle.python.protocols
|
||||
import semmle.python.SSA
|
||||
import semmle.python.Assigns
|
||||
import semmle.python.SelfAttribute
|
||||
import semmle.python.types.Properties
|
||||
import semmle.python.xml.XML
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
import python
|
||||
|
||||
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
abstract class AstNode extends AstNode_ {
|
||||
|
||||
/** Gets the scope that this node occurs in */
|
||||
abstract Scope getScope();
|
||||
|
||||
/** Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode` */
|
||||
ControlFlowNode getAFlowNode() {
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
Location getLocation() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Whether this syntactic element is artificial, that is it is generated
|
||||
* by the compiler and is not present in the source */
|
||||
predicate isArtificial() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt().
|
||||
*/
|
||||
abstract AstNode getAChildNode();
|
||||
|
||||
/** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt() applied to the parent.
|
||||
*/
|
||||
AstNode getParentNode() {
|
||||
result.getAChildNode() = this
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically */
|
||||
predicate contains(AstNode inner) {
|
||||
this.getAChildNode+() = inner
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
|
||||
predicate containsInScope(AstNode inner) {
|
||||
this.contains(inner) and
|
||||
this.getScope() = inner.getScope() and
|
||||
not inner instanceof Scope
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* In order to handle data flow and other analyses efficiently the extractor transforms various statements which perform binding in assignments.
|
||||
* These classes provide a wrapper to provide a more 'natural' interface to the syntactic elements transformed to assignments.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
|
||||
/** An assignment statement */
|
||||
class AssignStmt extends Assign {
|
||||
|
||||
AssignStmt() {
|
||||
not this instanceof FunctionDef and not this instanceof ClassDef
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "AssignStmt"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,61 @@
|
||||
import python
|
||||
|
||||
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
|
||||
abstract class AstNode extends AstNode_ {
|
||||
|
||||
/** Gets the scope that this node occurs in */
|
||||
abstract Scope getScope();
|
||||
|
||||
/** Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode` */
|
||||
ControlFlowNode getAFlowNode() {
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
/** Gets the location for this AST node */
|
||||
Location getLocation() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Whether this syntactic element is artificial, that is it is generated
|
||||
* by the compiler and is not present in the source */
|
||||
predicate isArtificial() {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt().
|
||||
*/
|
||||
abstract AstNode getAChildNode();
|
||||
|
||||
/** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
|
||||
* and other experiments. The child-parent relation may not be meaningful.
|
||||
* For a more meaningful relation in terms of dependency use
|
||||
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
|
||||
* Scope.getAStmt() applied to the parent.
|
||||
*/
|
||||
AstNode getParentNode() {
|
||||
result.getAChildNode() = this
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically */
|
||||
predicate contains(AstNode inner) {
|
||||
this.getAChildNode+() = inner
|
||||
}
|
||||
|
||||
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
|
||||
predicate containsInScope(AstNode inner) {
|
||||
this.contains(inner) and
|
||||
this.getScope() = inner.getScope() and
|
||||
not inner instanceof Scope
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Parents */
|
||||
|
||||
/** Internal implementation class */
|
||||
@@ -116,3 +172,59 @@ class StringPartList extends StringPartList_ {
|
||||
|
||||
}
|
||||
|
||||
/* **** Lists ***/
|
||||
|
||||
/** A parameter list */
|
||||
class ParameterList extends @py_parameter_list {
|
||||
|
||||
Function getParent() {
|
||||
py_parameter_lists(this, result)
|
||||
}
|
||||
|
||||
/** Gets a parameter */
|
||||
Parameter getAnItem() {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the nth parameter */
|
||||
Parameter getItem(int index) {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, index)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = "ParameterList"
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
|
||||
class ComprehensionList extends ComprehensionList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of expressions */
|
||||
class ExprList extends ExprList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
library class DictItemList extends DictItemList_ {
|
||||
|
||||
}
|
||||
|
||||
library class DictItemListParent extends DictItemListParent_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of strings (the primitive type string not Bytes or Unicode) */
|
||||
class StringList extends StringList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of aliases in an import statement */
|
||||
class AliasList extends AliasList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import python
|
||||
import semmle.python.flow.NameNode
|
||||
private import semmle.python.pointsto.PointsTo
|
||||
private import semmle.python.Pruning
|
||||
|
||||
/* Note about matching parent and child nodes and CFG splitting:
|
||||
*
|
||||
@@ -33,10 +31,6 @@ private AstNode toAst(ControlFlowNode n) {
|
||||
*/
|
||||
class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
cached ControlFlowNode() {
|
||||
Pruner::reachable(this)
|
||||
}
|
||||
|
||||
/** Whether this control flow node is a load (including those in augmented assignments) */
|
||||
predicate isLoad() {
|
||||
exists(Expr e | e = toAst(this) | py_expr_contexts(_, 3, e) and not augstore(_, this))
|
||||
@@ -180,8 +174,7 @@ class ControlFlowNode extends @py_flow_node {
|
||||
|
||||
/** Gets a successor of this flow node */
|
||||
ControlFlowNode getASuccessor() {
|
||||
py_successors(this, result) and
|
||||
not Pruner::unreachableEdge(this, result)
|
||||
py_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets the immediate dominator of this flow node */
|
||||
@@ -967,71 +960,147 @@ class RaiseStmtNode extends ControlFlowNode {
|
||||
|
||||
}
|
||||
|
||||
private
|
||||
predicate defined_by(NameNode def, Variable v) {
|
||||
def.defines(v) or
|
||||
exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v))
|
||||
}
|
||||
/** A control flow node corresponding to a (plain variable) name expression, such as `var`.
|
||||
* `None`, `True` and `False` are excluded.
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
|
||||
/* Combine extractor-generated basic block after pruning */
|
||||
|
||||
private class BasicBlockPart extends @py_flow_node {
|
||||
|
||||
string toString() { result = "Basic block part" }
|
||||
|
||||
BasicBlockPart() {
|
||||
py_flow_bb_node(_, _, this, _) and
|
||||
Pruner::reachable(this)
|
||||
}
|
||||
|
||||
predicate isHead() {
|
||||
count(this.(ControlFlowNode).getAPredecessor()) != 1
|
||||
NameNode() {
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(ControlFlowNode pred | pred = this.(ControlFlowNode).getAPredecessor() | strictcount(pred.getASuccessor()) > 1)
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
private BasicBlockPart previous() {
|
||||
not this.isHead() and
|
||||
py_flow_bb_node(this.(ControlFlowNode).getAPredecessor(), _, result, _)
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v))
|
||||
and not this.isLoad()
|
||||
}
|
||||
|
||||
BasicBlockPart getHead() {
|
||||
this.isHead() and result = this
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.deletes(v))
|
||||
}
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Variable v) {
|
||||
this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
result = this.previous().getHead()
|
||||
}
|
||||
|
||||
predicate isLast() {
|
||||
not exists(BasicBlockPart part | part.previous() = this)
|
||||
}
|
||||
|
||||
int length() {
|
||||
result = max(int j | py_flow_bb_node(_, _, this, j)) + 1
|
||||
}
|
||||
|
||||
int startIndex() {
|
||||
this.isHead() and result = 0
|
||||
exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load)
|
||||
or
|
||||
exists(BasicBlockPart prev |
|
||||
prev = this.previous() and
|
||||
result = prev.startIndex() + prev.length()
|
||||
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Name).getId()
|
||||
or
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
predicate isLocal() {
|
||||
Scopes::local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a non-local variable. */
|
||||
predicate isNonLocal() {
|
||||
Scopes::non_local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() {
|
||||
Scopes::use_of_global_variable(this, _, _)
|
||||
}
|
||||
|
||||
predicate isSelf() {
|
||||
exists(SsaVariable selfvar |
|
||||
selfvar.isSelf() and selfvar.getAUse() = this
|
||||
)
|
||||
}
|
||||
|
||||
predicate contains(ControlFlowNode node) {
|
||||
py_flow_bb_node(node, _, this, _)
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
|
||||
NameConstantNode() {
|
||||
exists(NameConstant n | py_flow_bb_node(this, n, _, _))
|
||||
}
|
||||
|
||||
int indexOf(ControlFlowNode node) {
|
||||
py_flow_bb_node(node, _, this, result)
|
||||
override deprecated predicate defines(Variable v) { none() }
|
||||
|
||||
override deprecated predicate deletes(Variable v) { none() }
|
||||
|
||||
/* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
deprecated predicate uses(Variable v) { none() }
|
||||
*/
|
||||
}
|
||||
|
||||
private module Scopes {
|
||||
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode lastNode() {
|
||||
this.indexOf(result) = max(this.indexOf(_))
|
||||
predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
BasicBlockPart getImmediateDominator() {
|
||||
result.contains(this.(ControlFlowNode).getImmediateDominator())
|
||||
predicate non_local(NameNode n) {
|
||||
exists(FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma [nomagic]
|
||||
predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n)
|
||||
and
|
||||
not exists(SsaVariable var |
|
||||
var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Class and
|
||||
not maybe_undefined(var)
|
||||
)
|
||||
and name = n.getId()
|
||||
and scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_defined(SsaVariable var) {
|
||||
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
|
||||
or
|
||||
exists(SsaVariable input |
|
||||
input = var.getAPhiInput() |
|
||||
maybe_defined(input)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
or
|
||||
maybe_undefined(var.getAPhiInput())
|
||||
or
|
||||
exists(BasicBlock incoming |
|
||||
exists(var.getAPhiInput()) and
|
||||
incoming.getASuccessor() = var.getDefinition().getBasicBlock() and
|
||||
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1040,24 +1109,17 @@ private class BasicBlockPart extends @py_flow_node {
|
||||
class BasicBlock extends @py_flow_node {
|
||||
|
||||
BasicBlock() {
|
||||
this.(BasicBlockPart).isHead()
|
||||
}
|
||||
|
||||
private BasicBlockPart getAPart() {
|
||||
result.getHead() = this
|
||||
py_flow_bb_node(_, _, this, _)
|
||||
}
|
||||
|
||||
/** Whether this basic block contains the specified node */
|
||||
predicate contains(ControlFlowNode node) {
|
||||
this.getAPart().contains(node)
|
||||
py_flow_bb_node(node, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the nth node in this basic block */
|
||||
ControlFlowNode getNode(int n) {
|
||||
exists(BasicBlockPart part |
|
||||
part = this.getAPart() and
|
||||
n = part.startIndex() + part.indexOf(result)
|
||||
)
|
||||
py_flow_bb_node(result, _, this, n)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
@@ -1077,7 +1139,7 @@ class BasicBlock extends @py_flow_node {
|
||||
}
|
||||
|
||||
cached BasicBlock getImmediateDominator() {
|
||||
this.getAPart().getImmediateDominator() = result.getAPart()
|
||||
this.firstNode().getImmediateDominator().getBasicBlock() = result
|
||||
}
|
||||
|
||||
/** Dominance frontier of a node x is the set of all nodes `other` such that `this` dominates a predecessor
|
||||
@@ -1093,10 +1155,9 @@ class BasicBlock extends @py_flow_node {
|
||||
|
||||
/** Gets the last node in this basic block */
|
||||
ControlFlowNode getLastNode() {
|
||||
exists(BasicBlockPart part |
|
||||
part = this.getAPart() and
|
||||
part.isLast() and
|
||||
result = part.lastNode()
|
||||
exists(int i |
|
||||
this.getNode(i) = result and
|
||||
i = max(int j | py_flow_bb_node(_, _, this, j))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import python
|
||||
|
||||
/** A parameter list */
|
||||
class ParameterList extends @py_parameter_list {
|
||||
|
||||
Function getParent() {
|
||||
py_parameter_lists(this, result)
|
||||
}
|
||||
|
||||
/** Gets a parameter */
|
||||
Parameter getAnItem() {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, _)
|
||||
}
|
||||
|
||||
/** Gets the nth parameter */
|
||||
Parameter getItem(int index) {
|
||||
/* Item can be a Name or a Tuple, both of which are expressions */
|
||||
py_exprs(result, _, this, index)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = "ParameterList"
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
|
||||
class ComprehensionList extends ComprehensionList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of expressions */
|
||||
class ExprList extends ExprList_ {
|
||||
|
||||
}
|
||||
|
||||
|
||||
library class DictItemList extends DictItemList_ {
|
||||
|
||||
}
|
||||
|
||||
library class DictItemListParent extends DictItemListParent_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of strings (the primitive type string not Bytes or Unicode) */
|
||||
class StringList extends StringList_ {
|
||||
|
||||
}
|
||||
|
||||
/** A list of aliases in an import statement */
|
||||
class AliasList extends AliasList_ {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,640 +0,0 @@
|
||||
|
||||
private import AST
|
||||
private import Exprs
|
||||
private import Stmts
|
||||
private import Import
|
||||
private import Operations
|
||||
|
||||
module Pruner {
|
||||
|
||||
/** A control flow node before pruning */
|
||||
class UnprunedCfgNode extends @py_flow_node {
|
||||
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets a predecessor of this flow node */
|
||||
UnprunedCfgNode getAPredecessor() {
|
||||
py_successors(result, this)
|
||||
}
|
||||
|
||||
/** Gets a successor of this flow node */
|
||||
UnprunedCfgNode getASuccessor() {
|
||||
py_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets the immediate dominator of this flow node */
|
||||
UnprunedCfgNode getImmediateDominator() {
|
||||
py_idoms(this, result)
|
||||
}
|
||||
|
||||
/* Holds if this CFG node is a branch */
|
||||
predicate isBranch() {
|
||||
py_true_successors(this, _) or py_false_successors(this, _)
|
||||
}
|
||||
|
||||
/** Gets the syntactic element corresponding to this flow node */
|
||||
AstNode getNode() {
|
||||
py_flow_bb_node(this, result, _, _)
|
||||
}
|
||||
|
||||
UnprunedBasicBlock getBasicBlock() {
|
||||
py_flow_bb_node(this, _, result, _)
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is True. */
|
||||
UnprunedCfgNode getATrueSuccessor() {
|
||||
py_true_successors(this, result)
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is False. */
|
||||
UnprunedCfgNode getAFalseSuccessor() {
|
||||
py_false_successors(this, result)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a comparison operation, such as `x<y` */
|
||||
class UnprunedCompareNode extends UnprunedCfgNode {
|
||||
|
||||
UnprunedCompareNode() {
|
||||
py_flow_bb_node(this, any(Compare c), _, _)
|
||||
}
|
||||
|
||||
/** Whether left and right are a pair of operands for this comparison */
|
||||
predicate operands(UnprunedCfgNode left, Cmpop op, UnprunedCfgNode right) {
|
||||
exists(Compare c, Expr eleft, Expr eright |
|
||||
this.getNode() = c and left.getNode() = eleft and right.getNode() = eright |
|
||||
eleft = c.getLeft() and eright = c.getComparator(0) and op = c.getOp(0)
|
||||
or
|
||||
exists(int i | eleft = c.getComparator(i-1) and eright = c.getComparator(i) and op = c.getOp(i))
|
||||
) and
|
||||
left.getBasicBlock().dominates(this.getBasicBlock()) and
|
||||
right.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a unary not expression: (`not x`) */
|
||||
class UnprunedNot extends UnprunedCfgNode {
|
||||
UnprunedNot() {
|
||||
exists(UnaryExpr unary |
|
||||
py_flow_bb_node(this, unary, _, _) and
|
||||
unary.getOp() instanceof Not
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the control flow node corresponding to the operand of this `not` expression */
|
||||
UnprunedCfgNode getOperand() {
|
||||
exists(UnaryExpr u | this.getNode() = u and result.getNode() = u.getOperand()) and
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A basic block before pruning */
|
||||
class UnprunedBasicBlock extends @py_flow_node {
|
||||
|
||||
UnprunedBasicBlock() {
|
||||
py_flow_bb_node(_, _, this, _)
|
||||
}
|
||||
|
||||
/** Whether this basic block contains the specified node */
|
||||
predicate contains(UnprunedCfgNode node) {
|
||||
py_flow_bb_node(node, _, this, _)
|
||||
}
|
||||
|
||||
string toString() { none() }
|
||||
|
||||
/** Whether this basic block strictly dominates the other */
|
||||
pragma[nomagic] predicate strictlyDominates(UnprunedBasicBlock other) {
|
||||
other.getImmediateDominator+() = this
|
||||
}
|
||||
|
||||
/** Whether this basic block dominates the other */
|
||||
pragma[nomagic] predicate dominates(UnprunedBasicBlock other) {
|
||||
this = other
|
||||
or
|
||||
this.strictlyDominates(other)
|
||||
}
|
||||
|
||||
UnprunedBasicBlock getImmediateDominator() {
|
||||
this.first().getImmediateDominator().getBasicBlock() = result
|
||||
}
|
||||
|
||||
UnprunedBasicBlock getASuccessor() {
|
||||
this.last().getASuccessor() = result.first()
|
||||
}
|
||||
|
||||
UnprunedCfgNode first() {
|
||||
py_flow_bb_node(result, _, this, 0)
|
||||
}
|
||||
|
||||
UnprunedCfgNode last() {
|
||||
py_flow_bb_node(result, _, this, max(int i | py_flow_bb_node(_, _, this, i)))
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is True. */
|
||||
UnprunedBasicBlock getATrueSuccessor() {
|
||||
this.last().getATrueSuccessor() = result.first()
|
||||
}
|
||||
|
||||
/** Gets a successor for this node if the relevant condition is False. */
|
||||
UnprunedBasicBlock getAFalseSuccessor() {
|
||||
this.last().getAFalseSuccessor() = result.first()
|
||||
}
|
||||
|
||||
/** Whether this BB is the first in its scope */
|
||||
predicate isEntry() {
|
||||
py_scope_flow(this.first(), _, -1)
|
||||
}
|
||||
|
||||
UnprunedCfgNode getANode() {
|
||||
py_flow_bb_node(result, _, this, _)
|
||||
}
|
||||
}
|
||||
|
||||
private import Comparisons
|
||||
private import SSA
|
||||
|
||||
private int intValue(ImmutableLiteral lit) {
|
||||
result = lit.(IntegerLiteral).getValue()
|
||||
or
|
||||
result = lit.(NegativeIntegerLiteral).getValue()
|
||||
}
|
||||
|
||||
newtype TConstraint =
|
||||
TTruthy(boolean b) { b = true or b = false }
|
||||
or
|
||||
TIsNone(boolean b) { b = true or b = false }
|
||||
or
|
||||
TConstrainedByConstant(CompareOp op, int k) {
|
||||
int_test(_, _, op, k)
|
||||
or
|
||||
exists(Assign a | intValue(a.getValue()) = k) and op = eq()
|
||||
}
|
||||
|
||||
/** A constraint that may be applied to an SSA variable.
|
||||
* Used for computing unreachable edges
|
||||
*/
|
||||
abstract class Constraint extends TConstraint {
|
||||
|
||||
abstract string toString();
|
||||
|
||||
abstract Constraint invert();
|
||||
|
||||
/** Holds if this constraint constrains the "truthiness" of the variable.
|
||||
* That is, for a variable `var` constrained by this constraint
|
||||
* `bool(var) is value`
|
||||
*/
|
||||
abstract predicate constrainsVariableToBe(boolean value);
|
||||
|
||||
/** Holds if the value constrained by this constraint cannot be `None` */
|
||||
abstract predicate cannotBeNone();
|
||||
|
||||
}
|
||||
|
||||
/** A basic block ending in a test (and branch). */
|
||||
class UnprunedConditionBlock extends UnprunedBasicBlock {
|
||||
|
||||
UnprunedConditionBlock() { this.last().isBranch() }
|
||||
|
||||
/** Holds if `controlled` is only reachable if the test in this block evaluates to `testIsTrue` */
|
||||
predicate controls(UnprunedBasicBlock controlled, boolean testIsTrue) {
|
||||
/* For this block to control the block 'controlled' with 'testIsTrue' the following must be true:
|
||||
Execution must have passed through the test i.e. 'this' must strictly dominate 'controlled'.
|
||||
Execution must have passed through the 'testIsTrue' edge leaving 'this'.
|
||||
|
||||
Although "passed through the true edge" implies that this.getATrueSuccessor() dominates 'controlled',
|
||||
the reverse is not true, as flow may have passed through another edge to get to this.getATrueSuccessor()
|
||||
so we need to assert that this.getATrueSuccessor() dominates 'controlled' *and* that
|
||||
all predecessors of this.getATrueSuccessor() are either this or dominated by this.getATrueSuccessor().
|
||||
|
||||
For example, in the following python snippet:
|
||||
<code>
|
||||
if x:
|
||||
controlled
|
||||
false_successor
|
||||
uncontrolled
|
||||
</code>
|
||||
false_successor dominates uncontrolled, but not all of its predecessors are this (if x)
|
||||
or dominated by itself. Whereas in the following code:
|
||||
<code>
|
||||
if x:
|
||||
while controlled:
|
||||
also_controlled
|
||||
false_successor
|
||||
uncontrolled
|
||||
</code>
|
||||
the block 'while controlled' is controlled because all of its predecessors are this (if x)
|
||||
or (in the case of 'also_controlled') dominated by itself.
|
||||
|
||||
The additional constraint on the predecessors of the test successor implies
|
||||
that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||
directly.
|
||||
*/
|
||||
exists(UnprunedBasicBlock succ |
|
||||
testIsTrue = true and succ = this.getATrueSuccessor()
|
||||
or
|
||||
testIsTrue = false and succ = this.getAFalseSuccessor()
|
||||
|
|
||||
succ.dominates(controlled) and
|
||||
forall(UnprunedBasicBlock pred | pred.getASuccessor() = succ |
|
||||
pred = this or succ.dominates(pred)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the edge `pred->succ` is reachable only if the test in this block evaluates to `testIsTrue` */
|
||||
predicate controlsEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ, boolean testIsTrue) {
|
||||
this.controls(pred, testIsTrue) and succ = pred.getASuccessor()
|
||||
or
|
||||
pred = this and (
|
||||
testIsTrue = true and succ = this.getATrueSuccessor()
|
||||
or
|
||||
testIsTrue = false and succ = this.getAFalseSuccessor()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A constraint that the variable is truthy `bool(var) is True` or falsey `bool(var) is False` */
|
||||
class Truthy extends Constraint, TTruthy {
|
||||
|
||||
private boolean booleanValue() {
|
||||
this = TTruthy(result)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Truthy" and this.booleanValue() = true
|
||||
or
|
||||
result = "Falsey" and this.booleanValue() = false
|
||||
}
|
||||
|
||||
override Constraint invert() {
|
||||
result = TTruthy(this.booleanValue().booleanNot())
|
||||
}
|
||||
|
||||
override predicate constrainsVariableToBe(boolean value) {
|
||||
value = this.booleanValue()
|
||||
}
|
||||
|
||||
override predicate cannotBeNone() {
|
||||
this.booleanValue() = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A constraint that the variable is None `(var is None) is True` or not None `(var is None) is False`.
|
||||
* This includes the `is not` operator, `x is not None` being equivalent to `not x is None` */
|
||||
class IsNone extends Constraint, TIsNone {
|
||||
|
||||
private boolean isNone() {
|
||||
this = TIsNone(result)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "Is None" and this.isNone() = true
|
||||
or
|
||||
result = "Is not None" and this.isNone() = false
|
||||
}
|
||||
|
||||
override Constraint invert() {
|
||||
result = TIsNone(this.isNone().booleanNot())
|
||||
}
|
||||
|
||||
override predicate constrainsVariableToBe(boolean value) {
|
||||
value = false and this.isNone() = true
|
||||
}
|
||||
|
||||
override predicate cannotBeNone() {
|
||||
this = TIsNone(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A constraint that the variable fulfils some equality or inequality to an integral constant.
|
||||
* `(var op k) is True` where `op` is an equality or inequality operator and `k` is an integer constant
|
||||
*/
|
||||
class ConstrainedByConstant extends Constraint, TConstrainedByConstant {
|
||||
|
||||
private int intValue() {
|
||||
this = TConstrainedByConstant(_, result)
|
||||
}
|
||||
|
||||
private CompareOp getOp() {
|
||||
this = TConstrainedByConstant(result, _)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.getOp().repr() + " " + this.intValue().toString()
|
||||
}
|
||||
|
||||
override Constraint invert() {
|
||||
result = TConstrainedByConstant(this.getOp().invert(), this.intValue())
|
||||
}
|
||||
|
||||
override predicate constrainsVariableToBe(boolean value) {
|
||||
this.getOp() = eq() and this.intValue() = 0 and value = false
|
||||
or
|
||||
value = true and (
|
||||
this.getOp() = eq() and this.intValue() != 0
|
||||
or
|
||||
this.getOp() = lt() and this.intValue() <= 0
|
||||
or
|
||||
this.getOp() = le() and this.intValue() < 0
|
||||
or
|
||||
this.getOp() = gt() and this.intValue() >= 0
|
||||
or
|
||||
this.getOp() = ge() and this.intValue() > 0
|
||||
)
|
||||
}
|
||||
|
||||
predicate eq(int val) {
|
||||
this = TConstrainedByConstant(eq(), val)
|
||||
}
|
||||
|
||||
predicate ne(int val) {
|
||||
this = TConstrainedByConstant(ne(), val)
|
||||
}
|
||||
|
||||
override predicate cannotBeNone() {
|
||||
this.getOp() = eq()
|
||||
}
|
||||
|
||||
/** The minimum value that a variable fulfilling this constraint may hold
|
||||
* within the bounds of a signed 32 bit number.
|
||||
*/
|
||||
int minValue() {
|
||||
this.getOp() = eq() and result = this.intValue()
|
||||
or
|
||||
this.getOp() = lt() and result = -2147483648
|
||||
or
|
||||
this.getOp() = le() and result = -2147483648
|
||||
or
|
||||
this.getOp() = gt() and result = this.intValue()+1
|
||||
or
|
||||
this.getOp() = ge() and result = this.intValue()
|
||||
}
|
||||
|
||||
/** The maximum value that a variable fulfilling this constraint may hold
|
||||
* within the bounds of a signed 32 bit number.
|
||||
*/
|
||||
int maxValue() {
|
||||
this.getOp() = eq() and result = this.intValue()
|
||||
or
|
||||
this.getOp() = gt() and result = 2147483647
|
||||
or
|
||||
this.getOp() = ge() and result = 2147483647
|
||||
or
|
||||
this.getOp() = lt() and result = this.intValue()-1
|
||||
or
|
||||
this.getOp() = le() and result = this.intValue()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Holds if the control flow node `n` is unreachable due to
|
||||
* one or more constraints.
|
||||
*/
|
||||
predicate unreachable(UnprunedCfgNode n) {
|
||||
exists(UnprunedBasicBlock bb |
|
||||
unreachableBB(bb) and bb.contains(n)
|
||||
)
|
||||
}
|
||||
|
||||
predicate reachable(UnprunedCfgNode n) {
|
||||
exists(UnprunedBasicBlock bb |
|
||||
reachableBB(bb) and bb.contains(n)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the basic block `bb` is unreachable due to
|
||||
* one or more constraints.
|
||||
*/
|
||||
predicate unreachableBB(UnprunedBasicBlock bb) {
|
||||
not reachableBB(bb)
|
||||
}
|
||||
|
||||
/** Holds if the basic block `bb` is reachable despite
|
||||
* constraints
|
||||
*/
|
||||
predicate reachableBB(UnprunedBasicBlock bb) {
|
||||
bb.isEntry() or
|
||||
reachableEdge(_, bb)
|
||||
}
|
||||
|
||||
Constraint constraintFromExpr(SsaVariable var, UnprunedCfgNode node) {
|
||||
py_ssa_use(node, var) and result = TTruthy(true)
|
||||
or
|
||||
exists(boolean b |
|
||||
none_test(node, var, b) and result = TIsNone(b)
|
||||
)
|
||||
or
|
||||
exists(CompareOp op, int k |
|
||||
int_test(node, var, op, k) and
|
||||
result = TConstrainedByConstant(op, k)
|
||||
)
|
||||
or
|
||||
result = constraintFromExpr(var, node.(UnprunedNot).getOperand()).invert()
|
||||
}
|
||||
|
||||
Constraint constraintFromTest(SsaVariable var, UnprunedCfgNode node) {
|
||||
result = constraintFromExpr(var, node) and node.isBranch()
|
||||
}
|
||||
|
||||
predicate none_test(UnprunedCompareNode test, SsaVariable var, boolean is) {
|
||||
exists(UnprunedCfgNode left, Cmpop op, UnprunedCfgNode right |
|
||||
py_ssa_use(left, var) and
|
||||
test.operands(left, op, right) and
|
||||
right.getNode() instanceof None
|
||||
|
|
||||
op instanceof Is and is = true
|
||||
or
|
||||
op instanceof IsNot and is = false
|
||||
)
|
||||
}
|
||||
|
||||
predicate int_test(UnprunedCfgNode test, SsaVariable var, CompareOp op, int k) {
|
||||
exists(UnprunedCfgNode left, UnprunedCfgNode right, Cmpop cop |
|
||||
test.(UnprunedCompareNode).operands(left, cop, right)
|
||||
|
|
||||
op.forOp(cop) and
|
||||
py_ssa_use(left, var) and
|
||||
intValue(right.getNode()) = k
|
||||
or
|
||||
op.reverse().forOp(cop) and
|
||||
py_ssa_use(right, var) and
|
||||
intValue(left.getNode()) = k
|
||||
)
|
||||
}
|
||||
|
||||
private predicate constrainingValue(Expr e) {
|
||||
exists(Assign a, UnprunedCfgNode asgn |
|
||||
a.getValue() = e and a.getATarget() = asgn.getNode() and py_ssa_defn(_, asgn)
|
||||
)
|
||||
or
|
||||
exists(UnaryExpr n | constrainingValue(n) and n.getOp() instanceof Not and e = n.getOperand())
|
||||
}
|
||||
|
||||
private Constraint constraintFromValue(Expr e) {
|
||||
constrainingValue(e) and
|
||||
(
|
||||
result = TConstrainedByConstant(eq(), intValue(e))
|
||||
or
|
||||
e instanceof True and result = TTruthy(true)
|
||||
or
|
||||
e instanceof False and result = TTruthy(false)
|
||||
or
|
||||
e instanceof None and result = TIsNone(true)
|
||||
or
|
||||
result = constraintFromValue(e.(UnaryExpr).getOperand()).invert()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the constraint on `var` resulting from the assignment in `asgn` */
|
||||
Constraint constraintFromAssignment(SsaVariable var, UnprunedCfgNode asgn) {
|
||||
exists(Assign a |
|
||||
a.getATarget() = asgn.getNode() and
|
||||
py_ssa_defn(var, asgn) and
|
||||
result = constraintFromValue(a.getValue())
|
||||
)
|
||||
or
|
||||
module_import(asgn, var) and result = TTruthy(true)
|
||||
}
|
||||
|
||||
/** Holds if the constraint `preval` holds for `var` on edge `pred` -> `succ` as a result of a prior test or assignment */
|
||||
pragma [nomagic]
|
||||
predicate priorConstraint(UnprunedBasicBlock pred, UnprunedBasicBlock succ, Constraint preval, SsaVariable var) {
|
||||
not (blacklisted(var) and preval = TTruthy(_))
|
||||
and
|
||||
not var.getVariable().escapes()
|
||||
and
|
||||
exists(UnprunedBasicBlock first |
|
||||
not first = pred and
|
||||
first.(UnprunedConditionBlock).controlsEdge(pred, succ, true) and
|
||||
preval = constraintFromTest(var, first.last())
|
||||
or
|
||||
not first = pred and
|
||||
first.(UnprunedConditionBlock).controlsEdge(pred, succ, false) and
|
||||
preval = constraintFromTest(var, first.last()).invert()
|
||||
or
|
||||
preval = constraintFromAssignment(var, first.getANode()) and
|
||||
first.dominates(pred) and
|
||||
(succ = pred.getAFalseSuccessor() or succ = pred.getATrueSuccessor())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `cond` holds for `var` on conditional edge `pred` -> `succ` as a result of the test for that edge */
|
||||
pragma [nomagic]
|
||||
predicate constraintOnBranch(UnprunedBasicBlock pred, UnprunedBasicBlock succ, Constraint cond, SsaVariable var) {
|
||||
cond = constraintFromTest(var, pred.last()) and
|
||||
succ = pred.getATrueSuccessor()
|
||||
or
|
||||
cond = constraintFromTest(var, pred.last()).invert() and
|
||||
succ = pred.getAFalseSuccessor()
|
||||
}
|
||||
|
||||
/** Holds if the pair of constraints (`preval`, `postcond`) holds on the edge `pred` -> `succ` for some SSA variable */
|
||||
predicate controllingConditions(UnprunedBasicBlock pred, UnprunedBasicBlock succ, Constraint preval, Constraint postcond) {
|
||||
exists(SsaVariable var |
|
||||
priorConstraint(pred, succ, preval, var) and
|
||||
constraintOnBranch(pred, succ, postcond, var)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the edge `pred` -> `succ` should be pruned as it cannot be reached */
|
||||
predicate unreachableEdge(UnprunedCfgNode pred, UnprunedCfgNode succ) {
|
||||
exists(UnprunedBasicBlock predBB, UnprunedBasicBlock succBB |
|
||||
succBB = predBB.getASuccessor() and
|
||||
not reachableEdge(predBB, succBB) and
|
||||
pred = predBB.last() and succ = succBB.first()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the edge `pred` -> `succ` is reachable as a result of
|
||||
* `pred` being reachable and this edge not being pruned. */
|
||||
predicate reachableEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
reachableBB(pred) and succ = pred.getASuccessor() and
|
||||
not contradictoryEdge(pred, succ) and
|
||||
not simplyDead(pred, succ)
|
||||
}
|
||||
|
||||
predicate contradictoryEdge(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
exists(Constraint pre, Constraint cond |
|
||||
controllingConditions(pred, succ, pre, cond) and
|
||||
contradicts(pre, cond)
|
||||
)
|
||||
}
|
||||
|
||||
/* Helper for `contradictoryEdge`, deal with inequalities here to avoid blow up */
|
||||
pragma [inline]
|
||||
private predicate contradicts(Constraint a, Constraint b) {
|
||||
a = TIsNone(true) and b.cannotBeNone()
|
||||
or
|
||||
a.cannotBeNone() and b = TIsNone(true)
|
||||
or
|
||||
a.constrainsVariableToBe(true) and b.constrainsVariableToBe(false)
|
||||
or
|
||||
a.constrainsVariableToBe(false) and b.constrainsVariableToBe(true)
|
||||
or
|
||||
a.(ConstrainedByConstant).minValue() > b.(ConstrainedByConstant).maxValue()
|
||||
or
|
||||
a.(ConstrainedByConstant).maxValue() < b.(ConstrainedByConstant).minValue()
|
||||
or
|
||||
exists(int val |
|
||||
a.(ConstrainedByConstant).eq(val) and b.(ConstrainedByConstant).ne(val)
|
||||
or
|
||||
a.(ConstrainedByConstant).ne(val) and b.(ConstrainedByConstant).eq(val)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if edge is simply dead. Stuff like `if False: ...` */
|
||||
predicate simplyDead(UnprunedBasicBlock pred, UnprunedBasicBlock succ) {
|
||||
constTest(pred.last()) = true and pred.getAFalseSuccessor() = succ
|
||||
or
|
||||
constTest(pred.last()) = false and pred.getATrueSuccessor() = succ
|
||||
}
|
||||
|
||||
/* Helper for simplyDead */
|
||||
private boolean constTest(UnprunedCfgNode node) {
|
||||
exists(ImmutableLiteral lit |
|
||||
result = lit.booleanValue() and lit = node.getNode()
|
||||
)
|
||||
or
|
||||
result = constTest(node.(UnprunedNot).getOperand()).booleanNot()
|
||||
}
|
||||
|
||||
/** Holds if `var` is blacklisted as having possibly been mutated */
|
||||
predicate blacklisted(SsaVariable var) {
|
||||
possibly_mutated(var) and not whitelisted(var)
|
||||
}
|
||||
|
||||
predicate possibly_mutated(SsaVariable var) {
|
||||
exists(Subscript subscr, UnprunedCfgNode node |
|
||||
subscr.getObject() = node.getNode() and
|
||||
py_ssa_use(node, var)
|
||||
)
|
||||
or
|
||||
exists(Attribute attr, UnprunedCfgNode node |
|
||||
attr.getObject() = node.getNode() and
|
||||
py_ssa_use(node, var)
|
||||
)
|
||||
}
|
||||
|
||||
/** If SSA variable is defined by an import, then it should
|
||||
* be whitelisted as taking an attribute cannot change its
|
||||
* truthiness.
|
||||
*/
|
||||
predicate whitelisted(SsaVariable var) {
|
||||
module_import(_, var)
|
||||
}
|
||||
|
||||
private predicate module_import(UnprunedCfgNode asgn, SsaVariable var) {
|
||||
exists(Alias alias |
|
||||
alias.getValue() instanceof ImportExpr and
|
||||
py_ssa_defn(var, asgn) and
|
||||
alias.getAsname() = asgn.getNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,18 @@ class Assign extends Assign_ {
|
||||
}
|
||||
}
|
||||
|
||||
/** An assignment statement */
|
||||
class AssignStmt extends Assign {
|
||||
|
||||
AssignStmt() {
|
||||
not this instanceof FunctionDef and not this instanceof ClassDef
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = "AssignStmt"
|
||||
}
|
||||
}
|
||||
|
||||
/** An augmented assignment statement, such as `x += y` */
|
||||
class AugAssign extends AugAssign_ {
|
||||
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
import python
|
||||
private import semmle.python.pointsto.Base
|
||||
|
||||
/** A control flow node corresponding to a (plain variable) name expression, such as `var`.
|
||||
* `None`, `True` and `False` are excluded.
|
||||
*/
|
||||
class NameNode extends ControlFlowNode {
|
||||
|
||||
NameNode() {
|
||||
exists(Name n | py_flow_bb_node(this, n, _, _))
|
||||
or
|
||||
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
|
||||
}
|
||||
|
||||
/** Whether this flow node defines the variable `v`. */
|
||||
predicate defines(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.defines(v))
|
||||
and not this.isLoad()
|
||||
}
|
||||
|
||||
/** Whether this flow node deletes the variable `v`. */
|
||||
predicate deletes(Variable v) {
|
||||
exists(Name d | this.getNode() = d and d.deletes(v))
|
||||
}
|
||||
|
||||
/** Whether this flow node uses the variable `v`. */
|
||||
predicate uses(Variable v) {
|
||||
this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v))
|
||||
or
|
||||
exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load)
|
||||
or
|
||||
use_of_global_variable(this, v.getScope(), v.getId())
|
||||
}
|
||||
|
||||
string getId() {
|
||||
result = this.getNode().(Name).getId()
|
||||
or
|
||||
result = this.getNode().(PlaceHolder).getId()
|
||||
}
|
||||
|
||||
/** Whether this is a use of a local variable. */
|
||||
predicate isLocal() {
|
||||
local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a non-local variable. */
|
||||
predicate isNonLocal() {
|
||||
non_local(this)
|
||||
}
|
||||
|
||||
/** Whether this is a use of a global (including builtin) variable. */
|
||||
predicate isGlobal() {
|
||||
use_of_global_variable(this, _, _)
|
||||
}
|
||||
|
||||
predicate isSelf() {
|
||||
exists(SsaVariable selfvar |
|
||||
selfvar.isSelf() and selfvar.getAUse() = this
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private predicate fast_local(NameNode n) {
|
||||
exists(FastLocalVariable v |
|
||||
n.uses(v) and
|
||||
v.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate local(NameNode n) {
|
||||
fast_local(n)
|
||||
or
|
||||
exists(SsaVariable var |
|
||||
var.getAUse() = n and
|
||||
n.getScope() instanceof Class and
|
||||
exists(var.getDefinition())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate non_local(NameNode n) {
|
||||
exists(FastLocalVariable flv |
|
||||
flv.getALoad() = n.getNode() and
|
||||
not flv.getScope() = n.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
// magic is fine, but we get questionable join-ordering of it
|
||||
pragma [nomagic]
|
||||
private predicate use_of_global_variable(NameNode n, Module scope, string name) {
|
||||
n.isLoad() and
|
||||
not non_local(n)
|
||||
and
|
||||
not exists(SsaVariable var |
|
||||
var.getAUse() = n |
|
||||
var.getVariable() instanceof FastLocalVariable
|
||||
or
|
||||
n.getScope() instanceof Class and
|
||||
not maybe_undefined(var)
|
||||
)
|
||||
and name = n.getId()
|
||||
and scope = n.getEnclosingModule()
|
||||
}
|
||||
|
||||
private predicate maybe_defined(SsaVariable var) {
|
||||
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
|
||||
or
|
||||
exists(SsaVariable input |
|
||||
input = var.getAPhiInput() |
|
||||
maybe_defined(input)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate maybe_undefined(SsaVariable var) {
|
||||
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
|
||||
or
|
||||
var.getDefinition().isDelete()
|
||||
or
|
||||
maybe_undefined(var.getAPhiInput())
|
||||
or
|
||||
exists(BasicBlock incoming |
|
||||
exists(var.getAPhiInput()) and
|
||||
incoming.getASuccessor() = var.getDefinition().getBasicBlock() and
|
||||
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
|
||||
)
|
||||
}
|
||||
|
||||
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
|
||||
class NameConstantNode extends NameNode {
|
||||
|
||||
NameConstantNode() {
|
||||
exists(NameConstant n | py_flow_bb_node(this, n, _, _))
|
||||
}
|
||||
|
||||
override deprecated predicate defines(Variable v) { none() }
|
||||
|
||||
override deprecated predicate deletes(Variable v) { none() }
|
||||
|
||||
/* We ought to override uses as well, but that has
|
||||
* a serious performance impact.
|
||||
deprecated predicate uses(Variable v) { none() }
|
||||
*/
|
||||
}
|
||||
@@ -97,6 +97,12 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
||||
/* Classes aren't usually iterable, but can e.g. Enums */
|
||||
override ObjectInternal getIterNext() { result = ObjectInternal::unknown() }
|
||||
|
||||
override predicate hasAttribute(string name) {
|
||||
this.getClassDeclaration().declaresAttribute(name)
|
||||
or
|
||||
Types::getBase(this, _).hasAttribute(name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Class representing Python source classes */
|
||||
|
||||
@@ -78,6 +78,7 @@ abstract class ConstantObjectInternal extends ObjectInternal {
|
||||
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private boolean callToBool(CallNode call, PointsToContext context) {
|
||||
PointsToInternal::pointsTo(call.getFunction(), context, ClassValue::bool(), _) and
|
||||
exists(ObjectInternal arg |
|
||||
|
||||
@@ -59,6 +59,18 @@ abstract class ModuleObjectInternal extends ObjectInternal {
|
||||
/* Modules aren't iterable */
|
||||
override ObjectInternal getIterNext() { none() }
|
||||
|
||||
/** Holds if this module "exports" name.
|
||||
* That is, does it define `name` in `__all__` or is
|
||||
* `__all__` not defined and `name` a global variable that does not start with "_"
|
||||
* This is the set of names imported by `from ... import *`.
|
||||
*/
|
||||
predicate exports(string name) {
|
||||
not this.(ModuleObjectInternal).attribute("__all__", _, _) and this.hasAttribute(name)
|
||||
and not name.charAt(0) = "_"
|
||||
or
|
||||
py_exports(this.getSourceModule(), name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A class representing built-in modules */
|
||||
@@ -209,6 +221,13 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this value has the attribute `name` */
|
||||
override predicate hasAttribute(string name) {
|
||||
this.getInitModule().hasAttribute(name)
|
||||
or
|
||||
exists(this.submodule(name))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A class representing Python modules */
|
||||
@@ -261,6 +280,24 @@ class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
|
||||
result = this.getSourceModule().getEntryNode()
|
||||
}
|
||||
|
||||
/** Holds if this value has the attribute `name` */
|
||||
override predicate hasAttribute(string name) {
|
||||
name = "__name__"
|
||||
or
|
||||
this.getSourceModule().(ImportTimeScope).definesName(name)
|
||||
or
|
||||
exists(ModuleObjectInternal mod, ImportStarNode imp |
|
||||
PointsToInternal::pointsTo(imp, _, mod, _) and
|
||||
imp.getScope() = this.getSourceModule() and
|
||||
mod.exports(name)
|
||||
)
|
||||
or
|
||||
exists(ObjectInternal defined |
|
||||
this.attribute(name, defined, _) and
|
||||
not defined instanceof UndefinedInternal
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A class representing a module that is missing from the DB, but inferred to exists from imports. */
|
||||
|
||||
@@ -94,6 +94,11 @@ class Value extends TObject {
|
||||
result = this.(ObjectInternal).getName()
|
||||
}
|
||||
|
||||
/** Holds if this value has the attribute `name` */
|
||||
predicate hasAttribute(string name) {
|
||||
this.(ObjectInternal).hasAttribute(name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** Class representing modules in the Python program
|
||||
@@ -111,10 +116,7 @@ class ModuleValue extends Value {
|
||||
* This is the set of names imported by `from ... import *`.
|
||||
*/
|
||||
predicate exports(string name) {
|
||||
not this.(ModuleObjectInternal).attribute("__all__", _, _) and exists(this.attr(name))
|
||||
and not name.charAt(0) = "_"
|
||||
or
|
||||
py_exports(this.getScope(), name)
|
||||
PointsTo::moduleExports(this, name)
|
||||
}
|
||||
|
||||
/** Gets the scope for this module, provided that it is a Python module. */
|
||||
|
||||
@@ -182,6 +182,11 @@ class ObjectInternal extends TObject {
|
||||
*/
|
||||
abstract ObjectInternal getIterNext();
|
||||
|
||||
/** Holds if this value has the attribute `name` */
|
||||
predicate hasAttribute(string name) {
|
||||
this.(ObjectInternal).attribute(name, _, _)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -166,6 +166,10 @@ module PointsTo {
|
||||
)
|
||||
}
|
||||
|
||||
cached predicate moduleExports(ModuleObjectInternal mod, string name) {
|
||||
InterModulePointsTo::moduleExportsBoolean(mod, name) = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cached module PointsToInternal {
|
||||
@@ -1244,39 +1248,63 @@ module Expressions {
|
||||
index = subscr.getIndex()
|
||||
}
|
||||
|
||||
/** Track bitwise expressions so we can handle integer flags and enums.
|
||||
* Tracking too many binary expressions is likely to kill performance.
|
||||
/** Tracking too many binary expressions is likely to kill performance, so just say anything other than addition or bitwise or is 'unknown'.
|
||||
*/
|
||||
pragma [noinline]
|
||||
predicate binaryPointsTo(BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode operand, ObjectInternal opvalue) {
|
||||
origin = b and
|
||||
exists(ControlFlowNode left, Operator op, ControlFlowNode right |
|
||||
b.operands(left, op, right)
|
||||
operand = genericBinaryOperand(b) and
|
||||
PointsToInternal::pointsTo(operand, context, opvalue, _) and
|
||||
value = ObjectInternal::unknown()
|
||||
}
|
||||
|
||||
private ControlFlowNode genericBinaryOperand(BinaryExprNode b) {
|
||||
exists(Operator op |
|
||||
b.operands(result, op, _)
|
||||
or
|
||||
b.operands(_, op, result)
|
||||
|
|
||||
not op instanceof BitOr and
|
||||
(operand = left or operand = right) and
|
||||
PointsToInternal::pointsTo(operand, context, opvalue, _) and
|
||||
(
|
||||
op instanceof Add and
|
||||
value = TUnknownInstance(opvalue.getClass())
|
||||
or
|
||||
not op instanceof Add and
|
||||
value = ObjectInternal::unknown()
|
||||
)
|
||||
not op instanceof Add
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
predicate addPointsTo(BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode operand, ObjectInternal opvalue) {
|
||||
origin = b and
|
||||
exists(Operator op |
|
||||
b.operands(operand, op, _)
|
||||
or
|
||||
b.operands(_, op, operand)
|
||||
|
|
||||
op instanceof Add and
|
||||
PointsToInternal::pointsTo(operand, context, opvalue, _) and
|
||||
value = TUnknownInstance(opvalue.getClass())
|
||||
)
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
predicate bitOrPointsTo(BinaryExprNode b, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode operand, ObjectInternal opvalue) {
|
||||
origin = b and
|
||||
exists(Operator op, ControlFlowNode other |
|
||||
b.operands(operand, op, other)
|
||||
or
|
||||
b.operands(other, op, operand)
|
||||
|
|
||||
op instanceof BitOr and
|
||||
exists(ObjectInternal lobj, ObjectInternal robj |
|
||||
PointsToInternal::pointsTo(left, context, lobj, _) and
|
||||
PointsToInternal::pointsTo(right, context, robj, _) and
|
||||
value = TInt(lobj.intValue().bitOr(robj.intValue()))
|
||||
|
|
||||
left = operand and opvalue = lobj
|
||||
or
|
||||
right = operand and opvalue = robj
|
||||
exists(ObjectInternal obj, int i1, int i2 |
|
||||
pointsToInt(operand, context, opvalue, i1) and
|
||||
pointsToInt(other, context, obj, i2) and
|
||||
value = TInt(i1.bitOr(i2))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate pointsToInt(ControlFlowNode n, PointsToContext context, ObjectInternal obj, int value) {
|
||||
PointsToInternal::pointsTo(n, context, obj, _) and
|
||||
value = obj.intValue()
|
||||
}
|
||||
|
||||
pragma [noinline]
|
||||
predicate unaryPointsTo(UnaryExprNode u, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode operand, ObjectInternal opvalue) {
|
||||
exists(Unaryop op |
|
||||
@@ -1518,6 +1546,10 @@ module Expressions {
|
||||
or
|
||||
subscriptPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
addPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
bitOrPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
binaryPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
or
|
||||
unaryPointsTo(expr, context, value, origin, subexpr, subvalue)
|
||||
|
||||
@@ -151,7 +151,7 @@ class ClassObject extends Object {
|
||||
|
||||
/** Whether this class has a attribute named `name`, either declared or inherited.*/
|
||||
predicate hasAttribute(string name) {
|
||||
Types::getMro(theClass()).getAnItem().getClassDeclaration().declaresAttribute(name)
|
||||
theClass().hasAttribute(name)
|
||||
}
|
||||
|
||||
/** Whether it is impossible to know all the attributes of this class. Usually because it is
|
||||
|
||||
@@ -128,6 +128,20 @@ class BottleRoutePointToExtension extends PointsToExtension {
|
||||
|
||||
/* Python 3.6+ regex module constants */
|
||||
|
||||
string short_flag(string flag) {
|
||||
(flag = "ASCII" or
|
||||
flag = "IGNORECASE" or
|
||||
flag = "LOCALE" or
|
||||
flag = "UNICODE" or
|
||||
flag = "MULTILINE" or
|
||||
flag = "TEMPLATE")
|
||||
and result = flag.prefix(1)
|
||||
or
|
||||
flag = "DOTALL" and result = "S"
|
||||
or
|
||||
flag = "VERBOSE" and result = "X"
|
||||
}
|
||||
|
||||
class ReModulePointToExtension extends PointsToExtension {
|
||||
|
||||
string name;
|
||||
@@ -139,9 +153,10 @@ class ReModulePointToExtension extends PointsToExtension {
|
||||
}
|
||||
|
||||
override predicate pointsTo(Context context, ObjectInternal value, ControlFlowNode origin) {
|
||||
exists(ModuleObjectInternal sre_constants, CfgOrigin orig |
|
||||
exists(ModuleObjectInternal sre_constants, CfgOrigin orig, string flag |
|
||||
(name = flag or name = short_flag(flag)) and
|
||||
sre_constants.getName() = "sre_constants" and
|
||||
sre_constants.attribute("SRE_FLAG_" + name, value, orig) and
|
||||
sre_constants.attribute("SRE_FLAG_" + flag, value, orig) and
|
||||
origin = orig.asCfgNodeOrHere(this)
|
||||
)
|
||||
and pointsTo_helper(context)
|
||||
|
||||
@@ -51,9 +51,8 @@ abstract class ModuleObject extends Object {
|
||||
result = this.getAttribute(name)
|
||||
}
|
||||
|
||||
|
||||
predicate hasAttribute(string name) {
|
||||
exists(theModule().attr(name))
|
||||
theModule().hasAttribute(name)
|
||||
}
|
||||
|
||||
predicate attributeRefersTo(string name, Object obj, ControlFlowNode origin) {
|
||||
|
||||
257
python/ql/test/LICENSE-PSF.md
Normal file
257
python/ql/test/LICENSE-PSF.md
Normal file
@@ -0,0 +1,257 @@
|
||||
Some test code is derived from code included in the CPython distribution.
|
||||
Its license is reproduced below.
|
||||
|
||||
A. HISTORY OF THE SOFTWARE
|
||||
==========================
|
||||
|
||||
Python was created in the early 1990s by Guido van Rossum at Stichting
|
||||
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
|
||||
as a successor of a language called ABC. Guido remains Python's
|
||||
principal author, although it includes many contributions from others.
|
||||
|
||||
In 1995, Guido continued his work on Python at the Corporation for
|
||||
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
|
||||
in Reston, Virginia where he released several versions of the
|
||||
software.
|
||||
|
||||
In May 2000, Guido and the Python core development team moved to
|
||||
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
|
||||
year, the PythonLabs team moved to Digital Creations, which became
|
||||
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
|
||||
https://www.python.org/psf/) was formed, a non-profit organization
|
||||
created specifically to own Python-related Intellectual Property.
|
||||
Zope Corporation was a sponsoring member of the PSF.
|
||||
|
||||
All Python releases are Open Source (see http://www.opensource.org for
|
||||
the Open Source Definition). Historically, most, but not all, Python
|
||||
releases have also been GPL-compatible; the table below summarizes
|
||||
the various releases.
|
||||
|
||||
Release Derived Year Owner GPL-
|
||||
from compatible? (1)
|
||||
|
||||
0.9.0 thru 1.2 1991-1995 CWI yes
|
||||
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
|
||||
1.6 1.5.2 2000 CNRI no
|
||||
2.0 1.6 2000 BeOpen.com no
|
||||
1.6.1 1.6 2001 CNRI yes (2)
|
||||
2.1 2.0+1.6.1 2001 PSF no
|
||||
2.0.1 2.0+1.6.1 2001 PSF yes
|
||||
2.1.1 2.1+2.0.1 2001 PSF yes
|
||||
2.1.2 2.1.1 2002 PSF yes
|
||||
2.1.3 2.1.2 2002 PSF yes
|
||||
2.2 and above 2.1.1 2001-now PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
(1) GPL-compatible doesn't mean that we're distributing Python under
|
||||
the GPL. All Python licenses, unlike the GPL, let you distribute
|
||||
a modified version without making your changes open source. The
|
||||
GPL-compatible licenses make it possible to combine Python with
|
||||
other software that is released under the GPL; the others don't.
|
||||
|
||||
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
|
||||
because its license has a choice of law clause. According to
|
||||
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
|
||||
is "not incompatible" with the GPL.
|
||||
|
||||
Thanks to the many outside volunteers who have worked under Guido's
|
||||
direction to make these releases possible.
|
||||
|
||||
|
||||
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
|
||||
===============================================================
|
||||
|
||||
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
||||
--------------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Python Software Foundation
|
||||
("PSF"), and the Individual or Organization ("Licensee") accessing and
|
||||
otherwise using this software ("Python") in source or binary form and
|
||||
its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, PSF hereby
|
||||
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
|
||||
analyze, test, perform and/or display publicly, prepare derivative works,
|
||||
distribute, and otherwise use Python alone or in any derivative version,
|
||||
provided, however, that PSF's License Agreement and PSF's notice of copyright,
|
||||
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python.
|
||||
|
||||
4. PSF is making Python available to Licensee on an "AS IS"
|
||||
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any
|
||||
relationship of agency, partnership, or joint venture between PSF and
|
||||
Licensee. This License Agreement does not grant permission to use PSF
|
||||
trademarks or trade name in a trademark sense to endorse or promote
|
||||
products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using Python, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
|
||||
-------------------------------------------
|
||||
|
||||
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
|
||||
|
||||
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
|
||||
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
|
||||
Individual or Organization ("Licensee") accessing and otherwise using
|
||||
this software in source or binary form and its associated
|
||||
documentation ("the Software").
|
||||
|
||||
2. Subject to the terms and conditions of this BeOpen Python License
|
||||
Agreement, BeOpen hereby grants Licensee a non-exclusive,
|
||||
royalty-free, world-wide license to reproduce, analyze, test, perform
|
||||
and/or display publicly, prepare derivative works, distribute, and
|
||||
otherwise use the Software alone or in any derivative version,
|
||||
provided, however, that the BeOpen Python License is retained in the
|
||||
Software, alone or in any derivative version prepared by Licensee.
|
||||
|
||||
3. BeOpen is making the Software available to Licensee on an "AS IS"
|
||||
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
|
||||
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
|
||||
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
5. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
6. This License Agreement shall be governed by and interpreted in all
|
||||
respects by the law of the State of California, excluding conflict of
|
||||
law provisions. Nothing in this License Agreement shall be deemed to
|
||||
create any relationship of agency, partnership, or joint venture
|
||||
between BeOpen and Licensee. This License Agreement does not grant
|
||||
permission to use BeOpen trademarks or trade names in a trademark
|
||||
sense to endorse or promote products or services of Licensee, or any
|
||||
third party. As an exception, the "BeOpen Python" logos available at
|
||||
http://www.pythonlabs.com/logos.html may be used according to the
|
||||
permissions granted on that web page.
|
||||
|
||||
7. By copying, installing or otherwise using the software, Licensee
|
||||
agrees to be bound by the terms and conditions of this License
|
||||
Agreement.
|
||||
|
||||
|
||||
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
|
||||
---------------------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the Corporation for National
|
||||
Research Initiatives, having an office at 1895 Preston White Drive,
|
||||
Reston, VA 20191 ("CNRI"), and the Individual or Organization
|
||||
("Licensee") accessing and otherwise using Python 1.6.1 software in
|
||||
source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, CNRI
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide
|
||||
license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python 1.6.1
|
||||
alone or in any derivative version, provided, however, that CNRI's
|
||||
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
|
||||
1995-2001 Corporation for National Research Initiatives; All Rights
|
||||
Reserved" are retained in Python 1.6.1 alone or in any derivative
|
||||
version prepared by Licensee. Alternately, in lieu of CNRI's License
|
||||
Agreement, Licensee may substitute the following text (omitting the
|
||||
quotes): "Python 1.6.1 is made available subject to the terms and
|
||||
conditions in CNRI's License Agreement. This Agreement together with
|
||||
Python 1.6.1 may be located on the Internet using the following
|
||||
unique, persistent identifier (known as a handle): 1895.22/1013. This
|
||||
Agreement may also be obtained from a proxy server on the Internet
|
||||
using the following URL: http://hdl.handle.net/1895.22/1013".
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python 1.6.1 or any part thereof, and wants to make
|
||||
the derivative work available to others as provided herein, then
|
||||
Licensee hereby agrees to include in any such work a brief summary of
|
||||
the changes made to Python 1.6.1.
|
||||
|
||||
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
|
||||
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
|
||||
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
|
||||
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
|
||||
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
|
||||
INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
|
||||
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
|
||||
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material
|
||||
breach of its terms and conditions.
|
||||
|
||||
7. This License Agreement shall be governed by the federal
|
||||
intellectual property law of the United States, including without
|
||||
limitation the federal copyright law, and, to the extent such
|
||||
U.S. federal law does not apply, by the law of the Commonwealth of
|
||||
Virginia, excluding Virginia's conflict of law provisions.
|
||||
Notwithstanding the foregoing, with regard to derivative works based
|
||||
on Python 1.6.1 that incorporate non-separable material that was
|
||||
previously distributed under the GNU General Public License (GPL), the
|
||||
law of the Commonwealth of Virginia shall govern this License
|
||||
Agreement only as to issues arising under or with respect to
|
||||
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
|
||||
License Agreement shall be deemed to create any relationship of
|
||||
agency, partnership, or joint venture between CNRI and Licensee. This
|
||||
License Agreement does not grant permission to use CNRI trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or
|
||||
services of Licensee, or any third party.
|
||||
|
||||
8. By clicking on the "ACCEPT" button where indicated, or by copying,
|
||||
installing or otherwise using Python 1.6.1, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
ACCEPT
|
||||
|
||||
|
||||
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
|
||||
--------------------------------------------------
|
||||
|
||||
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
|
||||
The Netherlands. All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose and without fee is hereby granted,
|
||||
provided that the above copyright notice appear in all copies and that
|
||||
both that copyright notice and this permission notice appear in
|
||||
supporting documentation, and that the name of Stichting Mathematisch
|
||||
Centrum or CWI not be used in advertising or publicity pertaining to
|
||||
distribution of the software without specific, written prior
|
||||
permission.
|
||||
|
||||
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
|
||||
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
||||
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
@@ -1,79 +0,0 @@
|
||||
| 8 | test | test | Truthy | test |
|
||||
| 10 | test | test | Truthy | test |
|
||||
| 14 | seq | seq | Truthy | test |
|
||||
| 17 | seq | seq | Truthy | test |
|
||||
| 21 | UnaryExpr | t1 | Falsey | test |
|
||||
| 24 | t1 | t1 | Truthy | test |
|
||||
| 25 | t1 | t1 | Truthy | test |
|
||||
| 26 | t2 | t2 | Truthy | test |
|
||||
| 29 | t2 | t2 | Truthy | test |
|
||||
| 30 | t2 | t2 | Truthy | test |
|
||||
| 31 | t3 | t3 | Truthy | test |
|
||||
| 31 | t4 | t4 | Truthy | test |
|
||||
| 32 | t3 | t3 | Truthy | test |
|
||||
| 33 | t3 | t3 | Truthy | test |
|
||||
| 34 | t3 | t3 | Truthy | test |
|
||||
| 35 | t4 | t4 | Truthy | test |
|
||||
| 36 | t5 | t5 | Truthy | test |
|
||||
| 36 | t6 | t6 | Truthy | test |
|
||||
| 37 | t5 | t5 | Truthy | test |
|
||||
| 38 | t5 | t5 | Truthy | test |
|
||||
| 39 | t6 | t6 | Truthy | test |
|
||||
| 40 | t6 | t6 | Truthy | test |
|
||||
| 43 | t1 | t1 | Truthy | test |
|
||||
| 44 | UnaryExpr | t2 | Falsey | test |
|
||||
| 47 | t1 | t1 | Truthy | test |
|
||||
| 48 | t2 | t2 | Truthy | test |
|
||||
| 49 | t2 | t2 | Truthy | test |
|
||||
| 51 | t2 | t2 | Truthy | test |
|
||||
| 52 | t2 | t2 | Truthy | test |
|
||||
| 55 | seq1 | seq1 | Truthy | test |
|
||||
| 57 | UnaryExpr | seq2 | Falsey | test |
|
||||
| 60 | seq1 | seq1 | Truthy | test |
|
||||
| 63 | seq2 | seq2 | Truthy | test |
|
||||
| 66 | seq3 | seq3 | Truthy | test |
|
||||
| 68 | UnaryExpr | seq4 | Falsey | test |
|
||||
| 88 | UnaryExpr | x | Falsey | test |
|
||||
| 90 | y | y | Truthy | test |
|
||||
| 93 | UnaryExpr | x | Falsey | test |
|
||||
| 95 | y | y | Truthy | test |
|
||||
| 99 | another_module | another_module | Truthy | assign |
|
||||
| 102 | UnaryExpr | another_module | Falsey | test |
|
||||
| 107 | UnaryExpr | t1 | Falsey | test |
|
||||
| 109 | t2 | t2 | Truthy | test |
|
||||
| 111 | t1 | t1 | Truthy | test |
|
||||
| 113 | UnaryExpr | t2 | Falsey | test |
|
||||
| 117 | UnaryExpr | test | Falsey | test |
|
||||
| 119 | UnaryExpr | test | Falsey | test |
|
||||
| 123 | m | m | Truthy | test |
|
||||
| 126 | m | m | Truthy | test |
|
||||
| 158 | Compare | ps | Is not None | test |
|
||||
| 160 | Compare | ps | Is None | test |
|
||||
| 172 | escapes | escapes | Is None | assign |
|
||||
| 177 | Compare | escapes | Is None | test |
|
||||
| 191 | true12 | true12 | == 0 | assign |
|
||||
| 195 | Compare | x | < 4 | test |
|
||||
| 197 | Compare | x | < 4 | test |
|
||||
| 201 | Compare | x | < 4 | test |
|
||||
| 203 | UnaryExpr | x | < 4 | test |
|
||||
| 207 | Compare | x | < 4 | test |
|
||||
| 209 | Compare | x | < 4 | test |
|
||||
| 215 | x | x | Truthy | test |
|
||||
| 215 | y | y | Truthy | test |
|
||||
| 217 | x | x | Truthy | test |
|
||||
| 217 | y | y | Truthy | test |
|
||||
| 219 | x | x | Truthy | test |
|
||||
| 223 | y | y | Truthy | test |
|
||||
| 229 | k | k | Falsey | assign |
|
||||
| 230 | k | k | Truthy | test |
|
||||
| 237 | k | k | == 3 | assign |
|
||||
| 238 | k | k | Truthy | test |
|
||||
| 245 | k | k | Is None | assign |
|
||||
| 246 | k | k | Truthy | test |
|
||||
| 253 | a | a | Truthy | test |
|
||||
| 254 | k | k | Truthy | assign |
|
||||
| 256 | k | k | Falsey | assign |
|
||||
| 257 | k | k | Truthy | test |
|
||||
| 264 | var | var | Truthy | assign |
|
||||
| 266 | var | var | Falsey | assign |
|
||||
| 267 | var | var | Truthy | test |
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.Pruning
|
||||
|
||||
from Pruner::Constraint c, SsaVariable var, Pruner::UnprunedCfgNode node, int line, string kind
|
||||
where line = node.getNode().getLocation().getStartLine() and line > 0 and
|
||||
(
|
||||
c = Pruner::constraintFromTest(var, node) and kind = "test"
|
||||
or
|
||||
c = Pruner::constraintFromAssignment(var, node) and kind = "assign"
|
||||
)
|
||||
select line, node.getNode().toString(), var.getId(), c, kind
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
| 10 | test | 11 | count |
|
||||
| 24 | t1 | 24 | count |
|
||||
| 25 | t1 | 26 | t2 |
|
||||
| 29 | t2 | 30 | BoolExpr |
|
||||
| 30 | t2 | 30 | count |
|
||||
| 37 | t5 | 37 | count |
|
||||
| 38 | t5 | 39 | BoolExpr |
|
||||
| 39 | t6 | 39 | count |
|
||||
| 40 | t6 | 20 | Function boolop |
|
||||
| 47 | t1 | 48 | BoolExpr |
|
||||
| 47 | t1 | 51 | BoolExpr |
|
||||
| 48 | t2 | 48 | count |
|
||||
| 49 | t2 | 42 | Function with_splitting |
|
||||
| 51 | t2 | 51 | count |
|
||||
| 52 | t2 | 42 | Function with_splitting |
|
||||
| 93 | UnaryExpr | 94 | count |
|
||||
| 95 | y | 96 | count |
|
||||
| 102 | UnaryExpr | 103 | count |
|
||||
| 111 | t1 | 113 | t2 |
|
||||
| 113 | UnaryExpr | 106 | Function negated_conditional_live |
|
||||
| 119 | UnaryExpr | 120 | count |
|
||||
| 160 | Compare | 161 | count |
|
||||
| 160 | Compare | 163 | count |
|
||||
| 197 | Compare | 198 | count |
|
||||
| 203 | UnaryExpr | 204 | count |
|
||||
| 209 | Compare | 210 | count |
|
||||
| 217 | x | 217 | UnaryExpr |
|
||||
| 217 | x | 217 | y |
|
||||
| 217 | y | 217 | UnaryExpr |
|
||||
| 219 | x | 220 | count |
|
||||
| 219 | x | 222 | count |
|
||||
| 223 | y | 224 | count |
|
||||
| 223 | y | 226 | count |
|
||||
| 230 | k | 231 | count |
|
||||
| 238 | k | 241 | count |
|
||||
| 246 | k | 247 | count |
|
||||
| 257 | k | 258 | count |
|
||||
| 257 | k | 259 | Pass |
|
||||
| 267 | var | 268 | count |
|
||||
| 267 | var | 269 | Pass |
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.Pruning
|
||||
|
||||
from Pruner::UnprunedBasicBlock pred, Pruner::UnprunedBasicBlock succ, int line1, int line2
|
||||
where Pruner::contradictoryEdge(pred, succ) and
|
||||
line1 = pred.last().getNode().getLocation().getStartLine() and
|
||||
line2 = succ.first().getNode().getLocation().getStartLine() and
|
||||
line1 > 0
|
||||
select line1, pred.last().getNode().toString(), line2, succ.first().getNode().toString()
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
| 10 | test | 11 | count |
|
||||
| 11 | count | 7 | Function conditional_dead |
|
||||
| 24 | count | 25 | BoolExpr |
|
||||
| 24 | t1 | 24 | count |
|
||||
| 25 | t1 | 26 | t2 |
|
||||
| 29 | t2 | 30 | BoolExpr |
|
||||
| 30 | count | 31 | BoolExpr |
|
||||
| 30 | t2 | 30 | count |
|
||||
| 37 | count | 38 | BoolExpr |
|
||||
| 37 | t5 | 37 | count |
|
||||
| 38 | t5 | 39 | BoolExpr |
|
||||
| 39 | count | 40 | BoolExpr |
|
||||
| 39 | t6 | 39 | count |
|
||||
| 40 | t6 | 20 | Function boolop |
|
||||
| 47 | t1 | 48 | BoolExpr |
|
||||
| 47 | t1 | 51 | BoolExpr |
|
||||
| 48 | count | 49 | BoolExpr |
|
||||
| 48 | t2 | 48 | count |
|
||||
| 48 | t2 | 49 | BoolExpr |
|
||||
| 49 | count | 42 | Function with_splitting |
|
||||
| 49 | t2 | 42 | Function with_splitting |
|
||||
| 49 | t2 | 49 | count |
|
||||
| 51 | count | 52 | BoolExpr |
|
||||
| 51 | t2 | 51 | count |
|
||||
| 51 | t2 | 52 | BoolExpr |
|
||||
| 52 | count | 42 | Function with_splitting |
|
||||
| 52 | t2 | 42 | Function with_splitting |
|
||||
| 52 | t2 | 52 | count |
|
||||
| 93 | UnaryExpr | 94 | count |
|
||||
| 94 | count | 95 | y |
|
||||
| 95 | y | 96 | count |
|
||||
| 96 | count | 99 | ImportExpr |
|
||||
| 102 | UnaryExpr | 103 | count |
|
||||
| 103 | count | 106 | FunctionExpr |
|
||||
| 111 | t1 | 113 | t2 |
|
||||
| 113 | UnaryExpr | 106 | Function negated_conditional_live |
|
||||
| 119 | UnaryExpr | 120 | count |
|
||||
| 120 | count | 116 | Function negated_conditional_dead |
|
||||
| 130 | None | 131 | count |
|
||||
| 132 | UnaryExpr | 133 | count |
|
||||
| 132 | UnaryExpr | 134 | False |
|
||||
| 133 | count | 134 | False |
|
||||
| 134 | False | 135 | count |
|
||||
| 134 | False | 137 | count |
|
||||
| 138 | True | 139 | count |
|
||||
| 138 | True | 141 | count |
|
||||
| 139 | count | 142 | IntegerLiteral |
|
||||
| 141 | count | 142 | IntegerLiteral |
|
||||
| 142 | IntegerLiteral | 143 | count |
|
||||
| 142 | IntegerLiteral | 145 | count |
|
||||
| 143 | count | 146 | IntegerLiteral |
|
||||
| 145 | count | 146 | IntegerLiteral |
|
||||
| 146 | UnaryExpr | 147 | count |
|
||||
| 146 | UnaryExpr | 149 | count |
|
||||
| 147 | count | 151 | False |
|
||||
| 149 | count | 151 | False |
|
||||
| 151 | UnaryExpr | 152 | count |
|
||||
| 151 | UnaryExpr | 153 | False |
|
||||
| 152 | count | 153 | False |
|
||||
| 153 | UnaryExpr | 129 | Function prune_const_branches |
|
||||
| 153 | UnaryExpr | 154 | count |
|
||||
| 154 | count | 129 | Function prune_const_branches |
|
||||
| 160 | Compare | 161 | count |
|
||||
| 160 | Compare | 163 | count |
|
||||
| 161 | count | 157 | Function attribute_lookup_cannot_effect_comparisons_with_immutable_constants |
|
||||
| 163 | count | 157 | Function attribute_lookup_cannot_effect_comparisons_with_immutable_constants |
|
||||
| 197 | Compare | 198 | count |
|
||||
| 198 | count | 194 | Function inequality1 |
|
||||
| 203 | UnaryExpr | 204 | count |
|
||||
| 204 | count | 200 | Function inequality2 |
|
||||
| 209 | Compare | 210 | count |
|
||||
| 210 | count | 206 | Function reversed_inequality |
|
||||
| 217 | x | 217 | UnaryExpr |
|
||||
| 217 | x | 217 | y |
|
||||
| 217 | y | 217 | UnaryExpr |
|
||||
| 219 | x | 220 | count |
|
||||
| 219 | x | 222 | count |
|
||||
| 220 | count | 223 | y |
|
||||
| 222 | count | 223 | y |
|
||||
| 223 | y | 224 | count |
|
||||
| 223 | y | 226 | count |
|
||||
| 224 | count | 214 | Function split_bool1 |
|
||||
| 226 | count | 214 | Function split_bool1 |
|
||||
| 230 | k | 231 | count |
|
||||
| 231 | count | 234 | Pass |
|
||||
| 238 | k | 241 | count |
|
||||
| 241 | count | 242 | Pass |
|
||||
| 246 | k | 247 | count |
|
||||
| 247 | count | 250 | Pass |
|
||||
| 257 | k | 258 | count |
|
||||
| 257 | k | 259 | Pass |
|
||||
| 258 | count | 259 | Pass |
|
||||
| 267 | var | 268 | count |
|
||||
| 267 | var | 269 | Pass |
|
||||
| 268 | count | 269 | Pass |
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.Pruning
|
||||
|
||||
from Pruner::UnprunedCfgNode pred, Pruner::UnprunedCfgNode succ, int line1, int line2
|
||||
where Pruner::unreachableEdge(pred, succ) and
|
||||
line1 = pred.getNode().getLocation().getStartLine() and
|
||||
line2 = succ.getNode().getLocation().getStartLine() and
|
||||
line1 > 0
|
||||
select line1, pred.getNode().toString(), line2, succ.getNode().toString()
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
| 130 | None | 131 | count |
|
||||
| 132 | UnaryExpr | 134 | False |
|
||||
| 134 | False | 135 | count |
|
||||
| 138 | True | 141 | count |
|
||||
| 142 | IntegerLiteral | 143 | count |
|
||||
| 146 | UnaryExpr | 149 | count |
|
||||
| 151 | UnaryExpr | 152 | count |
|
||||
| 153 | UnaryExpr | 129 | Function prune_const_branches |
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
import python
|
||||
|
||||
import semmle.python.Pruning
|
||||
|
||||
from Pruner::UnprunedBasicBlock pred, Pruner::UnprunedBasicBlock succ, int line1, int line2
|
||||
where Pruner::simplyDead(pred, succ) and
|
||||
line1 = pred.last().getNode().getLocation().getStartLine() and
|
||||
line2 = succ.first().getNode().getLocation().getStartLine() and
|
||||
line1 > 0
|
||||
select line1, pred.last().getNode().toString(), line2, succ.first().getNode().toString()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
semmle-extractor-options: --dont-prune-graph
|
||||
optimize: true
|
||||
@@ -1,64 +0,0 @@
|
||||
| 5 | 0 |
|
||||
| 11 | 0 |
|
||||
| 18 | 1 |
|
||||
| 24 | 0 |
|
||||
| 25 | 1 |
|
||||
| 29 | 1 |
|
||||
| 30 | 0 |
|
||||
| 32 | 1 |
|
||||
| 33 | 1 |
|
||||
| 34 | 1 |
|
||||
| 35 | 1 |
|
||||
| 37 | 0 |
|
||||
| 38 | 1 |
|
||||
| 39 | 0 |
|
||||
| 40 | 1 |
|
||||
| 48 | 0 |
|
||||
| 49 | 1 |
|
||||
| 51 | 1 |
|
||||
| 52 | 1 |
|
||||
| 61 | 1 |
|
||||
| 64 | 1 |
|
||||
| 94 | 0 |
|
||||
| 96 | 0 |
|
||||
| 103 | 0 |
|
||||
| 112 | 1 |
|
||||
| 114 | 1 |
|
||||
| 120 | 0 |
|
||||
| 127 | 1 |
|
||||
| 131 | 0 |
|
||||
| 133 | 1 |
|
||||
| 135 | 0 |
|
||||
| 137 | 1 |
|
||||
| 139 | 1 |
|
||||
| 141 | 0 |
|
||||
| 143 | 0 |
|
||||
| 145 | 1 |
|
||||
| 147 | 1 |
|
||||
| 149 | 0 |
|
||||
| 152 | 0 |
|
||||
| 154 | 1 |
|
||||
| 161 | 1 |
|
||||
| 163 | 1 |
|
||||
| 176 | 1 |
|
||||
| 178 | 1 |
|
||||
| 180 | 1 |
|
||||
| 184 | 1 |
|
||||
| 186 | 1 |
|
||||
| 189 | 1 |
|
||||
| 192 | 1 |
|
||||
| 198 | 0 |
|
||||
| 204 | 0 |
|
||||
| 210 | 0 |
|
||||
| 220 | 1 |
|
||||
| 222 | 1 |
|
||||
| 224 | 1 |
|
||||
| 226 | 1 |
|
||||
| 231 | 0 |
|
||||
| 233 | 1 |
|
||||
| 239 | 1 |
|
||||
| 241 | 0 |
|
||||
| 247 | 0 |
|
||||
| 249 | 1 |
|
||||
| 258 | 1 |
|
||||
| 268 | 1 |
|
||||
@@ -1,269 +0,0 @@
|
||||
#Test number of CFG nodes for each use of 'count'
|
||||
|
||||
def dead():
|
||||
return 0
|
||||
count
|
||||
|
||||
def conditional_dead(test):
|
||||
if test:
|
||||
return
|
||||
if test:
|
||||
count
|
||||
|
||||
def made_true(seq):
|
||||
if seq:
|
||||
return
|
||||
seq.append(1)
|
||||
if seq:
|
||||
count
|
||||
|
||||
def boolop(t1, t2, t3, t4, t5, t6):
|
||||
if not t1:
|
||||
return
|
||||
#bool(t1) must be True
|
||||
t1 or count
|
||||
t1 and count
|
||||
if t2:
|
||||
return
|
||||
#bool(t2) must be False
|
||||
t2 or count
|
||||
t2 and count
|
||||
if t3 or t4:
|
||||
t3 or count
|
||||
t3 and count
|
||||
t3 or count
|
||||
t4 and count
|
||||
if t5 and t6:
|
||||
t5 or count
|
||||
t5 and count
|
||||
t6 or count
|
||||
t6 and count
|
||||
|
||||
def with_splitting(t1, t2):
|
||||
if t1:
|
||||
if not t2:
|
||||
return
|
||||
#Cannot have bool(t1) be True and bool(t2) be False
|
||||
if t1:
|
||||
t2 or count #Unreachable
|
||||
t2 and count
|
||||
else:
|
||||
t2 or count
|
||||
t2 and count
|
||||
|
||||
def loops(seq1, seq2, seq3, seq4, seq5):
|
||||
if seq1:
|
||||
return
|
||||
if not seq2:
|
||||
return
|
||||
#bool(seq1) is False; bool(seq2) is True
|
||||
while seq1:
|
||||
count #This is unreachable, but the pop below forces us to be conservative.
|
||||
seq1.pop()
|
||||
while seq2:
|
||||
count
|
||||
seq2.pop()
|
||||
if seq3:
|
||||
return
|
||||
if not seq4:
|
||||
return
|
||||
#bool(seq3) is False; bool(seq4) is True
|
||||
#for var in seq3:
|
||||
# count #This is unreachable, but we cannot infer this yet.
|
||||
# print(var)
|
||||
#for var in seq4:
|
||||
# count
|
||||
# print(var)
|
||||
##seq5 false then made true
|
||||
#if seq5:
|
||||
# return
|
||||
#seq5.append(1)
|
||||
#for var in seq5:
|
||||
# count
|
||||
# print(var)
|
||||
|
||||
#Logic does not apply to global variables in calls,
|
||||
#as they may be changed from true to false externally.
|
||||
from some_module import x, y
|
||||
if not x:
|
||||
raise Exception()
|
||||
if y:
|
||||
raise Exception()
|
||||
make_a_call()
|
||||
if not x:
|
||||
count
|
||||
if y:
|
||||
count
|
||||
|
||||
# However, modules are always true -- Which is important.
|
||||
import another_module
|
||||
|
||||
make_a_call()
|
||||
if not another_module:
|
||||
count
|
||||
|
||||
|
||||
def negated_conditional_live(t1, t2):
|
||||
if not t1:
|
||||
return
|
||||
if t2:
|
||||
return
|
||||
if t1:
|
||||
count
|
||||
if not t2:
|
||||
count
|
||||
|
||||
def negated_conditional_dead(test):
|
||||
if not test:
|
||||
return
|
||||
if not test:
|
||||
count
|
||||
|
||||
def made_true2(m):
|
||||
if m:
|
||||
return
|
||||
del m['a']
|
||||
if m:
|
||||
count
|
||||
|
||||
def prune_const_branches():
|
||||
if None:
|
||||
count
|
||||
if not None:
|
||||
count
|
||||
if False:
|
||||
count
|
||||
else:
|
||||
count
|
||||
if True:
|
||||
count
|
||||
else:
|
||||
count
|
||||
if 0:
|
||||
count
|
||||
else:
|
||||
count
|
||||
if -4:
|
||||
count
|
||||
else:
|
||||
count
|
||||
#Muliptle nots
|
||||
if not not False:
|
||||
count
|
||||
if not not not False:
|
||||
count
|
||||
|
||||
#ODASA-6794
|
||||
def attribute_lookup_cannot_effect_comparisons_with_immutable_constants(ps):
|
||||
if ps is not None:
|
||||
ps_clamped = ps.clamp()
|
||||
if ps is None:
|
||||
count
|
||||
else:
|
||||
count
|
||||
|
||||
def func():
|
||||
global escapes
|
||||
so_something()
|
||||
escapes = True
|
||||
|
||||
#Don't prune on `escapes` as it escapes.
|
||||
if __name__ == '__main__':
|
||||
escapes = None # global
|
||||
try:
|
||||
func()
|
||||
except Exception as err:
|
||||
count
|
||||
if escapes is None:
|
||||
count
|
||||
else:
|
||||
count
|
||||
|
||||
def func2():
|
||||
while 1:
|
||||
count
|
||||
if cond12:
|
||||
count
|
||||
try:
|
||||
true12()
|
||||
count
|
||||
except IOError:
|
||||
true12 = 0
|
||||
count
|
||||
|
||||
def inequality1(x):
|
||||
if x < 4:
|
||||
return
|
||||
if x < 4:
|
||||
count
|
||||
|
||||
def inequality2(x):
|
||||
if x < 4:
|
||||
return
|
||||
if not x >= 4:
|
||||
count
|
||||
|
||||
def reversed_inequality(x):
|
||||
if x < 4:
|
||||
return
|
||||
if 4 > x:
|
||||
count
|
||||
|
||||
|
||||
#Splittings with boolean expressions:
|
||||
def split_bool1(x=None,y=None):
|
||||
if x and y:
|
||||
raise
|
||||
if not (x or y):
|
||||
raise
|
||||
if x:
|
||||
count
|
||||
else:
|
||||
count
|
||||
if y:
|
||||
count
|
||||
else:
|
||||
count
|
||||
|
||||
def prune_on_constant1():
|
||||
k = False
|
||||
if k:
|
||||
count
|
||||
else:
|
||||
count
|
||||
pass
|
||||
|
||||
def prune_on_constant2():
|
||||
k = 3
|
||||
if k:
|
||||
count
|
||||
else:
|
||||
count
|
||||
pass
|
||||
|
||||
def prune_on_constant3():
|
||||
k = None
|
||||
if k:
|
||||
count
|
||||
else:
|
||||
count
|
||||
pass
|
||||
|
||||
def prune_on_constant_in_test(a, b):
|
||||
if a:
|
||||
k = True
|
||||
else:
|
||||
k = False
|
||||
if k:
|
||||
count
|
||||
pass
|
||||
|
||||
def prune_on_constant_in_try():
|
||||
try:
|
||||
import foo
|
||||
var = True
|
||||
except:
|
||||
var = False
|
||||
if var:
|
||||
count
|
||||
pass
|
||||
@@ -1,5 +0,0 @@
|
||||
import python
|
||||
|
||||
from Name n
|
||||
where n.getId() = "count"
|
||||
select n.getLocation().getStartLine(), count(n.getAFlowNode())
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=3 --dont-prune-graph
|
||||
semmle-extractor-options: --max-import-depth=3
|
||||
optimize: true
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
semmle-extractor-options: --max-import-depth=4 --dont-prune-graph
|
||||
semmle-extractor-options: --max-import-depth=4
|
||||
optimize: true
|
||||
|
||||
@@ -7,3 +7,4 @@
|
||||
| 50 | VERBOSE |
|
||||
| 51 | UNICODE |
|
||||
| 52 | UNICODE |
|
||||
| 64 | MULTILINE |
|
||||
|
||||
@@ -60,3 +60,5 @@ re.compile(r'(?:(?P<n1>^(?:|x)))')
|
||||
|
||||
#Misparsed on LGTM
|
||||
re.compile(r"\[(?P<txt>[^[]*)\]\((?P<uri>[^)]*)")
|
||||
|
||||
re.compile("", re.M) # ODASA-8056
|
||||
|
||||
12
python/ql/test/query-tests/Statements/unreachable/imports.py
Normal file
12
python/ql/test/query-tests/Statements/unreachable/imports.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
try:
|
||||
import __builtin__
|
||||
except ImportError:
|
||||
import builtins as __builtin__
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| decorated_exports.py:3:33:3:45 | Str | The name 'not_defined' is exported by __all__ but is not defined. |
|
||||
| exports.py:1:57:1:64 | Str | The name 'nosuch' is exported by __all__ but is not defined. |
|
||||
@@ -0,0 +1 @@
|
||||
Variables/UndefinedExport.ql
|
||||
@@ -4,3 +4,5 @@
|
||||
| UndefinedGlobal.py:123:5:123:7 | ug3 | This use of global variable 'ug3' may be undefined. |
|
||||
| UninitializedLocal.py:5:13:5:15 | ug1 | This use of global variable 'ug1' may be undefined. |
|
||||
| UninitializedLocal.py:22:9:22:11 | ug1 | This use of global variable 'ug1' may be undefined. |
|
||||
| decorated_exports.py:10:2:10:19 | undotted_decorator | This use of global variable 'undotted_decorator' may be undefined. |
|
||||
| decorated_exports.py:14:2:14:13 | not_imported | This use of global variable 'not_imported' may be undefined. |
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import dotted
|
||||
|
||||
__all__ = ["foo", "bar", "baz", "not_defined"]
|
||||
|
||||
|
||||
@dotted.decorator
|
||||
def foo():
|
||||
pass
|
||||
|
||||
@undotted_decorator
|
||||
def bar():
|
||||
pass
|
||||
|
||||
@not_imported.but_dotted
|
||||
def baz():
|
||||
pass
|
||||
19
python/ql/test/query-tests/Variables/undefined/exports.py
Normal file
19
python/ql/test/query-tests/Variables/undefined/exports.py
Normal file
@@ -0,0 +1,19 @@
|
||||
__all__ = ["foo", "bar", "baz", "quux", "blat", "frob", "nosuch", "i_got_it_elsewhere"]
|
||||
|
||||
with open("foo.txt") as f:
|
||||
foo = f.read()
|
||||
|
||||
b = open("bar.txt")
|
||||
bar = b.read()
|
||||
|
||||
baz = open("baz.txt")
|
||||
|
||||
from unknown_module import unknown_value
|
||||
|
||||
quux = 5 + unknown_value
|
||||
|
||||
blat = str(5)
|
||||
|
||||
frob = "5"
|
||||
|
||||
from unknown_module import i_got_it_elsewhere
|
||||
@@ -0,0 +1,3 @@
|
||||
__all__ = ["foo"]
|
||||
|
||||
from unknown_module import *
|
||||
Reference in New Issue
Block a user