Skip to content

Commit 96d69ca

Browse files
committed
Merge branch 'main' into redsun82/lfs
2 parents daea674 + f7113e0 commit 96d69ca

File tree

29 files changed

+794
-688
lines changed

29 files changed

+794
-688
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,3 +1665,311 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
16651665

16661666
/** Gets the second-level scope containing the node `n`, if any. */
16671667
DataFlowSecondLevelScope getSecondLevelScope(Node n) { result.getANode() = n }
1668+
1669+
/**
1670+
* Module that defines flow through iterators.
1671+
* For example,
1672+
* ```cpp
1673+
* auto it = v.begin();
1674+
* *it = source();
1675+
* ...
1676+
* sink(v[0]);
1677+
* ```
1678+
*/
1679+
module IteratorFlow {
1680+
private import codeql.ssa.Ssa as SsaImpl
1681+
private import semmle.code.cpp.models.interfaces.Iterator as Interface
1682+
private import semmle.code.cpp.models.implementations.Iterator as Impl
1683+
1684+
/**
1685+
* A variable of some type that can produce an iterator.
1686+
*/
1687+
class SourceVariable extends Ssa::SourceVariable {
1688+
SourceVariable() {
1689+
exists(Interface::GetIteratorFunction gets, Cpp::FunctionInput input, int i |
1690+
input.isParameterDerefOrQualifierObject(i) and
1691+
gets.getsIterator(input, _)
1692+
|
1693+
this.getType().stripType() = gets.getParameter(i).getType().stripType()
1694+
or
1695+
i = -1 and
1696+
this.getType().stripType() = gets.getDeclaringType()
1697+
)
1698+
}
1699+
}
1700+
1701+
private module SsaInput implements SsaImpl::InputSig<Location> {
1702+
import Ssa::InputSigCommon
1703+
1704+
class SourceVariable = IteratorFlow::SourceVariable;
1705+
1706+
/** A call to function that dereferences an iterator. */
1707+
private class IteratorPointerDereferenceCall extends CallInstruction {
1708+
IteratorPointerDereferenceCall() {
1709+
this.getStaticCallTarget() instanceof Impl::IteratorPointerDereferenceOperator
1710+
}
1711+
}
1712+
1713+
/** A call to a function that obtains an iterator. */
1714+
private class GetsIteratorCall extends CallInstruction {
1715+
GetsIteratorCall() { this.getStaticCallTarget() instanceof Impl::GetIteratorFunction }
1716+
}
1717+
1718+
/** A call to `operator++` or `operator--` on an iterator. */
1719+
private class IteratorCrementCall extends CallInstruction {
1720+
IteratorCrementCall() { this.getStaticCallTarget() instanceof Impl::IteratorCrementOperator }
1721+
}
1722+
1723+
/**
1724+
* Gets an ultimate definition of `def`.
1725+
*
1726+
* Note: Unlike `def.getAnUltimateDefinition()` this predicate also
1727+
* traverses back through iterator increment and decrement operations.
1728+
*/
1729+
private Ssa::Def getAnUltimateDefinition(Ssa::Def def) {
1730+
result = def.getAnUltimateDefinition()
1731+
or
1732+
exists(IRBlock bb, int i, IteratorCrementCall crementCall, Ssa::SourceVariable sv |
1733+
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
1734+
sv = def.getSourceVariable() and
1735+
bb.getInstruction(i) = crementCall and
1736+
Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i)
1737+
)
1738+
}
1739+
1740+
/**
1741+
* Holds if `write` is an instruction that writes to address `address`
1742+
*/
1743+
private predicate isIteratorWrite(Instruction write, Operand address) {
1744+
exists(Ssa::DefImpl writeDef, IRBlock bb, int i |
1745+
writeDef.hasIndexInBlock(bb, i, _) and
1746+
bb.getInstruction(i) = write and
1747+
address = writeDef.getAddressOperand()
1748+
)
1749+
}
1750+
1751+
/**
1752+
* Holds if `writeToDeref` is a write to an iterator that was obtained
1753+
* by `beginCall`. That is, the following instruction sequence holds:
1754+
* ```cpp
1755+
* it = container.begin(); // or a similar iterator-obtaining function call
1756+
* ...
1757+
* *it = value;
1758+
* ```
1759+
*/
1760+
private predicate isIteratorStoreInstruction(
1761+
GetsIteratorCall beginCall, Instruction writeToDeref
1762+
) {
1763+
exists(
1764+
StoreInstruction beginStore, IRBlock bbStar, int iStar, Ssa::Def def,
1765+
IteratorPointerDereferenceCall starCall, Ssa::Def ultimate, Operand address
1766+
|
1767+
isIteratorWrite(writeToDeref, address) and
1768+
operandForFullyConvertedCall(address, starCall) and
1769+
bbStar.getInstruction(iStar) = starCall and
1770+
Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and
1771+
ultimate = getAnUltimateDefinition*(def) and
1772+
beginStore = ultimate.getValue().asInstruction() and
1773+
operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)
1774+
)
1775+
}
1776+
1777+
/**
1778+
* Holds if `(bb, i)` contains a write to an iterator that may have been obtained
1779+
* by calling `begin` (or related functions) on the variable `v`.
1780+
*/
1781+
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
1782+
certain = false and
1783+
exists(GetsIteratorCall beginCall, Instruction writeToDeref, IRBlock bbQual, int iQual |
1784+
isIteratorStoreInstruction(beginCall, writeToDeref) and
1785+
bb.getInstruction(i) = writeToDeref and
1786+
bbQual.getInstruction(iQual) = beginCall and
1787+
Ssa::variableRead(bbQual, iQual, v, _)
1788+
)
1789+
}
1790+
1791+
/** Holds if `(bb, i)` reads the container variable `v`. */
1792+
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
1793+
Ssa::variableRead(bb, i, v, certain)
1794+
}
1795+
}
1796+
1797+
private module IteratorSsa = SsaImpl::Make<Location, SsaInput>;
1798+
1799+
cached
1800+
private newtype TSsaDef =
1801+
TDef(IteratorSsa::DefinitionExt def) or
1802+
TPhi(PhiNode phi)
1803+
1804+
abstract private class SsaDef extends TSsaDef {
1805+
/** Gets a textual representation of this element. */
1806+
string toString() { none() }
1807+
1808+
/** Gets the underlying non-phi definition or use. */
1809+
IteratorSsa::DefinitionExt asDef() { none() }
1810+
1811+
/** Gets the underlying phi node. */
1812+
PhiNode asPhi() { none() }
1813+
1814+
/** Gets the location of this element. */
1815+
abstract Location getLocation();
1816+
}
1817+
1818+
private class Def extends TDef, SsaDef {
1819+
IteratorSsa::DefinitionExt def;
1820+
1821+
Def() { this = TDef(def) }
1822+
1823+
final override IteratorSsa::DefinitionExt asDef() { result = def }
1824+
1825+
final override Location getLocation() { result = this.getImpl().getLocation() }
1826+
1827+
/** Gets the variable written to by this definition. */
1828+
final SourceVariable getSourceVariable() { result = def.getSourceVariable() }
1829+
1830+
override string toString() { result = def.toString() }
1831+
1832+
/**
1833+
* Holds if this definition (or use) has index `index` in block `block`,
1834+
* and is a definition (or use) of the variable `sv`.
1835+
*/
1836+
predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
1837+
def.definesAt(sv, block, index, _)
1838+
}
1839+
1840+
private Ssa::DefImpl getImpl() {
1841+
exists(IRBlock bb, int i |
1842+
this.hasIndexInBlock(bb, i, _) and
1843+
result.hasIndexInBlock(bb, i)
1844+
)
1845+
}
1846+
1847+
/** Gets the value written by this definition (i.e., the "right-hand side"). */
1848+
Node0Impl getValue() { result = this.getImpl().getValue() }
1849+
1850+
/** Gets the indirection index of this definition. */
1851+
int getIndirectionIndex() { result = this.getImpl().getIndirectionIndex() }
1852+
}
1853+
1854+
private class Phi extends TPhi, SsaDef {
1855+
PhiNode phi;
1856+
1857+
Phi() { this = TPhi(phi) }
1858+
1859+
final override PhiNode asPhi() { result = phi }
1860+
1861+
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
1862+
1863+
override string toString() { result = phi.toString() }
1864+
1865+
SsaIteratorNode getNode() { result.getIteratorFlowNode() = phi }
1866+
}
1867+
1868+
private class PhiNode extends IteratorSsa::DefinitionExt {
1869+
PhiNode() {
1870+
this instanceof IteratorSsa::PhiNode or
1871+
this instanceof IteratorSsa::PhiReadNode
1872+
}
1873+
1874+
SsaIteratorNode getNode() { result.getIteratorFlowNode() = this }
1875+
}
1876+
1877+
cached
1878+
private module IteratorSsaCached {
1879+
cached
1880+
predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) {
1881+
IteratorSsa::adjacentDefReadExt(_, sv, bb1, i1, bb2, i2)
1882+
or
1883+
exists(PhiNode phi |
1884+
IteratorSsa::lastRefRedefExt(_, sv, bb1, i1, phi) and
1885+
phi.definesAt(sv, bb2, i2, _)
1886+
)
1887+
}
1888+
1889+
cached
1890+
Node getAPriorDefinition(IteratorSsa::DefinitionExt next) {
1891+
exists(IRBlock bb, int i, SourceVariable sv, IteratorSsa::DefinitionExt def |
1892+
IteratorSsa::lastRefRedefExt(pragma[only_bind_into](def), pragma[only_bind_into](sv),
1893+
pragma[only_bind_into](bb), pragma[only_bind_into](i), next) and
1894+
nodeToDefOrUse(result, sv, bb, i, _)
1895+
)
1896+
}
1897+
}
1898+
1899+
/** The set of nodes necessary for iterator flow. */
1900+
class IteratorFlowNode instanceof PhiNode {
1901+
/** Gets a textual representation of this node. */
1902+
string toString() { result = super.toString() }
1903+
1904+
/** Gets the type of this node. */
1905+
DataFlowType getType() {
1906+
exists(Ssa::SourceVariable sv |
1907+
super.definesAt(sv, _, _, _) and
1908+
result = sv.getType()
1909+
)
1910+
}
1911+
1912+
/** Gets the `Declaration` that contains this block. */
1913+
Declaration getFunction() { result = super.getBasicBlock().getEnclosingFunction() }
1914+
1915+
/** Gets the locatino of this node. */
1916+
Location getLocation() { result = super.getBasicBlock().getLocation() }
1917+
}
1918+
1919+
private import IteratorSsaCached
1920+
1921+
private predicate defToNode(Node node, Def def, boolean uncertain) {
1922+
(
1923+
nodeHasOperand(node, def.getValue().asOperand(), def.getIndirectionIndex())
1924+
or
1925+
nodeHasInstruction(node, def.getValue().asInstruction(), def.getIndirectionIndex())
1926+
) and
1927+
uncertain = false
1928+
}
1929+
1930+
private predicate nodeToDefOrUse(
1931+
Node node, SourceVariable sv, IRBlock bb, int i, boolean uncertain
1932+
) {
1933+
exists(Def def |
1934+
def.hasIndexInBlock(bb, i, sv) and
1935+
defToNode(node, def, uncertain)
1936+
)
1937+
or
1938+
useToNode(bb, i, sv, node) and
1939+
uncertain = false
1940+
}
1941+
1942+
private predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) {
1943+
exists(PhiNode phi |
1944+
phi.definesAt(sv, bb, i, _) and
1945+
nodeTo = phi.getNode()
1946+
)
1947+
or
1948+
exists(Ssa::UseImpl use |
1949+
use.hasIndexInBlock(bb, i, sv) and
1950+
nodeTo = use.getNode()
1951+
)
1952+
}
1953+
1954+
/**
1955+
* Holds if `nodeFrom` flows to `nodeTo` in a single step.
1956+
*/
1957+
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
1958+
exists(
1959+
Node nFrom, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2, boolean uncertain
1960+
|
1961+
adjacentDefRead(bb1, i1, sv, bb2, i2) and
1962+
nodeToDefOrUse(nFrom, sv, bb1, i1, uncertain) and
1963+
useToNode(bb2, i2, sv, nodeTo)
1964+
|
1965+
if uncertain = true
1966+
then
1967+
nodeFrom =
1968+
[
1969+
nFrom,
1970+
getAPriorDefinition(any(IteratorSsa::DefinitionExt next | next.definesAt(sv, bb1, i1, _)))
1971+
]
1972+
else nFrom = nodeFrom
1973+
)
1974+
}
1975+
}

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ private newtype TIRDataFlowNode =
4646
Ssa::isModifiableByCall(operand, indirectionIndex)
4747
} or
4848
TSsaPhiNode(Ssa::PhiNode phi) or
49+
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
4950
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
5051
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
5152
} or
@@ -653,6 +654,30 @@ class SsaPhiNode extends Node, TSsaPhiNode {
653654
predicate isPhiRead() { phi.isPhiRead() }
654655
}
655656

