From f7c29e6bfbc3be47b2afdaf1f1c14158ffb0a19f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 15 Mar 2024 14:21:39 +0000 Subject: [PATCH 01/10] C++: Expose some previously private classes from our models so they can be used in queries. --- .../lib/semmle/code/cpp/models/implementations/Iterator.qll | 6 +++++- .../semmle/code/cpp/models/implementations/StdContainer.qll | 4 +++- .../lib/semmle/code/cpp/models/implementations/StdMap.qll | 4 +++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll index cafd9aeeef05..2c65ac57aeb8 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll @@ -438,7 +438,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe * A `begin` or `end` member function, or a related member function, that * returns an iterator. */ -private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction { +class BeginOrEndFunction extends MemberFunction { BeginOrEndFunction() { this.hasName([ "begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin", @@ -446,7 +446,11 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera ]) and this.getType().getUnspecifiedType() instanceof Iterator } +} +private class BeginOrEndFunctionModels extends BeginOrEndFunction, TaintFunction, + GetIteratorFunction +{ override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { input.isQualifierObject() and output.isReturnValue() diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll index 877dc5d3ac48..6fb17d80c5d2 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll @@ -253,13 +253,15 @@ private class StdSequenceContainerAssign extends TaintFunction { /** * The standard container functions `at` and `operator[]`. */ -private class StdSequenceContainerAt extends TaintFunction { +class StdSequenceContainerAt extends MemberFunction { StdSequenceContainerAt() { this.getClassAndName(["at", "operator[]"]) instanceof Array or this.getClassAndName(["at", "operator[]"]) instanceof Deque or this.getClassAndName(["at", "operator[]"]) instanceof Vector } +} +private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from qualifier to referenced return value input.isQualifierObject() and diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll index b6d869d7bea4..ce3c596f308a 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdMap.qll @@ -129,9 +129,11 @@ private class StdMapMerge extends TaintFunction { /** * The standard map functions `at` and `operator[]`. */ -private class StdMapAt extends TaintFunction { +class StdMapAt extends MemberFunction { StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap } +} +private class StdMapAtModels extends StdMapAt, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from qualifier to referenced return value input.isQualifierObject() and From 23cf99734af6ad72a0aa9f1bb83c61bdd3c3d379 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 15 Mar 2024 14:29:29 +0000 Subject: [PATCH 02/10] C++: Add a new experimental query ' cpp/iterator-to-expired-container'. --- .../CWE/CWE-416/IteratorToExpiredContainer.ql | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql new file mode 100644 index 000000000000..4c165d197eb4 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql @@ -0,0 +1,89 @@ +/** + * @name Iterator to expired container + * @description Using an iterator owned by a container whose lifetimes has expired may lead to unexpected behavior. + * @kind problem + * @precision high + * @id cpp/iterator-to-expired-container + * @problem.severity warning + * @tags reliability + * security + * external/cwe/cwe-416 + * external/cwe/cwe-664 + */ + +// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released +import cpp +import semmle.code.cpp.ir.IR +import semmle.code.cpp.dataflow.new.DataFlow +import semmle.code.cpp.models.implementations.StdContainer +import semmle.code.cpp.models.implementations.StdMap +import semmle.code.cpp.models.implementations.Iterator + +/** + * A configuration to track flow from a temporary variable to the qualifier of + * a destructor call + */ +module TempToDestructorConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable + } + + predicate isSink(DataFlow::Node sink) { + sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor + } +} + +module TempToDestructorFlow = DataFlow::Global; + +/** + * Gets a `DataFlow::Node` that represents a temporary that will be destroyed + * by a call to a destructor, or a `DataFlow::Node` that will transitively be + * destroyed by a call to a destructor. + * + * For the latter case, consider something like: + * ``` + * std::vector> get_2d_vector(); + * auto& v = get_2d_vector()[0]; + * ``` + * Given the above, this predicate returns the node corresponding + * to `get_2d_vector()[0]` since the temporary `get_2d_vector()` gets + * destroyed by a call to `std::vector>::~vector`, + * and thus the result of `get_2d_vector()[0]` is also an invalid reference. + */ +DataFlow::Node getADestroyedNode() { + exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() | + result = destroyedTemp.getNode() + or + exists(CallInstruction call | + result.asInstruction() = call and + DataFlow::localFlow(destroyedTemp.getNode(), + DataFlow::operandNode(call.getThisArgumentOperand())) + | + call.getStaticCallTarget() instanceof StdSequenceContainerAt or + call.getStaticCallTarget() instanceof StdMapAt + ) + ) +} + +predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) { + exists(CallInstruction call | + call = sink.asOperand().(ThisArgumentOperand).getCall() and + fc = call.getUnconvertedResultExpression() and + call.getStaticCallTarget() instanceof BeginOrEndFunction + ) +} + +/** + * Flow from any destroyed object to the qualifier of a `begin` call + */ +module DestroyedToBeginConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source = getADestroyedNode() } + + predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) } +} + +module DestroyedToBeginFlow = DataFlow::Global; + +from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd +where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd) +select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString() From 3a8db49573f6ff79bbb55bc98aafab7b5bd68ef1 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 15 Mar 2024 14:29:47 +0000 Subject: [PATCH 03/10] C++: Add tests for 'cpp/iterator-to-expired-container'. NOTE: This is with the yet-to-be-merged changes to the extractor and IR generation. --- .../IteratorToExpiredContainer.expected | 10 + .../CWE-416/IteratorToExpiredContainer.qlref | 1 + .../query-tests/Security/CWE/CWE-416/test.cpp | 719 ++++++++++++++++++ 3 files changed, 730 insertions(+) create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.qlref create mode 100644 cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected new file mode 100644 index 000000000000..39588f865bef --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected @@ -0,0 +1,10 @@ +| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to begin | call to begin | +| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to end | call to end | +| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to begin | call to begin | +| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to end | call to end | +| test.cpp:689:16:689:28 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:30:689:34 | call to begin | call to begin | +| test.cpp:689:45:689:57 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:59:689:61 | call to end | call to end | +| test.cpp:702:26:702:26 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:18:703:22 | call to begin | call to begin | +| test.cpp:702:26:702:26 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:35:703:37 | call to end | call to end | +| test.cpp:716:35:716:47 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:16:716:16 | call to begin | call to begin | +| test.cpp:716:35:716:47 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:16:716:16 | call to end | call to end | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.qlref new file mode 100644 index 000000000000..5f86bb26ff09 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp new file mode 100644 index 000000000000..c035fe203a86 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp @@ -0,0 +1,719 @@ + +typedef unsigned long size_t; + +template +struct remove_const { typedef T type; }; + +template +struct remove_const { typedef T type; }; + +// `remove_const_t` removes any `const` specifier from `T` +template +using remove_const_t = typename remove_const::type; + +template +struct remove_reference { typedef T type; }; + +template +struct remove_reference { typedef T type; }; + +template +struct remove_reference { typedef T type; }; + +// `remove_reference_t` removes any `&` from `T` +template +using remove_reference_t = typename remove_reference::type; + +template +struct decay_impl { + typedef T type; +}; + +template +struct decay_impl { + typedef T* type; +}; + +template +using decay_t = typename decay_impl>::type; + + +namespace std +{ + template constexpr T&& forward(remove_reference_t& t) noexcept; + template constexpr T&& forward(remove_reference_t&& t) noexcept; +} + +// --- iterator --- + +namespace std { + struct ptrdiff_t; + + template struct iterator_traits; + + template + struct iterator { + typedef Category iterator_category; + + iterator(); + iterator(iterator > const &other); // non-const -> const conversion constructor + + iterator &operator++(); + iterator operator++(int); + iterator &operator--(); + iterator operator--(int); + bool operator==(iterator other) const; + bool operator!=(iterator other) const; + reference_type operator*() const; + pointer_type operator->() const; + iterator operator+(int); + iterator operator-(int); + iterator &operator+=(int); + iterator &operator-=(int); + int operator-(iterator); + reference_type operator[](int); + }; + + struct input_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + + struct output_iterator_tag {}; + + template + class back_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr back_insert_iterator() noexcept = default; + constexpr explicit back_insert_iterator(Container& x); + back_insert_iterator& operator=(const typename Container::value_type& value); + back_insert_iterator& operator=(typename Container::value_type&& value); + back_insert_iterator& operator*(); + back_insert_iterator& operator++(); + back_insert_iterator operator++(int); + }; + + template + constexpr back_insert_iterator back_inserter(Container& x) { + return back_insert_iterator(x); + } + + template + class front_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr front_insert_iterator() noexcept = default; + constexpr explicit front_insert_iterator(Container& x); + constexpr front_insert_iterator& operator=(const typename Container::value_type& value); + constexpr front_insert_iterator& operator=(typename Container::value_type&& value); + constexpr front_insert_iterator& operator*(); + constexpr front_insert_iterator& operator++(); + constexpr front_insert_iterator operator++(int); + }; + template + constexpr front_insert_iterator front_inserter(Container& x) { + return front_insert_iterator(x); + } +} + +// --- string --- + +namespace std +{ + template struct char_traits; + + typedef size_t streamsize; + + template class allocator { + public: + allocator() throw(); + typedef size_t size_type; + }; + + template, class Allocator = allocator > + class basic_string { + public: + using value_type = charT; + using reference = value_type&; + using const_reference = const value_type&; + typedef typename Allocator::size_type size_type; + static const size_type npos = -1; + + explicit basic_string(const Allocator& a = Allocator()); + basic_string(const charT* s, const Allocator& a = Allocator()); + template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); + + const charT* c_str() const; + charT* data() noexcept; + size_t length() const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + void push_back(charT c); + + const charT& front() const; + charT& front(); + const charT& back() const; + charT& back(); + + const_reference operator[](size_type pos) const; + reference operator[](size_type pos); + const_reference at(size_type n) const; + reference at(size_type n); + template basic_string& operator+=(const T& t); + basic_string& operator+=(const charT* s); + basic_string& append(const basic_string& str); + basic_string& append(const charT* s); + basic_string& append(size_type n, charT c); + template basic_string& append(InputIterator first, InputIterator last); + basic_string& assign(const basic_string& str); + basic_string& assign(size_type n, charT c); + template basic_string& assign(InputIterator first, InputIterator last); + basic_string& insert(size_type pos, const basic_string& str); + basic_string& insert(size_type pos, size_type n, charT c); + basic_string& insert(size_type pos, const charT* s); + iterator insert(const_iterator p, size_type n, charT c); + template iterator insert(const_iterator p, InputIterator first, InputIterator last); + basic_string& replace(size_type pos1, size_type n1, const basic_string& str); + basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c); + size_type copy(charT* s, size_type n, size_type pos = 0) const; + void clear() noexcept; + basic_string substr(size_type pos = 0, size_type n = npos) const; + void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + }; + + template basic_string operator+(const basic_string& lhs, const basic_string& rhs); + template basic_string operator+(const basic_string& lhs, const charT* rhs); + + typedef basic_string string; +} + +// --- istring / ostream / stringstream --- + +namespace std +{ + template > + class basic_istream /*: virtual public basic_ios - not needed for this test */ { + public: + using char_type = charT; + using int_type = int; //typename traits::int_type; + + basic_istream& operator>>(int& n); + + int_type get(); + basic_istream& get(char_type& c); + basic_istream& get(char_type* s, streamsize n); + int_type peek(); + basic_istream& read (char_type* s, streamsize n); + streamsize readsome(char_type* s, streamsize n); + basic_istream& putback(char_type c); + basic_istream& unget(); + + basic_istream& getline(char_type* s, streamsize n); + basic_istream& getline(char_type* s, streamsize n, char_type delim); + }; + + template basic_istream& operator>>(basic_istream&, charT*); + template basic_istream& operator>>(basic_istream& is, basic_string& str); + + template basic_istream& getline(basic_istream& is, basic_string& str, charT delim); + template basic_istream& getline(basic_istream& is, basic_string& str); + + template > + class basic_ostream /*: virtual public basic_ios - not needed for this test */ { + public: + typedef charT char_type; + + basic_ostream& operator<<(int n); + + basic_ostream& put(char_type c); + basic_ostream& write(const char_type* s, streamsize n); + basic_ostream& flush(); + }; + + template basic_ostream& operator<<(basic_ostream&, const charT*); + template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); + + template> + class basic_iostream : public basic_istream, public basic_ostream { + public: + }; + + template, class Allocator = allocator> + class basic_stringstream : public basic_iostream { + public: + explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); + explicit basic_stringstream( const basic_string& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/); + basic_stringstream(const basic_stringstream& rhs) = delete; + basic_stringstream(basic_stringstream&& rhs); + basic_stringstream& operator=(const basic_stringstream& rhs) = delete; + basic_stringstream& operator=(basic_stringstream&& rhs); + + void swap(basic_stringstream& rhs); + + basic_string str() const; + void str(const basic_string& str); + }; + + typedef basic_istream istream; + typedef basic_ostream ostream; + extern istream cin; + extern ostream cout; + + using stringstream = basic_stringstream; +} + +// --- vector --- + +namespace std { + template> + class vector { + public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = unsigned int; + using iterator = std::iterator; + using const_iterator = std::iterator; + + vector() noexcept(noexcept(Allocator())); + vector(const std::vector&); + explicit vector(const Allocator&) noexcept; + explicit vector(size_type n, const Allocator& = Allocator()); + vector(size_type n, const T& value, const Allocator& = Allocator()); + template vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + ~vector(); + + vector& operator=(const vector& x); + vector& operator=(vector&& x) noexcept/*(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value)*/; + template void assign(InputIterator first, InputIterator last); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + void assign(size_type n, const T& u); + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + size_type size() const noexcept; + + reference operator[](size_type n); + const_reference operator[](size_type n) const; + const_reference at(size_type n) const; + reference at(size_type n); + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + T* data() noexcept; + const T* data() const noexcept; + + void push_back(const T& x); + void push_back(T&& x); + + iterator insert(const_iterator position, const T& x); + iterator insert(const_iterator position, T&& x); + iterator insert(const_iterator position, size_type n, const T& x); + template iterator insert(const_iterator position, InputIterator first, InputIterator last); + + template iterator emplace (const_iterator position, Args&&... args); + template void emplace_back (Args&&... args); + + void swap(vector&) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + + void clear() noexcept; + }; +} + +// --- make_shared / make_unique --- + +namespace std { + template + class shared_ptr { + public: + shared_ptr() noexcept; + explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(shared_ptr&&) noexcept; + + shared_ptr& operator=(const shared_ptr&) noexcept; + shared_ptr& operator=(shared_ptr&&) noexcept; + + T& operator*() const noexcept; + T* operator->() const noexcept; + + T* get() const noexcept; + }; + + template + class unique_ptr { + public: + constexpr unique_ptr() noexcept; + explicit unique_ptr(T*) noexcept; + unique_ptr(unique_ptr&&) noexcept; + + unique_ptr& operator=(unique_ptr&&) noexcept; + + T& operator*() const; + T* operator->() const noexcept; + + T* get() const noexcept; + }; + + template unique_ptr make_unique(Args&&...); + + template shared_ptr make_shared(Args&&...); +} + +// --- pair --- + +namespace std { + template + struct pair { + typedef T1 first_type; + typedef T2 second_type; + + T1 first; + T2 second; + pair(); + pair(const T1& x, const T2& y); + template pair(const pair &p); + + void swap(pair& p) /*noexcept(...)*/; + }; + + template constexpr pair, decay_t> make_pair(T1&& x, T2&& y) { + return pair, decay_t>(std::forward(x), std::forward(y)); + } +} + +// --- map --- + +namespace std { + template struct less; + + template, class Allocator = allocator>> + class map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + map(); + map(const map& x); + map(map&& x); + ~map(); + + map& operator=(const map& x); + map& operator=(map&& x) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + T& operator[](const key_type& x); + T& operator[](key_type&& x); + T& at(const key_type& x); + const T& at(const key_type& x) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(map&) /*noexcept(/*==allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(map& source); + template void merge(map&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template struct hash; + template struct equal_to; + + template, class Pred = equal_to, class Allocator = allocator>> + class unordered_map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_map(); + unordered_map(const unordered_map&); + unordered_map(unordered_map&&); + ~unordered_map(); + + unordered_map& operator=(const unordered_map&); + unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + mapped_type& operator[](const key_type& k); + mapped_type& operator[](key_type&& k); + mapped_type& at(const key_type& k); + const mapped_type& at(const key_type& k) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_map&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_map& source); + template void merge(unordered_map&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; +}; + +// --- set --- + +namespace std { + template, class Allocator = allocator> + class set { + public: + using key_type = Key; + using value_type = Key; + using size_type = size_t; + using allocator_type = Allocator; + using iterator = std::iterator; + using const_iterator = std::iterator; + + set(); + set(const set& x); + set(set&& x); + template set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/); + ~set(); + + set& operator=(const set& x); + set& operator=(set&& x) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(set& source); + template void merge(set&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template, class Pred = equal_to, class Allocator = allocator> + class unordered_set { + public: + using key_type = Key; + using value_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using size_type = size_t; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_set(); + unordered_set(const unordered_set&); + unordered_set(unordered_set&&); + template unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/); + ~unordered_set(); + + unordered_set& operator=(const unordered_set&); + unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_set& source); + template void merge(unordered_set&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; +} + +std::vector> returnValue(); +std::vector>& returnRef(); + +std::vector> external_by_value(std::vector>); + +std::vector> external_by_const_ref(const std::vector>&); + +// *: Will be detected once extract destruction of unnamed temporaries and generate IR for them + +const std::vector>& return_self_by_ref(const std::vector>& v) { + return v; +} + +std::vector> return_self_by_value(const std::vector>& v) { + return v; +} + +void test() { + for (auto x : returnValue()) {} // GOOD + for (auto x : returnValue()[0]) {} // BAD [NOT DETECTED] (see *) + for (auto x : external_by_value(returnValue())) {} // GOOD + for (auto x : external_by_const_ref(returnValue())) {} // GOOD + for (auto x : returnValue().at(0)) {} // BAD [NOT DETECTED] (see *) + + for (auto x : returnRef()) {} // GOOD + for (auto x : returnRef()[0]) {} // GOOD + for (auto x : returnRef().at(0)) {} // GOOD + + for(auto it = returnValue().begin(); it != returnValue().end(); ++it) {} // BAD + + { + auto v = returnValue(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } + + { + auto&& v = returnValue(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } + + { + auto&& v = returnValue()[0]; + for(auto it = v.begin(); it != v.end(); ++it) {} // BAD [NOT DETECTED] (see *) + } + + { + auto&& v = returnRef(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } + + { + auto&& v = returnRef()[0]; + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } + + for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED] (see *) + + for (auto x : return_self_by_value(returnValue())) {} // GOOD +} From a8718f99a1965fdadbc093088aa08428168cb17e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 15 Mar 2024 14:32:20 +0000 Subject: [PATCH 04/10] C++: Add qhelp for 'cpp/iterator-to-expired-container'. --- .../CWE-416/IteratorToExpiredContainer.qhelp | 53 +++++++++++++++++++ ...atorToExpiredContainerExtendedLifetime.cpp | 20 +++++++ 2 files changed, 73 insertions(+) create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.qhelp create mode 100644 cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainerExtendedLifetime.cpp diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.qhelp new file mode 100644 index 000000000000..19975b174932 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.qhelp @@ -0,0 +1,53 @@ + + + +

+Using an iterator owned by a container after the lifetime of the container has expired can lead to undefined behavior. +This is because the iterator may be invalidated when the container is destroyed, and dereferencing an invalidated iterator is undefined behavior. +These problems can be hard to spot due to C++'s complex rules for temporary object lifetimes and their extensions. +

+ +
+ + +

+Never create an iterator to a temporary container when the iterator is expected to be used after the container's lifetime has expired. +

+ +
+ +

+ +

+ +

+The rules for lifetime extension ensures that the code in lifetime_of_temp_extended is well-defined. This is because the +lifetime of the temporary container returned by get_vector is extended to the end of the loop. However, prior to C++23, +the lifetime extension rules do not ensure that the container returned by get_vector is extended in lifetime_of_temp_not_extended. +This is because the temporary container is not bound to a rvalue reference. +

+ + +
+ + +
  • CERT C Coding Standard: +MEM30-C. Do not access freed memory.
  • +
  • +OWASP: +Using freed memory. +
  • +
  • +Lifetime safety: Preventing common dangling +
  • +
  • +Containers library +
  • +
  • +Range-based for loop (since C++11) +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainerExtendedLifetime.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainerExtendedLifetime.cpp new file mode 100644 index 000000000000..70232447f05a --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainerExtendedLifetime.cpp @@ -0,0 +1,20 @@ +#include + +std::vector get_vector(); + +void use(int); + +void lifetime_of_temp_extended() { + for(auto x : get_vector()) { + use(x); // GOOD: The lifetime of the vector returned by `get_vector()` is extended until the end of the loop. + } +} + +// Writes the the values of `v` to an external log and returns it unchanged. +const std::vector& log_and_return_argument(const std::vector& v); + +void lifetime_of_temp_not_extended() { + for(auto x : log_and_return_argument(get_vector())) { + use(x); // BAD: The lifetime of the vector returned by `get_vector()` is not extended, and the behavior is undefined. + } +} From e23e3d7fb43192e3e24fd4cf36fc4fd8c141de57 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 15 Mar 2024 17:35:01 +0000 Subject: [PATCH 05/10] C++: Run tests without the extractor and analysis changes. --- .../CWE/CWE-416/IteratorToExpiredContainer.expected | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected index 39588f865bef..e69de29bb2d1 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected @@ -1,10 +0,0 @@ -| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to begin | call to begin | -| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to end | call to end | -| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to begin | call to begin | -| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to end | call to end | -| test.cpp:689:16:689:28 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:30:689:34 | call to begin | call to begin | -| test.cpp:689:45:689:57 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:59:689:61 | call to end | call to end | -| test.cpp:702:26:702:26 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:18:703:22 | call to begin | call to begin | -| test.cpp:702:26:702:26 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:35:703:37 | call to end | call to end | -| test.cpp:716:35:716:47 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:16:716:16 | call to begin | call to begin | -| test.cpp:716:35:716:47 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:16:716:16 | call to end | call to end | From 457d71d7bce302ae546a2d3465601f0c92c544c0 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 18 Mar 2024 12:01:44 +0000 Subject: [PATCH 06/10] Update cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- .../Security/CWE/CWE-416/IteratorToExpiredContainer.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql index 4c165d197eb4..ee4c1584e5cb 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql @@ -74,7 +74,7 @@ predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) { } /** - * Flow from any destroyed object to the qualifier of a `begin` call + * Flow from any destroyed object to the qualifier of a `begin` or `end` call */ module DestroyedToBeginConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = getADestroyedNode() } From 7b6accd33ab65117ae877cbf425d2c441e6ca15c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 18 Mar 2024 12:01:51 +0000 Subject: [PATCH 07/10] Update cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com> --- .../Security/CWE/CWE-416/IteratorToExpiredContainer.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql index ee4c1584e5cb..cf3a22792fe8 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql @@ -1,6 +1,6 @@ /** * @name Iterator to expired container - * @description Using an iterator owned by a container whose lifetimes has expired may lead to unexpected behavior. + * @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior. * @kind problem * @precision high * @id cpp/iterator-to-expired-container From 668239f355ea11d21e05fbe272675baca35fb547 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 18 Mar 2024 15:16:10 +0000 Subject: [PATCH 08/10] C++: Convert tabs to spaces. --- .../query-tests/Security/CWE/CWE-416/test.cpp | 1180 ++++++++--------- 1 file changed, 590 insertions(+), 590 deletions(-) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp index c035fe203a86..3e2d74829758 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp @@ -40,622 +40,622 @@ using decay_t = typename decay_impl>::type; namespace std { - template constexpr T&& forward(remove_reference_t& t) noexcept; - template constexpr T&& forward(remove_reference_t&& t) noexcept; + template constexpr T&& forward(remove_reference_t& t) noexcept; + template constexpr T&& forward(remove_reference_t&& t) noexcept; } // --- iterator --- namespace std { - struct ptrdiff_t; - - template struct iterator_traits; - - template - struct iterator { - typedef Category iterator_category; - - iterator(); - iterator(iterator > const &other); // non-const -> const conversion constructor - - iterator &operator++(); - iterator operator++(int); - iterator &operator--(); - iterator operator--(int); - bool operator==(iterator other) const; - bool operator!=(iterator other) const; - reference_type operator*() const; - pointer_type operator->() const; - iterator operator+(int); - iterator operator-(int); - iterator &operator+=(int); - iterator &operator-=(int); - int operator-(iterator); - reference_type operator[](int); - }; - - struct input_iterator_tag {}; - struct forward_iterator_tag : public input_iterator_tag {}; - struct bidirectional_iterator_tag : public forward_iterator_tag {}; - struct random_access_iterator_tag : public bidirectional_iterator_tag {}; - - struct output_iterator_tag {}; - - template - class back_insert_iterator { - protected: - Container* container = nullptr; - public: - using iterator_category = output_iterator_tag; - using value_type = void; - using difference_type = ptrdiff_t; - using pointer = void; - using reference = void; - using container_type = Container; - constexpr back_insert_iterator() noexcept = default; - constexpr explicit back_insert_iterator(Container& x); - back_insert_iterator& operator=(const typename Container::value_type& value); - back_insert_iterator& operator=(typename Container::value_type&& value); - back_insert_iterator& operator*(); - back_insert_iterator& operator++(); - back_insert_iterator operator++(int); - }; - - template - constexpr back_insert_iterator back_inserter(Container& x) { - return back_insert_iterator(x); - } - - template - class front_insert_iterator { - protected: - Container* container = nullptr; - public: - using iterator_category = output_iterator_tag; - using value_type = void; - using difference_type = ptrdiff_t; - using pointer = void; - using reference = void; - using container_type = Container; - constexpr front_insert_iterator() noexcept = default; - constexpr explicit front_insert_iterator(Container& x); - constexpr front_insert_iterator& operator=(const typename Container::value_type& value); - constexpr front_insert_iterator& operator=(typename Container::value_type&& value); - constexpr front_insert_iterator& operator*(); - constexpr front_insert_iterator& operator++(); - constexpr front_insert_iterator operator++(int); - }; - template - constexpr front_insert_iterator front_inserter(Container& x) { - return front_insert_iterator(x); - } + struct ptrdiff_t; + + template struct iterator_traits; + + template + struct iterator { + typedef Category iterator_category; + + iterator(); + iterator(iterator > const &other); // non-const -> const conversion constructor + + iterator &operator++(); + iterator operator++(int); + iterator &operator--(); + iterator operator--(int); + bool operator==(iterator other) const; + bool operator!=(iterator other) const; + reference_type operator*() const; + pointer_type operator->() const; + iterator operator+(int); + iterator operator-(int); + iterator &operator+=(int); + iterator &operator-=(int); + int operator-(iterator); + reference_type operator[](int); + }; + + struct input_iterator_tag {}; + struct forward_iterator_tag : public input_iterator_tag {}; + struct bidirectional_iterator_tag : public forward_iterator_tag {}; + struct random_access_iterator_tag : public bidirectional_iterator_tag {}; + + struct output_iterator_tag {}; + + template + class back_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr back_insert_iterator() noexcept = default; + constexpr explicit back_insert_iterator(Container& x); + back_insert_iterator& operator=(const typename Container::value_type& value); + back_insert_iterator& operator=(typename Container::value_type&& value); + back_insert_iterator& operator*(); + back_insert_iterator& operator++(); + back_insert_iterator operator++(int); + }; + + template + constexpr back_insert_iterator back_inserter(Container& x) { + return back_insert_iterator(x); + } + + template + class front_insert_iterator { + protected: + Container* container = nullptr; + public: + using iterator_category = output_iterator_tag; + using value_type = void; + using difference_type = ptrdiff_t; + using pointer = void; + using reference = void; + using container_type = Container; + constexpr front_insert_iterator() noexcept = default; + constexpr explicit front_insert_iterator(Container& x); + constexpr front_insert_iterator& operator=(const typename Container::value_type& value); + constexpr front_insert_iterator& operator=(typename Container::value_type&& value); + constexpr front_insert_iterator& operator*(); + constexpr front_insert_iterator& operator++(); + constexpr front_insert_iterator operator++(int); + }; + template + constexpr front_insert_iterator front_inserter(Container& x) { + return front_insert_iterator(x); + } } // --- string --- namespace std { - template struct char_traits; - - typedef size_t streamsize; - - template class allocator { - public: - allocator() throw(); - typedef size_t size_type; - }; - - template, class Allocator = allocator > - class basic_string { - public: - using value_type = charT; - using reference = value_type&; - using const_reference = const value_type&; - typedef typename Allocator::size_type size_type; - static const size_type npos = -1; - - explicit basic_string(const Allocator& a = Allocator()); - basic_string(const charT* s, const Allocator& a = Allocator()); - template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); - - const charT* c_str() const; - charT* data() noexcept; - size_t length() const; - - typedef std::iterator iterator; - typedef std::iterator const_iterator; - - iterator begin(); - iterator end(); - const_iterator begin() const; - const_iterator end() const; - const_iterator cbegin() const; - const_iterator cend() const; - - void push_back(charT c); - - const charT& front() const; - charT& front(); - const charT& back() const; - charT& back(); - - const_reference operator[](size_type pos) const; - reference operator[](size_type pos); - const_reference at(size_type n) const; - reference at(size_type n); - template basic_string& operator+=(const T& t); - basic_string& operator+=(const charT* s); - basic_string& append(const basic_string& str); - basic_string& append(const charT* s); - basic_string& append(size_type n, charT c); - template basic_string& append(InputIterator first, InputIterator last); - basic_string& assign(const basic_string& str); - basic_string& assign(size_type n, charT c); - template basic_string& assign(InputIterator first, InputIterator last); - basic_string& insert(size_type pos, const basic_string& str); - basic_string& insert(size_type pos, size_type n, charT c); - basic_string& insert(size_type pos, const charT* s); - iterator insert(const_iterator p, size_type n, charT c); - template iterator insert(const_iterator p, InputIterator first, InputIterator last); - basic_string& replace(size_type pos1, size_type n1, const basic_string& str); - basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c); - size_type copy(charT* s, size_type n, size_type pos = 0) const; - void clear() noexcept; - basic_string substr(size_type pos = 0, size_type n = npos) const; - void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; - }; - - template basic_string operator+(const basic_string& lhs, const basic_string& rhs); - template basic_string operator+(const basic_string& lhs, const charT* rhs); - - typedef basic_string string; + template struct char_traits; + + typedef size_t streamsize; + + template class allocator { + public: + allocator() throw(); + typedef size_t size_type; + }; + + template, class Allocator = allocator > + class basic_string { + public: + using value_type = charT; + using reference = value_type&; + using const_reference = const value_type&; + typedef typename Allocator::size_type size_type; + static const size_type npos = -1; + + explicit basic_string(const Allocator& a = Allocator()); + basic_string(const charT* s, const Allocator& a = Allocator()); + template basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator()); + + const charT* c_str() const; + charT* data() noexcept; + size_t length() const; + + typedef std::iterator iterator; + typedef std::iterator const_iterator; + + iterator begin(); + iterator end(); + const_iterator begin() const; + const_iterator end() const; + const_iterator cbegin() const; + const_iterator cend() const; + + void push_back(charT c); + + const charT& front() const; + charT& front(); + const charT& back() const; + charT& back(); + + const_reference operator[](size_type pos) const; + reference operator[](size_type pos); + const_reference at(size_type n) const; + reference at(size_type n); + template basic_string& operator+=(const T& t); + basic_string& operator+=(const charT* s); + basic_string& append(const basic_string& str); + basic_string& append(const charT* s); + basic_string& append(size_type n, charT c); + template basic_string& append(InputIterator first, InputIterator last); + basic_string& assign(const basic_string& str); + basic_string& assign(size_type n, charT c); + template basic_string& assign(InputIterator first, InputIterator last); + basic_string& insert(size_type pos, const basic_string& str); + basic_string& insert(size_type pos, size_type n, charT c); + basic_string& insert(size_type pos, const charT* s); + iterator insert(const_iterator p, size_type n, charT c); + template iterator insert(const_iterator p, InputIterator first, InputIterator last); + basic_string& replace(size_type pos1, size_type n1, const basic_string& str); + basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c); + size_type copy(charT* s, size_type n, size_type pos = 0) const; + void clear() noexcept; + basic_string substr(size_type pos = 0, size_type n = npos) const; + void swap(basic_string& s) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + }; + + template basic_string operator+(const basic_string& lhs, const basic_string& rhs); + template basic_string operator+(const basic_string& lhs, const charT* rhs); + + typedef basic_string string; } // --- istring / ostream / stringstream --- namespace std { - template > - class basic_istream /*: virtual public basic_ios - not needed for this test */ { - public: - using char_type = charT; - using int_type = int; //typename traits::int_type; - - basic_istream& operator>>(int& n); - - int_type get(); - basic_istream& get(char_type& c); - basic_istream& get(char_type* s, streamsize n); - int_type peek(); - basic_istream& read (char_type* s, streamsize n); - streamsize readsome(char_type* s, streamsize n); - basic_istream& putback(char_type c); - basic_istream& unget(); - - basic_istream& getline(char_type* s, streamsize n); - basic_istream& getline(char_type* s, streamsize n, char_type delim); - }; - - template basic_istream& operator>>(basic_istream&, charT*); - template basic_istream& operator>>(basic_istream& is, basic_string& str); - - template basic_istream& getline(basic_istream& is, basic_string& str, charT delim); - template basic_istream& getline(basic_istream& is, basic_string& str); - - template > - class basic_ostream /*: virtual public basic_ios - not needed for this test */ { - public: - typedef charT char_type; - - basic_ostream& operator<<(int n); - - basic_ostream& put(char_type c); - basic_ostream& write(const char_type* s, streamsize n); - basic_ostream& flush(); - }; - - template basic_ostream& operator<<(basic_ostream&, const charT*); - template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); - - template> - class basic_iostream : public basic_istream, public basic_ostream { - public: - }; - - template, class Allocator = allocator> - class basic_stringstream : public basic_iostream { - public: - explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); - explicit basic_stringstream( const basic_string& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/); - basic_stringstream(const basic_stringstream& rhs) = delete; - basic_stringstream(basic_stringstream&& rhs); - basic_stringstream& operator=(const basic_stringstream& rhs) = delete; - basic_stringstream& operator=(basic_stringstream&& rhs); - - void swap(basic_stringstream& rhs); - - basic_string str() const; - void str(const basic_string& str); - }; - - typedef basic_istream istream; - typedef basic_ostream ostream; - extern istream cin; - extern ostream cout; - - using stringstream = basic_stringstream; + template > + class basic_istream /*: virtual public basic_ios - not needed for this test */ { + public: + using char_type = charT; + using int_type = int; //typename traits::int_type; + + basic_istream& operator>>(int& n); + + int_type get(); + basic_istream& get(char_type& c); + basic_istream& get(char_type* s, streamsize n); + int_type peek(); + basic_istream& read (char_type* s, streamsize n); + streamsize readsome(char_type* s, streamsize n); + basic_istream& putback(char_type c); + basic_istream& unget(); + + basic_istream& getline(char_type* s, streamsize n); + basic_istream& getline(char_type* s, streamsize n, char_type delim); + }; + + template basic_istream& operator>>(basic_istream&, charT*); + template basic_istream& operator>>(basic_istream& is, basic_string& str); + + template basic_istream& getline(basic_istream& is, basic_string& str, charT delim); + template basic_istream& getline(basic_istream& is, basic_string& str); + + template > + class basic_ostream /*: virtual public basic_ios - not needed for this test */ { + public: + typedef charT char_type; + + basic_ostream& operator<<(int n); + + basic_ostream& put(char_type c); + basic_ostream& write(const char_type* s, streamsize n); + basic_ostream& flush(); + }; + + template basic_ostream& operator<<(basic_ostream&, const charT*); + template basic_ostream& operator<<(basic_ostream& os, const basic_string& str); + + template> + class basic_iostream : public basic_istream, public basic_ostream { + public: + }; + + template, class Allocator = allocator> + class basic_stringstream : public basic_iostream { + public: + explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/); + explicit basic_stringstream( const basic_string& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/); + basic_stringstream(const basic_stringstream& rhs) = delete; + basic_stringstream(basic_stringstream&& rhs); + basic_stringstream& operator=(const basic_stringstream& rhs) = delete; + basic_stringstream& operator=(basic_stringstream&& rhs); + + void swap(basic_stringstream& rhs); + + basic_string str() const; + void str(const basic_string& str); + }; + + typedef basic_istream istream; + typedef basic_ostream ostream; + extern istream cin; + extern ostream cout; + + using stringstream = basic_stringstream; } // --- vector --- namespace std { - template> - class vector { - public: - using value_type = T; - using reference = value_type&; - using const_reference = const value_type&; - using size_type = unsigned int; - using iterator = std::iterator; - using const_iterator = std::iterator; - - vector() noexcept(noexcept(Allocator())); + template> + class vector { + public: + using value_type = T; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = unsigned int; + using iterator = std::iterator; + using const_iterator = std::iterator; + + vector() noexcept(noexcept(Allocator())); vector(const std::vector&); - explicit vector(const Allocator&) noexcept; - explicit vector(size_type n, const Allocator& = Allocator()); - vector(size_type n, const T& value, const Allocator& = Allocator()); - template vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); - // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or - // similar that should match a different overload (SFINAE). - ~vector(); - - vector& operator=(const vector& x); - vector& operator=(vector&& x) noexcept/*(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value)*/; - template void assign(InputIterator first, InputIterator last); - // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or - // similar that should match a different overload (SFINAE). - void assign(size_type n, const T& u); - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - - size_type size() const noexcept; - - reference operator[](size_type n); - const_reference operator[](size_type n) const; - const_reference at(size_type n) const; - reference at(size_type n); - reference front(); - const_reference front() const; - reference back(); - const_reference back() const; - - T* data() noexcept; - const T* data() const noexcept; - - void push_back(const T& x); - void push_back(T&& x); - - iterator insert(const_iterator position, const T& x); - iterator insert(const_iterator position, T&& x); - iterator insert(const_iterator position, size_type n, const T& x); - template iterator insert(const_iterator position, InputIterator first, InputIterator last); - - template iterator emplace (const_iterator position, Args&&... args); - template void emplace_back (Args&&... args); - - void swap(vector&) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; - - void clear() noexcept; - }; + explicit vector(const Allocator&) noexcept; + explicit vector(size_type n, const Allocator& = Allocator()); + vector(size_type n, const T& value, const Allocator& = Allocator()); + template vector(InputIterator first, InputIterator last, const Allocator& = Allocator()); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + ~vector(); + + vector& operator=(const vector& x); + vector& operator=(vector&& x) noexcept/*(allocator_traits::propagate_on_container_move_assignment::value || allocator_traits::is_always_equal::value)*/; + template void assign(InputIterator first, InputIterator last); + // use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or + // similar that should match a different overload (SFINAE). + void assign(size_type n, const T& u); + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + size_type size() const noexcept; + + reference operator[](size_type n); + const_reference operator[](size_type n) const; + const_reference at(size_type n) const; + reference at(size_type n); + reference front(); + const_reference front() const; + reference back(); + const_reference back() const; + + T* data() noexcept; + const T* data() const noexcept; + + void push_back(const T& x); + void push_back(T&& x); + + iterator insert(const_iterator position, const T& x); + iterator insert(const_iterator position, T&& x); + iterator insert(const_iterator position, size_type n, const T& x); + template iterator insert(const_iterator position, InputIterator first, InputIterator last); + + template iterator emplace (const_iterator position, Args&&... args); + template void emplace_back (Args&&... args); + + void swap(vector&) noexcept/*(allocator_traits::propagate_on_container_swap::value || allocator_traits::is_always_equal::value)*/; + + void clear() noexcept; + }; } // --- make_shared / make_unique --- namespace std { - template - class shared_ptr { - public: - shared_ptr() noexcept; - explicit shared_ptr(T*); - shared_ptr(const shared_ptr&) noexcept; - template shared_ptr(const shared_ptr&) noexcept; - template shared_ptr(shared_ptr&&) noexcept; + template + class shared_ptr { + public: + shared_ptr() noexcept; + explicit shared_ptr(T*); + shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(const shared_ptr&) noexcept; + template shared_ptr(shared_ptr&&) noexcept; - shared_ptr& operator=(const shared_ptr&) noexcept; - shared_ptr& operator=(shared_ptr&&) noexcept; + shared_ptr& operator=(const shared_ptr&) noexcept; + shared_ptr& operator=(shared_ptr&&) noexcept; - T& operator*() const noexcept; - T* operator->() const noexcept; + T& operator*() const noexcept; + T* operator->() const noexcept; - T* get() const noexcept; - }; + T* get() const noexcept; + }; - template - class unique_ptr { - public: - constexpr unique_ptr() noexcept; - explicit unique_ptr(T*) noexcept; - unique_ptr(unique_ptr&&) noexcept; + template + class unique_ptr { + public: + constexpr unique_ptr() noexcept; + explicit unique_ptr(T*) noexcept; + unique_ptr(unique_ptr&&) noexcept; - unique_ptr& operator=(unique_ptr&&) noexcept; + unique_ptr& operator=(unique_ptr&&) noexcept; - T& operator*() const; - T* operator->() const noexcept; + T& operator*() const; + T* operator->() const noexcept; - T* get() const noexcept; - }; + T* get() const noexcept; + }; - template unique_ptr make_unique(Args&&...); + template unique_ptr make_unique(Args&&...); - template shared_ptr make_shared(Args&&...); + template shared_ptr make_shared(Args&&...); } // --- pair --- namespace std { - template - struct pair { - typedef T1 first_type; - typedef T2 second_type; - - T1 first; - T2 second; - pair(); - pair(const T1& x, const T2& y); - template pair(const pair &p); - - void swap(pair& p) /*noexcept(...)*/; - }; - - template constexpr pair, decay_t> make_pair(T1&& x, T2&& y) { - return pair, decay_t>(std::forward(x), std::forward(y)); - } + template + struct pair { + typedef T1 first_type; + typedef T2 second_type; + + T1 first; + T2 second; + pair(); + pair(const T1& x, const T2& y); + template pair(const pair &p); + + void swap(pair& p) /*noexcept(...)*/; + }; + + template constexpr pair, decay_t> make_pair(T1&& x, T2&& y) { + return pair, decay_t>(std::forward(x), std::forward(y)); + } } // --- map --- namespace std { - template struct less; - - template, class Allocator = allocator>> - class map { - public: - using key_type = Key; - using mapped_type = T; - using value_type = pair; - using iterator = std::iterator; - using const_iterator = std::iterator; - - map(); - map(const map& x); - map(map&& x); - ~map(); - - map& operator=(const map& x); - map& operator=(map&& x) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - - T& operator[](const key_type& x); - T& operator[](key_type&& x); - T& at(const key_type& x); - const T& at(const key_type& x) const; - - template pair emplace(Args&&... args); - template iterator emplace_hint(const_iterator position, Args&&... args); - - pair insert(const value_type& x); - pair insert(value_type&& x); - iterator insert(const_iterator position, const value_type& x); - iterator insert(const_iterator position, value_type&& x); - - template pair try_emplace(const key_type& k, Args&&... args); - template pair try_emplace(key_type&& k, Args&&... args); - template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); - template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); - template pair insert_or_assign(const key_type& k, M&& obj); - template pair insert_or_assign(key_type&& k, M&& obj); - template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); - template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); - - iterator erase(iterator position); - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - void swap(map&) /*noexcept(/*==allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; - void clear() noexcept; - - template void merge(map& source); - template void merge(map&& source); - - iterator find(const key_type& x); - const_iterator find(const key_type& x) const; - - iterator lower_bound(const key_type& x); - const_iterator lower_bound(const key_type& x) const; - iterator upper_bound(const key_type& x); - const_iterator upper_bound(const key_type& x) const; - - pair equal_range(const key_type& x); - pair equal_range(const key_type& x) const; - }; - - template struct hash; - template struct equal_to; - - template, class Pred = equal_to, class Allocator = allocator>> - class unordered_map { - public: - using key_type = Key; - using mapped_type = T; - using value_type = pair; - using iterator = std::iterator; - using const_iterator = std::iterator; - - unordered_map(); - unordered_map(const unordered_map&); - unordered_map(unordered_map&&); - ~unordered_map(); - - unordered_map& operator=(const unordered_map&); - unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - - mapped_type& operator[](const key_type& k); - mapped_type& operator[](key_type&& k); - mapped_type& at(const key_type& k); - const mapped_type& at(const key_type& k) const; - - template pair emplace(Args&&... args); - template iterator emplace_hint(const_iterator position, Args&&... args); - - pair insert(const value_type& obj); - pair insert(value_type&& obj); - iterator insert(const_iterator hint, const value_type& obj); - iterator insert(const_iterator hint, value_type&& obj); - - template pair try_emplace(const key_type& k, Args&&... args); - template pair try_emplace(key_type&& k, Args&&... args); - template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); - template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); - template pair insert_or_assign(const key_type& k, M&& obj); - template pair insert_or_assign(key_type&& k, M&& obj); - template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); - template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); - - iterator erase(iterator position); - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - void swap(unordered_map&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; - void clear() noexcept; - - template void merge(unordered_map& source); - template void merge(unordered_map&& source); - - iterator find(const key_type& k); - const_iterator find(const key_type& k) const; - - pair equal_range(const key_type& k); - pair equal_range(const key_type& k) const; - }; + template struct less; + + template, class Allocator = allocator>> + class map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + map(); + map(const map& x); + map(map&& x); + ~map(); + + map& operator=(const map& x); + map& operator=(map&& x) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + T& operator[](const key_type& x); + T& operator[](key_type&& x); + T& at(const key_type& x); + const T& at(const key_type& x) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(map&) /*noexcept(/*==allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(map& source); + template void merge(map&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template struct hash; + template struct equal_to; + + template, class Pred = equal_to, class Allocator = allocator>> + class unordered_map { + public: + using key_type = Key; + using mapped_type = T; + using value_type = pair; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_map(); + unordered_map(const unordered_map&); + unordered_map(unordered_map&&); + ~unordered_map(); + + unordered_map& operator=(const unordered_map&); + unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + mapped_type& operator[](const key_type& k); + mapped_type& operator[](key_type&& k); + mapped_type& at(const key_type& k); + const mapped_type& at(const key_type& k) const; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + + template pair try_emplace(const key_type& k, Args&&... args); + template pair try_emplace(key_type&& k, Args&&... args); + template iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); + template iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); + template pair insert_or_assign(const key_type& k, M&& obj); + template pair insert_or_assign(key_type&& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); + template iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_map&) /*noexcept(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_map& source); + template void merge(unordered_map&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; }; // --- set --- namespace std { - template, class Allocator = allocator> - class set { - public: - using key_type = Key; - using value_type = Key; - using size_type = size_t; - using allocator_type = Allocator; - using iterator = std::iterator; - using const_iterator = std::iterator; - - set(); - set(const set& x); - set(set&& x); - template set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/); - ~set(); - - set& operator=(const set& x); - set& operator=(set&& x) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - - template pair emplace(Args&&... args); - template iterator emplace_hint(const_iterator position, Args&&... args); - pair insert(const value_type& x); - pair insert(value_type&& x); - iterator insert(const_iterator position, const value_type& x); - iterator insert(const_iterator position, value_type&& x); - template void insert(InputIterator first, InputIterator last); - - iterator erase(iterator position); - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - void swap(set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; - void clear() noexcept; - - template void merge(set& source); - template void merge(set&& source); - - iterator find(const key_type& x); - const_iterator find(const key_type& x) const; - - iterator lower_bound(const key_type& x); - const_iterator lower_bound(const key_type& x) const; - iterator upper_bound(const key_type& x); - const_iterator upper_bound(const key_type& x) const; - pair equal_range(const key_type& x); - pair equal_range(const key_type& x) const; - }; - - template, class Pred = equal_to, class Allocator = allocator> - class unordered_set { - public: - using key_type = Key; - using value_type = Key; - using hasher = Hash; - using key_equal = Pred; - using allocator_type = Allocator; - using size_type = size_t; - using iterator = std::iterator; - using const_iterator = std::iterator; - - unordered_set(); - unordered_set(const unordered_set&); - unordered_set(unordered_set&&); - template unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/); - ~unordered_set(); - - unordered_set& operator=(const unordered_set&); - unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; - - iterator begin() noexcept; - const_iterator begin() const noexcept; - iterator end() noexcept; - const_iterator end() const noexcept; - - template pair emplace(Args&&... args); - template iterator emplace_hint(const_iterator position, Args&&... args); - pair insert(const value_type& obj); - pair insert(value_type&& obj); - iterator insert(const_iterator hint, const value_type& obj); - iterator insert(const_iterator hint, value_type&& obj); - template void insert(InputIterator first, InputIterator last); - - iterator erase(iterator position); - iterator erase(const_iterator position); - iterator erase(const_iterator first, const_iterator last); - void swap(unordered_set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; - void clear() noexcept; - - template void merge(unordered_set& source); - template void merge(unordered_set&& source); - - iterator find(const key_type& k); - const_iterator find(const key_type& k) const; - pair equal_range(const key_type& k); - pair equal_range(const key_type& k) const; - }; + template, class Allocator = allocator> + class set { + public: + using key_type = Key; + using value_type = Key; + using size_type = size_t; + using allocator_type = Allocator; + using iterator = std::iterator; + using const_iterator = std::iterator; + + set(); + set(const set& x); + set(set&& x); + template set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/); + ~set(); + + set& operator=(const set& x); + set& operator=(set&& x) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& x); + pair insert(value_type&& x); + iterator insert(const_iterator position, const value_type& x); + iterator insert(const_iterator position, value_type&& x); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(set& source); + template void merge(set&& source); + + iterator find(const key_type& x); + const_iterator find(const key_type& x) const; + + iterator lower_bound(const key_type& x); + const_iterator lower_bound(const key_type& x) const; + iterator upper_bound(const key_type& x); + const_iterator upper_bound(const key_type& x) const; + pair equal_range(const key_type& x); + pair equal_range(const key_type& x) const; + }; + + template, class Pred = equal_to, class Allocator = allocator> + class unordered_set { + public: + using key_type = Key; + using value_type = Key; + using hasher = Hash; + using key_equal = Pred; + using allocator_type = Allocator; + using size_type = size_t; + using iterator = std::iterator; + using const_iterator = std::iterator; + + unordered_set(); + unordered_set(const unordered_set&); + unordered_set(unordered_set&&); + template unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/); + ~unordered_set(); + + unordered_set& operator=(const unordered_set&); + unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_move_assignable_v && is_nothrow_move_assignable_v)*/; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + iterator end() noexcept; + const_iterator end() const noexcept; + + template pair emplace(Args&&... args); + template iterator emplace_hint(const_iterator position, Args&&... args); + pair insert(const value_type& obj); + pair insert(value_type&& obj); + iterator insert(const_iterator hint, const value_type& obj); + iterator insert(const_iterator hint, value_type&& obj); + template void insert(InputIterator first, InputIterator last); + + iterator erase(iterator position); + iterator erase(const_iterator position); + iterator erase(const_iterator first, const_iterator last); + void swap(unordered_set&) noexcept/*(allocator_traits::is_always_equal::value && is_nothrow_swappable_v && is_nothrow_swappable_v)*/; + void clear() noexcept; + + template void merge(unordered_set& source); + template void merge(unordered_set&& source); + + iterator find(const key_type& k); + const_iterator find(const key_type& k) const; + pair equal_range(const key_type& k); + pair equal_range(const key_type& k) const; + }; } std::vector> returnValue(); @@ -668,11 +668,11 @@ std::vector> external_by_const_ref(const std::vector>& return_self_by_ref(const std::vector>& v) { - return v; + return v; } std::vector> return_self_by_value(const std::vector>& v) { - return v; + return v; } void test() { @@ -686,34 +686,34 @@ void test() { for (auto x : returnRef()[0]) {} // GOOD for (auto x : returnRef().at(0)) {} // GOOD - for(auto it = returnValue().begin(); it != returnValue().end(); ++it) {} // BAD + for(auto it = returnValue().begin(); it != returnValue().end(); ++it) {} // BAD - { - auto v = returnValue(); - for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD - } + { + auto v = returnValue(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } - { - auto&& v = returnValue(); - for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD - } + { + auto&& v = returnValue(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } - { - auto&& v = returnValue()[0]; - for(auto it = v.begin(); it != v.end(); ++it) {} // BAD [NOT DETECTED] (see *) - } - - { - auto&& v = returnRef(); - for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD - } + { + auto&& v = returnValue()[0]; + for(auto it = v.begin(); it != v.end(); ++it) {} // BAD [NOT DETECTED] (see *) + } + + { + auto&& v = returnRef(); + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } - { - auto&& v = returnRef()[0]; - for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD - } + { + auto&& v = returnRef()[0]; + for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD + } - for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED] (see *) + for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED] (see *) - for (auto x : return_self_by_value(returnValue())) {} // GOOD + for (auto x : return_self_by_value(returnValue())) {} // GOOD } From e373341f62a133b45a1f86cdc8f730d81d4c468f Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 18 Mar 2024 15:47:38 +0000 Subject: [PATCH 09/10] C++: Add more tests. --- .../query-tests/Security/CWE/CWE-416/test.cpp | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp index 3e2d74829758..506096ae1447 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp @@ -717,3 +717,52 @@ void test() { for (auto x : return_self_by_value(returnValue())) {} // GOOD } + +template +void iterate(const std::vector& v) { + for (auto x : v) {} +} + +std::vector& ref_to_first_in_returnValue_1() { + return returnValue()[0]; // BAD [NOT DETECTED] (see *) +} + +std::vector& ref_to_first_in_returnValue_2() { + return returnValue()[0]; // BAD [NOT DETECTED] +} + +std::vector& ref_to_first_in_returnValue_3() { + return returnValue()[0]; // BAD [NOT DETECTED] (see *) +} + +std::vector first_in_returnValue_1() { + return returnValue()[0]; // GOOD +} + +std::vector first_in_returnValue_2() { + return returnValue()[0]; // GOOD +} + +void test2() { + iterate(returnValue()); // GOOD [FALSE POSITIVE] (see *) + iterate(returnValue()[0]); // GOOD [FALSE POSITIVE] (see *) + + for (auto x : ref_to_first_in_returnValue_1()) {} + + { + auto value = ref_to_first_in_returnValue_2(); + for (auto x : value) {} + } + + { + auto& ref = ref_to_first_in_returnValue_3(); + for (auto x : ref) {} + } + + for (auto x : first_in_returnValue_1()) {} + + { + auto value = first_in_returnValue_2(); + for (auto x : value) {} + } +} From b944f3b411f31c704db467696ac8c494bf2ab714 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 18 Mar 2024 15:54:53 +0000 Subject: [PATCH 10/10] C++: Fix FP. --- .../CWE/CWE-416/IteratorToExpiredContainer.ql | 14 ++++++++++++++ .../query-tests/Security/CWE/CWE-416/test.cpp | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql index cf3a22792fe8..15f34aa8d151 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql @@ -80,6 +80,20 @@ module DestroyedToBeginConfig implements DataFlow::ConfigSig { predicate isSource(DataFlow::Node source) { source = getADestroyedNode() } predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) } + + DataFlow::FlowFeature getAFeature() { + // By blocking argument-to-parameter flow we ensure that we don't enter a + // function body where the temporary outlives anything inside the function. + // This prevents false positives in cases like: + // ```cpp + // void foo(const std::vector& v) { + // for(auto x : v) { ... } // this is fine since v outlives the loop + // } + // ... + // foo(create_temporary()) + // ``` + result instanceof DataFlow::FeatureHasSinkCallContext + } } module DestroyedToBeginFlow = DataFlow::Global; diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp index 506096ae1447..4e5fcbd21498 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp @@ -744,8 +744,8 @@ std::vector first_in_returnValue_2() { } void test2() { - iterate(returnValue()); // GOOD [FALSE POSITIVE] (see *) - iterate(returnValue()[0]); // GOOD [FALSE POSITIVE] (see *) + iterate(returnValue()); // GOOD + iterate(returnValue()[0]); // GOOD for (auto x : ref_to_first_in_returnValue_1()) {}