From 675009a2ee22f207b87cd7e5287fcf79407827d6 Mon Sep 17 00:00:00 2001 From: Andrzej Kotulski Date: Fri, 18 Mar 2016 10:38:32 -0700 Subject: [PATCH] Add unique_ptr model Summary:public Create a model of std::unique_ptr in similar fashion to what was done to std::shared_ptr. For now, we are modeling it as container of raw pointer (no ownership concept). This time unique_ptr is not derived from std__unique_ptr (unlike shared_ptr, it was easier to not do that) and so we need to provide implementations for all non-member functions per C++ reference: http://en.cppreference.com/w/cpp/memory/unique_ptr Reviewed By: dulmarod Differential Revision: D3048209 fb-gh-sync-id: a9a6455 shipit-source-id: a9a6455 --- infer/models/cpp/include/backward/auto_ptr.h | 13 + infer/models/cpp/include/bits/unique_ptr.h | 15 + .../infer_model/begin_name_override.inc | 3 +- .../include/infer_model/end_name_override.inc | 2 + .../cpp/include/infer_model/unique_ptr.h | 370 ++++++++++++++++++ infer/models/cpp/include/memory | 1 + .../cpp/errors/smart_ptr/unique_ptr_deref.cpp | 109 ++++++ .../endtoend/cpp/UniquePtrDerefTest.java | 73 ++++ 8 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 infer/models/cpp/include/backward/auto_ptr.h create mode 100644 infer/models/cpp/include/bits/unique_ptr.h create mode 100644 infer/models/cpp/include/infer_model/unique_ptr.h create mode 100644 infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp create mode 100644 infer/tests/endtoend/cpp/UniquePtrDerefTest.java diff --git a/infer/models/cpp/include/backward/auto_ptr.h b/infer/models/cpp/include/backward/auto_ptr.h new file mode 100644 index 000000000..d8af4b62a --- /dev/null +++ b/infer/models/cpp/include/backward/auto_ptr.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +// this file exists in gcc headers and we need to capture those includes +#include +#include_next +#include diff --git a/infer/models/cpp/include/bits/unique_ptr.h b/infer/models/cpp/include/bits/unique_ptr.h new file mode 100644 index 000000000..5851ac29b --- /dev/null +++ b/infer/models/cpp/include/bits/unique_ptr.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +// this file exists in gcc headers and we need to capture those includes +#include +#include_next +#include + +#include diff --git a/infer/models/cpp/include/infer_model/begin_name_override.inc b/infer/models/cpp/include/infer_model/begin_name_override.inc index e7a8cffe1..9c48c48de 100644 --- a/infer/models/cpp/include/infer_model/begin_name_override.inc +++ b/infer/models/cpp/include/infer_model/begin_name_override.inc @@ -1,3 +1,4 @@ #define make_shared std__make_shared #define shared_ptr std__shared_ptr - +#define unique_ptr std__unique_ptr +#define make_unique std__make_unique diff --git a/infer/models/cpp/include/infer_model/end_name_override.inc b/infer/models/cpp/include/infer_model/end_name_override.inc index 53b074f1c..f91c269d6 100644 --- a/infer/models/cpp/include/infer_model/end_name_override.inc +++ b/infer/models/cpp/include/infer_model/end_name_override.inc @@ -1,2 +1,4 @@ #undef make_shared #undef shared_ptr +#undef unique_ptr +#undef make_unique diff --git a/infer/models/cpp/include/infer_model/unique_ptr.h b/infer/models/cpp/include/infer_model/unique_ptr.h new file mode 100644 index 000000000..4be60327c --- /dev/null +++ b/infer/models/cpp/include/infer_model/unique_ptr.h @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#pragma once + +#include + +INFER_NAMESPACE_STD_BEGIN + +// IMPORTANT +// There is specialization of unique_ptr below and it's mosly copy paste. +// When changing model, remember to change it for specialization as well! +template > +struct unique_ptr { + // use SFINAE to determine whether _Del::pointer exists + class _Pointer { + template + static typename _Up::pointer __test(typename _Up::pointer*); + + template + static _Tp* __test(...); + + typedef typename remove_reference<_Dp>::type _Del; + + public: + typedef decltype(__test<_Del>(0)) type; + }; + + public: + typedef typename _Pointer::type pointer; + typedef _Tp element_type; + typedef _Dp deleter_type; + + pointer data; + template + unique_ptr(const std__unique_ptr& u) {} + + constexpr unique_ptr() noexcept : data(nullptr) {} + + constexpr unique_ptr(nullptr_t) noexcept : unique_ptr<_Tp, _Dp>() {} + + unique_ptr(pointer ptr) : data(ptr) {} + + unique_ptr(pointer ptr, + typename conditional< + is_reference::value, + deleter_type, + typename add_lvalue_reference::type>::type + __d) noexcept : data(ptr) {} + + unique_ptr(pointer ptr, + typename remove_reference::type&& __d) noexcept + : data(ptr) {} + + unique_ptr(unique_ptr&& u) noexcept : data(u.data) { u.data = nullptr; } + + template ::value && + is_convertible::pointer, + pointer>::value && + is_convertible<_Ep, deleter_type>::value && + (!is_reference::value || + is_same::value)>::type> + unique_ptr(unique_ptr<_Up, _Ep>&& u) noexcept : data(u.data) { + u.data = nullptr; + } + + template < + class _Up, + typename = typename enable_if::value>::type> + unique_ptr(auto_ptr<_Up>&& __p) noexcept; + + ~unique_ptr() { reset(); } + + unique_ptr& operator=(unique_ptr&& __u) noexcept { + reset(__u.data); + return *this; + } + + template ::value && + is_convertible::pointer, + pointer>::value && + is_assignable::value>::type> + unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) noexcept { + reset(__u.data); + return *this; + } + + unique_ptr& operator=(nullptr_t) noexcept { + reset(); + return *this; + } + typename add_lvalue_reference<_Tp>::type operator*() const { return *data; } + + pointer operator->() const { return data; } + + pointer get() const { return data; } + + typedef typename remove_reference::type& _Dp_reference; + typedef const typename remove_reference::type& + _Dp_const_reference; + _Dp_const_reference get_deleter() const {} + _Dp_reference get_deleter() {} + + explicit operator bool() const { return data != nullptr; } + pointer release() { return data; } + + void reset(pointer p = nullptr) { data = p; } + + void swap(unique_ptr& u) { + pointer tmp = data; + data = u.data; + u.data = tmp; + } +}; + +template +struct unique_ptr<_Tp[], _Dp> { + // use SFINAE to determine whether _Del::pointer exists + class _Pointer { + template + static typename _Up::pointer __test(typename _Up::pointer*); + + template + static _Tp* __test(...); + + typedef typename remove_reference<_Dp>::type _Del; + + public: + typedef decltype(__test<_Del>(0)) type; + }; + + public: + typedef typename _Pointer::type pointer; + typedef _Tp element_type; + typedef _Dp deleter_type; + + pointer data; + + constexpr unique_ptr() noexcept : data(nullptr) {} + + constexpr unique_ptr(nullptr_t) noexcept : data(nullptr) {} + + unique_ptr(pointer ptr) : data(ptr) {} + + unique_ptr( + pointer ptr, + typename conditional< + is_reference::value, + deleter_type, + typename add_lvalue_reference::type>::type __d) + : data(ptr) {} + + unique_ptr(pointer ptr, typename remove_reference::type&& __d) + : data(ptr) {} + + unique_ptr(unique_ptr&& u) : data(u.data) { u.data = nullptr; } + + template ::value && + is_convertible::pointer, + pointer>::value && + is_convertible<_Ep, deleter_type>::value && + (!is_reference::value || + is_same::value)>::type> + unique_ptr(unique_ptr<_Up, _Ep>&& u) : data(u.data) { + u.data = nullptr; + } + + template < + class _Up, + typename = typename enable_if::value>::type> + unique_ptr(auto_ptr<_Up>&& __p) noexcept; + + ~unique_ptr() { reset(); } + + unique_ptr& operator=(unique_ptr&& __u) { + reset(__u.data); + return *this; + } + + template ::value && + is_convertible::pointer, + pointer>::value && + is_assignable::value>::type> + unique_ptr& operator=(unique_ptr<_Up, _Ep>&& __u) { + reset(__u.data); + return *this; + } + + unique_ptr& operator=(nullptr_t) { + reset(); + return *this; + } + + typename add_lvalue_reference<_Tp>::type operator[](size_t i) const {} + + pointer get() const { return data; } + + typedef typename remove_reference::type& _Dp_reference; + typedef const typename remove_reference::type& + _Dp_const_reference; + _Dp_const_reference get_deleter() const {} + _Dp_reference get_deleter() {} + + explicit operator bool() const { return data != nullptr; } + pointer release() { return data; } + + void reset(pointer p = nullptr) { data = p; } + + void swap(unique_ptr& u) { + pointer tmp = data; + data = u.data; + u.data = tmp; + } +}; + +template +inline bool operator==(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + return __x.get() == __y.get(); +} + +template +inline bool operator!=(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + return !(__x == __y); +} + +template +inline bool operator<(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + /*typedef typename unique_ptr<_T1, _D1>::pointer _P1; + typedef typename unique_ptr<_T2, _D2>::pointer _P2; + typedef typename common_type<_P1, _P2>::type _Vp; + return less<_Vp>()(__x.get(), __y.get());*/ +} + +template +inline bool operator>(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + return __y < __x; +} + +template +inline bool operator<=(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + return !(__y < __x); +} + +template +inline bool operator>=(const unique_ptr<_T1, _D1>& __x, + const unique_ptr<_T2, _D2>& __y) { + return !(__x < __y); +} + +template +inline bool operator==(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + return !__x; +} + +template +inline bool operator==(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + return !__x; +} + +template +inline bool operator!=(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + return static_cast(__x); +} + +template +inline bool operator!=(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + return static_cast(__x); +} + +template +inline bool operator<(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + /*typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(__x.get(), nullptr);*/ +} + +template +inline bool operator<(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + /*typedef typename unique_ptr<_T1, _D1>::pointer _P1; + return less<_P1>()(nullptr, __x.get());*/ +} + +template +inline bool operator>(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + return nullptr < __x; +} + +template +inline bool operator>(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + return __x < nullptr; +} + +template +inline bool operator<=(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + return !(nullptr < __x); +} + +template +inline bool operator<=(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + return !(__x < nullptr); +} + +template +inline bool operator>=(const unique_ptr<_T1, _D1>& __x, nullptr_t) { + return !(__x < nullptr); +} + +template +inline bool operator>=(nullptr_t, const unique_ptr<_T1, _D1>& __x) { + return !(nullptr < __x); +} + +template +struct hash> : public hash> {}; + +template +struct _MakeUniq2 { + typedef unique_ptr<_Tp> __single_object; +}; + +template +struct _MakeUniq2<_Tp[]> { + typedef unique_ptr<_Tp[]> __array; +}; + +template +struct _MakeUniq2<_Tp[_Bound]> { + struct __invalid_type {}; +}; + +/// std::make_unique for single objects +template +inline typename _MakeUniq2<_Tp>::__single_object make_unique( + _Args&&... __args) { + return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); +} + +/// std::make_unique for arrays of unknown bound +template +inline typename _MakeUniq2<_Tp>::__array make_unique(size_t __num) { + return unique_ptr<_Tp>(new typename remove_extent<_Tp>::type[__num]()); +} + +/// Disable std::make_unique for arrays of known bound +template +inline typename _MakeUniq2<_Tp>::__invalid_type make_unique(_Args&&...) = + delete; +INFER_NAMESPACE_STD_END diff --git a/infer/models/cpp/include/memory b/infer/models/cpp/include/memory index 1eccbca41..9182434a0 100644 --- a/infer/models/cpp/include/memory +++ b/infer/models/cpp/include/memory @@ -5,6 +5,7 @@ #include_next #include +#include #include #else // __cplusplus < 201103L diff --git a/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp new file mode 100644 index 000000000..a9943ab45 --- /dev/null +++ b/infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#include + +struct X { + int field; + int get() { return field; } + void set(int value) { field = value; } +}; + +int empty_ptr_access() { + std::unique_ptr x; + int* p = x.get(); // no dereference + if (p) { + return 1; + } + return 0; +} + +int empty_ptr_deref() { + std::unique_ptr x; + return *x; +} + +int nullptr_ptr_deref() { + std::unique_ptr x(nullptr); + return *x; +} + +int empty_ptr_field_deref() { + std::unique_ptr x; + return x.get()->field; +} + +int empty_ptr_field_deref2() { + std::unique_ptr x; + return x->field; +} + +int empty_ptr_method_deref() { + std::unique_ptr x; + return x->get(); +} + +int reset_ptr_null_deref() { + std::unique_ptr x(new int); + x.reset(); + return *x; +} + +int reset_ptr_null_deref2() { + std::unique_ptr x(new int); + x.reset(new int); + x.reset(); + return *x; +} + +int reset_ptr_ok_deref() { + std::unique_ptr x; + x.reset(new int); + return *x; +} + +int reset_ptr_ok_deref2() { + std::unique_ptr x; + x.reset(); + x.reset(new int); + return *x; +} + +int unique_ptr_copy_null_deref() { + std::unique_ptr p1; + std::unique_ptr p2 = std::move(p1); + return *p2; +} + +int unique_ptr_assign_null_deref() { + std::unique_ptr p1(new int); + std::unique_ptr p2; + p1 = std::move(p2); + return *p1; +} + +int unique_ptr_move_ok_deref() { + std::unique_ptr p1(new int); + std::unique_ptr p2 = std::move(p1); + return *p2; +} + +int unique_ptr_assign_ok_deref() { + std::unique_ptr p1(new int); + std::unique_ptr p2; + p2 = std::move(p1); + p1.reset(); + return *p2; +} + +int unique_ptr_move_null_deref() { + std::unique_ptr p1(new int); + std::unique_ptr p2 = std::move(p1); + return *p1; +} diff --git a/infer/tests/endtoend/cpp/UniquePtrDerefTest.java b/infer/tests/endtoend/cpp/UniquePtrDerefTest.java new file mode 100644 index 000000000..6a8ffa646 --- /dev/null +++ b/infer/tests/endtoend/cpp/UniquePtrDerefTest.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package endtoend.cpp; + +import static org.hamcrest.MatcherAssert.assertThat; +import static utils.matchers.ResultContainsExactly.containsExactly; + + +import com.google.common.collect.ImmutableList; + +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import utils.DebuggableTemporaryFolder; +import utils.InferException; +import utils.InferResults; +import utils.InferRunner; + +public class UniquePtrDerefTest { + + public static final String FILE = + "infer/tests/codetoanalyze/cpp/errors/smart_ptr/unique_ptr_deref.cpp"; + + private static ImmutableList inferCmd; + + public static final String NULL_DEREFERENCE = "NULL_DEREFERENCE"; + + @ClassRule + public static DebuggableTemporaryFolder folder = + new DebuggableTemporaryFolder(); + + @BeforeClass + public static void runInfer() throws InterruptedException, IOException { + inferCmd = InferRunner.createCPPInferCommand(folder, FILE); + } + + @Test + public void whenInferRunsNullDerefFunctionsErrorIsFound() + throws InterruptedException, IOException, InferException { + String[] procedures = { + "empty_ptr_deref", + "nullptr_ptr_deref", + "empty_ptr_field_deref", + "empty_ptr_field_deref2", + "empty_ptr_method_deref", + "reset_ptr_null_deref", + "reset_ptr_null_deref2", + "unique_ptr_copy_null_deref", + "unique_ptr_assign_null_deref", + "unique_ptr_move_null_deref", + }; + InferResults inferResults = InferRunner.runInferCPP(inferCmd); + assertThat( + "Results should contain null dereference", + inferResults, + containsExactly( + NULL_DEREFERENCE, + FILE, + procedures + ) + ); + } +}