657+
/**
658+
* INTERNAL: do not use.
659+
*
660+
* Dataflow nodes necessary for iterator flow
661+
*/
662+
class SsaIteratorNode extends Node, TSsaIteratorNode {
663+
IteratorFlow::IteratorFlowNode node;
664+
665+
SsaIteratorNode() { this = TSsaIteratorNode(node) }
666+
667+
/** Gets the phi node associated with this node. */
668+
IteratorFlow::IteratorFlowNode getIteratorFlowNode() { result = node }
669+
670+
override Declaration getEnclosingCallable() { result = this.getFunction() }
671+
672+
override Declaration getFunction() { result = node.getFunction() }
673+
674+
override DataFlowType getType() { result = node.getType() }
675+
676+
final override Location getLocationImpl() { result = node.getLocation() }
677+
678+
override string toStringImpl() { result = node.toString() }
679+
}
680+
656681
/**
657682
* INTERNAL: do not use.
658683
*
@@ -1190,11 +1215,11 @@ class UninitializedNode extends Node {
11901215
LocalVariable v;
11911216

11921217
UninitializedNode() {
1193-
exists(Ssa::Def def |
1218+
exists(Ssa::Def def, Ssa::SourceVariable sv |
11941219
def.getIndirectionIndex() = 0 and
11951220
def.getValue().asInstruction() instanceof UninitializedInstruction and
1196-
Ssa::nodeToDefOrUse(this, def, _) and
1197-
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
1221+
Ssa::defToNode(this, def, sv, _, _, _) and
1222+
v = sv.getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
11981223
)
11991224
}
12001225

@@ -2151,6 +2176,8 @@ private module Cached {
21512176
// Def-use/Use-use flow
21522177
Ssa::ssaFlow(nodeFrom, nodeTo)
21532178
or
2179+
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
2180+
or
21542181
// Operand -> Instruction flow
21552182
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
21562183
or

0 commit comments

Comments
 (0)