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 79d36e77f3dc..47e4d5f4f0cc 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Iterator.qll @@ -440,9 +440,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, - AliasFunction, SideEffectFunction -{ +class BeginOrEndFunction extends MemberFunction { BeginOrEndFunction() { this.hasName([ "begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin", @@ -450,7 +448,11 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera ]) and this.getType().getUnspecifiedType() instanceof Iterator } +} +private class BeginOrEndFunctionModels extends BeginOrEndFunction, TaintFunction, + GetIteratorFunction, AliasFunction, SideEffectFunction +{ 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 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/IteratorToExpiredContainer.ql b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql new file mode 100644 index 000000000000..15f34aa8d151 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql @@ -0,0 +1,103 @@ +/** + * @name Iterator to expired container + * @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 + * @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` or `end` call + */ +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; + +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() 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. + } +} 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..e69de29bb2d1 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..4e5fcbd21498 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp @@ -0,0 +1,768 @@ + +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 +} + +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 + iterate(returnValue()[0]); // GOOD + + 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) {} + } +} diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitattributes b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitattributes new file mode 100644 index 000000000000..00a51aff5e5a --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitattributes @@ -0,0 +1,6 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# These are explicitly windows files and should use crlf +*.bat text eol=crlf + diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitignore b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitignore new file mode 100644 index 000000000000..1b6985c0094c --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/.gitignore @@ -0,0 +1,5 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/build.gradle b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/build.gradle new file mode 100644 index 000000000000..071a12b7691c --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/build.gradle @@ -0,0 +1,30 @@ +/* + * This build file was auto generated by running the Gradle 'init' task + * by 'arthur' at '28/11/20 22:29' with Gradle 3.0 + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * user guide available at https://docs.gradle.org/3.0/userguide/tutorial_java_projects.html + */ + +// Apply the java plugin to add support for Java +apply plugin: 'java' + +// In this section you declare where to find the dependencies of your project +repositories { + // Use 'jcenter' for resolving your dependencies. + // You can declare any Maven/Ivy/file repository here. + jcenter() +} + +// In this section you declare the dependencies for your production and test code +dependencies { + // The production code uses the SLF4J logging API at compile time + compile 'org.slf4j:slf4j-api:1.7.21' + + // Declare the dependency for your favourite test framework you want to use in your tests. + // TestNG is also supported by the Gradle Test task. Just change the + // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add + // 'test.useTestNG()' to your build script. + testCompile 'junit:junit:4.12' +} diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/force_sequential_test_execution b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/force_sequential_test_execution new file mode 100644 index 000000000000..b0e2500b259b --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/force_sequential_test_execution @@ -0,0 +1,3 @@ +# We currently have a bug where gradle tests become flaky when executed in parallel +# - sometimes, gradle fails to connect to the gradle daemon. +# Therefore, force this test to run sequentially. diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/verification-metadata.xml b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/verification-metadata.xml new file mode 100644 index 000000000000..14a69b8178b7 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/verification-metadata.xml @@ -0,0 +1,7 @@ + + + + true + false + + \ No newline at end of file diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/wrapper/gradle-wrapper.properties b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..12d38de6a487 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew new file mode 100755 index 000000000000..4f906e0c811f --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew.bat b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew.bat new file mode 100644 index 000000000000..107acd32c4e6 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/settings.gradle b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/settings.gradle new file mode 100644 index 000000000000..233410459f60 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/settings.gradle @@ -0,0 +1,19 @@ +/* + * This settings file was auto generated by the Gradle buildInit task + * by 'arthur' at '28/11/20 22:29' with Gradle 3.0 + * + * The settings file is used to specify which projects to include in your build. + * In a single project build this file can be empty or even removed. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user guide at https://docs.gradle.org/3.0/userguide/multi_project_builds.html + */ + +/* +// To declare projects as part of a multi-project build use the 'include' method +include 'shared' +include 'api' +include 'services:webservice' +*/ + +rootProject.name = 'gradle-sample' diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/main/java/com/example/App.java b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/main/java/com/example/App.java new file mode 100644 index 000000000000..1c13f7d885e5 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/main/java/com/example/App.java @@ -0,0 +1,14 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package com.example; + +public class App { + public String getGreeting() { + return "Hello world."; + } + + public static void main(String[] args) { + System.out.println(new App().getGreeting()); + } +} diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/test/java/com/example/AppTest.java b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/test/java/com/example/AppTest.java new file mode 100644 index 000000000000..813bc5e1a2ae --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/src/test/java/com/example/AppTest.java @@ -0,0 +1,14 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package com.example; + +import org.junit.Test; +import static org.junit.Assert.*; + +public class AppTest { + @Test public void testAppHasAGreeting() { + App classUnderTest = new App(); + assertNotNull("app should have a greeting", classUnderTest.getGreeting()); + } +} diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.expected b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.expected new file mode 100644 index 000000000000..82f7ee275a1a --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.expected @@ -0,0 +1,5 @@ +xmlFiles +| gradle/verification-metadata.xml:0:0:0:0 | gradle/verification-metadata.xml | +#select +| src/main/java/com/example/App.java:0:0:0:0 | App | +| src/test/java/com/example/AppTest.java:0:0:0:0 | AppTest | diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.py b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.py new file mode 100644 index 000000000000..846a89e87038 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.py @@ -0,0 +1,31 @@ +import sys + +from create_database_utils import * +import shutil +import os.path +import tempfile +import platform + +#The version of gradle used doesn't work on java 17 +try_use_java11() + +gradle_override_dir = tempfile.mkdtemp() +if platform.system() == "Windows": + with open(os.path.join(gradle_override_dir, "gradle.bat"), "w") as f: + f.write("@echo off\nexit /b 2\n") +else: + gradlepath = os.path.join(gradle_override_dir, "gradle") + with open(gradlepath, "w") as f: + f.write("#!/bin/bash\nexit 1\n") + os.chmod(gradlepath, 0o0755) + +oldpath = os.getenv("PATH") +os.environ["PATH"] = gradle_override_dir + os.pathsep + oldpath + +try: + run_codeql_database_create([], lang="java") +finally: + try: + shutil.rmtree(gradle_override_dir) + except Exception as e: + pass diff --git a/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.ql b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.ql new file mode 100644 index 000000000000..c11b8fba7070 --- /dev/null +++ b/java/ql/integration-tests/all-platforms/java/partial-gradle-sample-without-gradle/test.ql @@ -0,0 +1,7 @@ +import java + +from File f +where f.isSourceFile() +select f + +query predicate xmlFiles(XmlFile x) { any() } diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml index 0569b4c209c4..9012f7c981fa 100644 --- a/java/ql/lib/ext/java.lang.model.yml +++ b/java/ql/lib/ext/java.lang.model.yml @@ -82,8 +82,8 @@ extensions: - ["java.lang", "Exception", False, "Exception", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - ["java.lang", "Exception", False, "Exception", "(String,Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - ["java.lang", "Exception", False, "Exception", "(String,Throwable)", "", "Argument[1]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "manual"] - - ["java.lang", "Exception", False, "Exception", "(Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "manual"] - - ["java.lang", "Exception", False, "Exception", "(Throwable)", "", "Argument[0].SyntheticField[java.lang.Throwable.message]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "taint", "manual"] + - ["java.lang", "Exception", False, "Exception", "(Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "df-manual"] + - ["java.lang", "Exception", False, "Exception", "(Throwable)", "", "Argument[0].SyntheticField[java.lang.Throwable.message]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "taint", "df-manual"] - ["java.lang", "IllegalArgumentException", False, "IllegalArgumentException", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - ["java.lang", "IllegalStateException", False, "IllegalStateException", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - ["java.lang", "IndexOutOfBoundsException", False, "IndexOutOfBoundsException", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] @@ -148,16 +148,16 @@ extensions: - ["java.lang", "ThreadLocal", True, "set", "(Object)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.ThreadLocal.value]", "value", "manual"] - ["java.lang", "ThreadLocal", False, "withInitial", "(Supplier)", "", "Argument[0].ReturnValue", "ReturnValue.SyntheticField[java.lang.ThreadLocal.value]", "value", "manual"] - ["java.lang", "Throwable", False, "Throwable", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - - ["java.lang", "Throwable", False, "Throwable", "(String,Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - - ["java.lang", "Throwable", False, "Throwable", "(String,Throwable)", "", "Argument[1]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "manual"] + - ["java.lang", "Throwable", False, "Throwable", "(String,Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "df-manual"] + - ["java.lang", "Throwable", False, "Throwable", "(String,Throwable)", "", "Argument[1]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "df-manual"] - ["java.lang", "Throwable", False, "Throwable", "(Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "manual"] - - ["java.lang", "Throwable", False, "Throwable", "(Throwable)", "", "Argument[0].SyntheticField[java.lang.Throwable.message]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "taint", "manual"] + - ["java.lang", "Throwable", False, "Throwable", "(Throwable)", "", "Argument[0].SyntheticField[java.lang.Throwable.message]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "taint", "df-manual"] - ["java.lang", "Throwable", True, "getCause", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "ReturnValue", "value", "manual"] - ["java.lang", "Throwable", True, "getMessage", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"] - ["java.lang", "Throwable", True, "getLocalizedMessage", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"] - - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "manual"] - - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[0]", "ReturnValue.SyntheticField[java.lang.Throwable.cause]", "value", "manual"] - - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[this]", "ReturnValue", "value", "manual"] + - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.cause]", "value", "df-manual"] + - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[0]", "ReturnValue.SyntheticField[java.lang.Throwable.cause]", "value", "df-manual"] + - ["java.lang", "Throwable", True, "initCause", "(Throwable)", "", "Argument[this]", "ReturnValue", "value", "df-manual"] - ["java.lang", "Throwable", True, "toString", "()", "", "Argument[this].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "taint", "manual"] - ["java.lang", "UnsupportedOperationException", False, "UnsupportedOperationException", "(String)", "", "Argument[0]", "Argument[this].SyntheticField[java.lang.Throwable.message]", "value", "manual"] - addsTo: diff --git a/java/ql/lib/ext/javax.crypto.model.yml b/java/ql/lib/ext/javax.crypto.model.yml index 53b54f1a22db..2b3bfc1abe85 100644 --- a/java/ql/lib/ext/javax.crypto.model.yml +++ b/java/ql/lib/ext/javax.crypto.model.yml @@ -11,18 +11,18 @@ extensions: pack: codeql/java-all extensible: neutralModel data: - - ["javax.crypto", "Cipher", "doFinal", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getAlgorithm", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getExemptionMechanism", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getInstance", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getIV", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getParameters", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "getProvider", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "init", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "toString", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "unwrap", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "update", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "updateAAD", "", "summary", "manual"] - - ["javax.crypto", "Cipher", "wrap", "", "summary", "manual"] + - ["javax.crypto", "Cipher", "doFinal", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getAlgorithm", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getExemptionMechanism", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getInstance", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getIV", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getParameters", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "getProvider", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "init", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "toString", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "unwrap", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "update", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "updateAAD", "", "summary", "df-manual"] + - ["javax.crypto", "Cipher", "wrap", "", "summary", "df-manual"] - ["javax.crypto", "Mac", "init", "(Key)", "summary", "df-manual"] - ["javax.crypto", "Mac", "doFinal", "()", "summary", "df-manual"]