@@ -1665,3 +1665,311 @@ class DataFlowSecondLevelScope extends TDataFlowSecondLevelScope {
16651665
16661666/** Gets the second-level scope containing the node `n`, if any. */
16671667DataFlowSecondLevelScope 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+ }
0 commit comments