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