[pulse] copy tests from ownership

Summary:
The time has come to keep track of which tests pass and which are FP/FN
for pulse.

Reviewed By: mbouaziz

Differential Revision: D10854064

fbshipit-source-id: 60938e48f
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent cf66ea0afb
commit 6cce767d19

@ -10,7 +10,7 @@ CLANG_OPTIONS = -x c++ -std=c++14 -Wc++1z-extensions -nostdinc++ -isystem$(MODEL
INFER_OPTIONS = --pulse-only --debug-exceptions --project-root $(TESTS_DIR)
INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard ../ownership/*.cpp) $(wildcard *.cpp)
SOURCES = $(wildcard *.cpp)
include $(TESTS_DIR)/clang.make

@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <string>
#include <vector>
struct Aggregate {
int i;
~Aggregate() {}
};
void FP_aggregate_reassign_ok() {
const int len = 5;
Aggregate arr[len];
for (int i = 0; i < len; i++) {
Aggregate s = {1};
// assign with curly bracket syntax doesn't call constructor; need to
// recognize that this is a reassignment anyway
arr[0] = s; // shouldn't be flagged as a use-after-lifetime
}
}
struct AggregateWithConstructedField {
std::string str;
};
void FP_aggregate_reassign2_ok() {
AggregateWithConstructedField arr[10];
for (int i = 0; i < 10; i++) {
// this is translated as string(&(a.str), "hi"). need to make sure this is
// treated the same as initializing a
AggregateWithConstructedField a{"hi"};
arr[i] = a;
}
}
struct NestedAggregate {
AggregateWithConstructedField a;
};
void FP_aggregate_reassign3_ok() {
NestedAggregate arr[10];
for (int i = 0; i < 10; i++) {
// this is translated as std::basic_string(&(a.str), "hi"). need to make
// sure this is treated the same as initializing a
NestedAggregate a{{"hi"}};
arr[i] = a;
}
}
int multiple_invalidations_branch_bad(int n, int* ptr) {
if (n == 7) {
delete ptr;
} else {
delete ptr;
}
return *ptr;
}
int multiple_invalidations_loop_bad(int n, int* ptr) {
for (int i = 0; i < n; i++) {
if (i == 7) {
delete ptr;
} else {
delete ptr;
}
}
return *ptr;
}
Aggregate* FP_pointer_arithmetic_ok(Aggregate* a) {
a->~Aggregate();
a++;
return a;
}
void iterator_pointer_arithmetic_ok(std::vector<Aggregate> v) {
for (auto it = v.begin(); it != v.end(); ++it) {
it->~Aggregate();
}
}
struct A {
~A();
int f(int i);
};
A getA();
int FP_struct_inside_loop_ok(std::vector<int> numbers) {
int sum;
for (auto number : numbers) {
A a = getA();
sum += a.f(number);
}
return sum;
}

@ -0,0 +1,135 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <functional>
struct S {
int f;
S() { f = 1; }
};
int FN_ref_capture_destroy_invoke_bad() {
std::function<int()> f;
{
S s;
f = [&s] { return s.f; };
} // destructor for s called here
return f(); // s used here
}
int FN_implicit_ref_capture_destroy_invoke_bad() {
std::function<int()> f;
{
auto s = S();
f = [&] { return s.f; };
}
return f();
}
int FN_reassign_lambda_capture_destroy_invoke_bad() {
std::function<int()> f;
{
auto s = S();
auto tmp = [&] { return s.f; };
f = tmp;
}
return f();
}
// frontend doesn't understand difference between capture-by-value and
// capture-by-ref, need to fix
int value_capture_destroy_invoke_ok() {
std::function<int()> f;
{
S s;
f = [s] { return s.f; };
}
return f();
}
// same thing here
int implicit_value_capture_destroy_invoke_ok() {
std::function<int()> f;
{
S s;
f = [=] { return s.f; };
}
return f();
}
int ref_capture_invoke_ok() {
std::function<int()> f;
int ret;
{
S s;
f = [&s] { return s.f; };
ret = f();
}
return ret;
}
void invoke_twice_ok() {
std::function<int()> f;
int ret;
{
S s;
f = [&s] { return s.f; };
f();
f();
}
}
std::function<int()> ref_capture_read_lambda_ok() {
std::function<int()> f;
int ret;
{
S s;
f = [&s] { return s.f; };
}
auto tmp =
f; // reading (but not invoking) the lambda doesn't use its captured vars
}
// we'll miss this because we count invoking a lambda object as a use of its
// captured vars, not the lambda object itself.
void FN_delete_lambda_then_call_bad() {
auto lambda = [] { return 1; };
~lambda();
return lambda();
}
// need to treat escaping as a use in order to catch this
std::function<int()> FN_ref_capture_return_lambda_bad() {
std::function<int()> f;
int ret;
{
S s;
f = [&s] { return s.f; };
}
return f; // if the caller invokes the lambda, it will try to read the invalid
// stack address
}
int ref_capture_return_local_lambda_ok() {
S x;
auto f = [&x](void) -> S& {
// do not report this because there is a good chance that this function will
// only be used in the local scope
return x;
};
return f().f;
}
S& FN_ref_capture_return_local_lambda_bad() {
S x;
auto f = [&x](void) -> S& {
// no way to know if ok here
return x;
};
// woops, this returns a ref to a local!
return f();
}

@ -1,33 +1,33 @@
codetoanalyze/cpp/ownership/basics.cpp, aggregate_reassign2_ok, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `AggregateWithConstructedField_~AggregateWithConstructedField(&(a))` at line 39, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/ownership/basics.cpp, aggregate_reassign3_ok, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `NestedAggregate_~NestedAggregate(&(a))` at line 53, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/ownership/basics.cpp, aggregate_reassign_ok, 7, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `Aggregate_~Aggregate(&(s))` at line 25, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/ownership/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 60, column 5 here,accessed `*(ptr)` here]
codetoanalyze/cpp/ownership/basics.cpp, multiple_invalidations_loop_bad, 8, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 70, column 7 here,accessed `*(ptr)` here]
codetoanalyze/cpp/ownership/basics.cpp, pointer_arithmetic_ok, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `Aggregate_~Aggregate(a)` at line 77, column 3 here,accessed `a` here]
codetoanalyze/cpp/ownership/basics.cpp, struct_inside_loop_ok, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `A_~A(&(a))` at line 100, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/ownership/returns.cpp, returns::FN_return_destructed_pointer_bad, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `returns::S_~S(s)` at line 120, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/returns.cpp, returns::return_deleted_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete x` at line 112, column 3 here,accessed `x` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, delete_in_branch_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 57, column 5 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, delete_in_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 82, column 5 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, delete_ref_in_loop_ok, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 97, column 5 here,accessed `*(v.__internal_array)[_]` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, deref_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 18, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, double_delete_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 50, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, gated_delete_abort_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 112, column 5 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, gated_delete_throw_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 130, column 5 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, gated_exit_abort_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 121, column 5 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, reassign_field_of_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 37, column 3 here,accessed `s->f` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, return_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 24, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, use_in_branch_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 73, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_delete.cpp, use_in_loop_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 103, column 3 here,accessed `s->f` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, FN_placement_new_aliasing1_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete alias` at line 133, column 3 here,accessed `s` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, FN_placement_new_aliasing2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 141, column 3 here,accessed `alias` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, basic_placement_new_ok, 4, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(tptr)` at line 118, column 3 here,accessed `ptr` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, destructor_in_loop_ok, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 163, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, double_destructor_bad, 5, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 54, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, reinit_after_explicit_destructor_ok, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 34, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, use_after_destructor_bad, 4, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 61, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/ownership/use_after_destructor.cpp, use_after_scope4_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `C_~C(&(c))` at line 186, column 3 here,accessed `pc->f` here]
codetoanalyze/cpp/pulse/basics.cpp, FP_aggregate_reassign2_ok, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `AggregateWithConstructedField_~AggregateWithConstructedField(&(a))` at line 39, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/pulse/basics.cpp, FP_aggregate_reassign3_ok, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `NestedAggregate_~NestedAggregate(&(a))` at line 53, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/pulse/basics.cpp, FP_aggregate_reassign_ok, 7, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `Aggregate_~Aggregate(&(s))` at line 25, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/pulse/basics.cpp, FP_pointer_arithmetic_ok, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `Aggregate_~Aggregate(a)` at line 77, column 3 here,accessed `a` here]
codetoanalyze/cpp/pulse/basics.cpp, FP_struct_inside_loop_ok, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `A_~A(&(a))` at line 100, column 3 here,accessed `&(a)` here]
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 60, column 5 here,accessed `*(ptr)` here]
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 8, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 70, column 7 here,accessed `*(ptr)` here]
codetoanalyze/cpp/pulse/join.cpp, visit_list, 11, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete result` at line 21, column 5 here,accessed `*(result)` here]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_deleted_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete x` at line 112, column 3 here,accessed `x` here]
codetoanalyze/cpp/pulse/returns.cpp, returns::return_destructed_pointer_bad, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `returns::S_~S(s)` at line 120, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, FP_delete_ref_in_loop_ok, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 97, column 5 here,accessed `*(v.__internal_array)[_]` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, FP_gated_delete_abort_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 112, column 5 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, FP_gated_delete_throw_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 130, column 5 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, FP_gated_exit_abort_ok, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 121, column 5 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_branch_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 57, column 5 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, delete_in_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 82, column 5 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, deref_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 18, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, double_delete_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 50, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, reassign_field_of_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 37, column 3 here,accessed `s->f` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, return_deleted_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 24, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_branch_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 73, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_delete.cpp, use_in_loop_bad, 4, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 103, column 3 here,accessed `s->f` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, FP_basic_placement_new_ok, 4, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(tptr)` at line 118, column 3 here,accessed `ptr` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, FP_destructor_in_loop_ok, 2, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 162, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, FP_reinit_after_explicit_destructor_ok, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 34, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, double_destructor_bad, 5, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 54, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, placement_new_aliasing1_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete alias` at line 132, column 3 here,accessed `s` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, placement_new_aliasing2_bad, 5, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete s` at line 140, column 3 here,accessed `alias` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_destructor_bad, 4, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `S_~S(&(s))` at line 61, column 3 here,accessed `&(s)` here]
codetoanalyze/cpp/pulse/use_after_destructor.cpp, use_after_scope4_bad, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [invalidated by destructor call `C_~C(&(c))` at line 185, column 3 here,accessed `pc->f` here]
codetoanalyze/cpp/pulse/use_after_free.cpp, use_after_free_simple_bad, 2, USE_AFTER_FREE, no_bucket, ERROR, [invalidated by call to `free(x)` at line 10, column 3 here,accessed `*(x)` here]
codetoanalyze/cpp/pulse/vector.cpp, FP_init_fill_then_push_back_loop_ok, 6, USE_AFTER_LIFETIME, no_bucket, ERROR, [potentially invalidated by call to `std::vector::push_back(&(vec), ..)` at line 56, column 5 here,accessed `*(elt)` here]
codetoanalyze/cpp/pulse/vector.cpp, FP_push_back_in_loop_ok, 3, USE_AFTER_LIFETIME, no_bucket, ERROR, [potentially invalidated by call to `std::vector::push_back(vec, ..)` at line 31, column 5 here,accessed `*(vec.__internal_array)[_]` here]

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
struct B {
B(int v) : f(v){};
int f;
};
struct WrapsB {
WrapsB(int f) { b = new B(f); }
B* b;
B* getb() { return b; };
~WrapsB() { delete b; }
};
struct ReferenceWrapperHeap {
ReferenceWrapperHeap(WrapsB& a) : b(a.getb()){};
B* b;
};
ReferenceWrapperHeap getwrapperHeap() {
WrapsB a(1);
return a; // We store a.b in ReferenceWrapper, but we delete a.b in the
// destructor of WrapsB
}
int FN_reference_wrapper_heap_bad() {
ReferenceWrapperHeap rw = getwrapperHeap();
return rw.b->f; // we want to report use after lifetime bug here
}
struct ReferenceWrapperStack {
ReferenceWrapperStack(B& bref) : b(&bref){};
B* b;
};
ReferenceWrapperStack getwrapperStack() {
B b(1);
return b;
}
int FN_reference_wrapper_stack_bad() {
ReferenceWrapperStack rw = getwrapperStack();
return rw.b->f; // we want to report use after lifetime bug here
}

@ -0,0 +1,144 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <string>
namespace returns {
struct S {
int f_;
S(int f) : f_(f) {}
~S() {}
};
const int& FN_return_literal_stack_reference_bad() { return 1; }
const int& FN_return_variable_stack_reference1_bad() {
const int& x = 2;
return x;
}
const int& FN_return_variable_stack_reference2_bad() {
const int& x = 2;
const int& y = x;
return y;
}
const int return_read_of_stack_reference_ok() {
const int& x = 2;
return x;
}
const int& return_formal_reference_ok(int& formal) { return formal; }
const int& return_reference_to_formal_pointer_ok(const int* formal) {
const int& local = *formal;
return local;
}
extern const int& callee();
const int& return_reference_from_callee_ok() {
const int& local = callee();
return local;
}
const int return_int_ok() { return 1; }
const bool return_comparison_temp_ok() { return 1 != 2; }
const bool compare_local_refs_ok() {
const int& local1 = 1;
const int& local2 = 1;
return local1 != local2;
}
extern int& global;
const int& return_global_reference_ok() { return global; }
struct MemberReference {
int& member1;
int& return_member_reference_ok() { return member1; }
int* member2;
int* return_member_reference_indirect_ok() {
int* local = member2;
return local;
}
};
extern const char* const kOptions;
const char* return_field_addr_ternary_ok() {
const char* const* const t = &kOptions;
return t ? *t : "";
}
int* FN_return_stack_pointer_bad() {
int x = 3;
return &x;
}
S* return_static_local_ok() {
S* local;
static S s{1};
local = &s;
return local;
}
S* return_static_local_inner_scope_ok(bool b) {
S* local = nullptr;
if (b) {
static S s{1};
local = &s;
// destructor for s gets called here, but it shouldn't be
}
return local;
}
int* return_formal_pointer_ok(int* formal) { return formal; }
int* return_deleted_bad() {
int* x = new int;
*x = 2;
delete x;
return x;
}
// this *could* be ok depending on what the destructor does, but there's
// probably no good reason to do it
S* return_destructed_pointer_bad() {
S* s = new S(1);
s->~S();
return s;
}
const char* return_nullptr1_ok() { return nullptr; }
const char* return_nullptr2_ok() {
const char* local = nullptr;
return local;
}
struct A {
~A();
};
int try_catch_return_ok() {
A a;
try {
return 1;
} catch (...) {
return 2;
}
}
} // namespace returns

@ -0,0 +1,134 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <memory>
#include <string>
#include <vector>
struct Simple {
int f;
};
void deref_deleted_bad() {
auto s = new Simple{1};
delete s;
Simple tmp = *s;
}
Simple* return_deleted_bad() {
auto s = new Simple{1};
delete s;
return s;
}
Simple* reassign_deleted_ok() {
auto s = new Simple{1};
delete s;
s = new Simple{2};
return s;
}
void reassign_field_of_deleted_bad() {
auto s = new Simple{1};
delete s;
s->f = 7;
}
void reassign_field_of_reinitialized_ok(Simple* tmp) {
auto s = new Simple{1};
delete s;
s = tmp;
s->f = 7;
}
void double_delete_bad() {
auto s = new Simple{1};
delete s;
delete s;
}
Simple* delete_in_branch_bad(bool b) {
auto s = new Simple{1};
if (b) {
delete s;
}
return s;
}
void delete_in_branch_ok(bool b) {
auto s = new Simple{1};
if (b) {
delete s;
} else {
delete s;
}
}
void use_in_branch_bad(bool b) {
auto s = new Simple{1};
delete s;
if (b) {
auto tmp = *s;
}
}
void delete_in_loop_bad() {
auto s = new Simple{1};
for (int i = 0; i < 10; i++) {
delete s;
}
}
void delete_in_loop_ok() {
for (int i = 0; i < 10; i++) {
auto s = new Simple{1};
delete s;
}
}
void FP_delete_ref_in_loop_ok(int j, std::vector<std::string> v) {
int i = 0;
for (int i = 0; i < 10; i++) {
auto s = &v[i];
delete s;
}
}
void use_in_loop_bad() {
auto s = new Simple{1};
delete s;
for (int i = 0; i < 10; i++) {
s->f = i;
}
}
Simple* FP_gated_delete_abort_ok(bool b) {
auto s = new Simple{1};
if (b) {
delete s;
std::abort();
}
return s;
}
Simple* FP_gated_exit_abort_ok(bool b) {
auto s = new Simple{1};
if (b) {
delete s;
exit(1);
}
return s;
}
Simple* FP_gated_delete_throw_ok(bool b) {
auto s = new Simple{1};
if (b) {
delete s;
throw 5;
}
return s;
}

@ -0,0 +1,214 @@
/*
* Copyright (c) 2018-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <iostream>
#include <memory>
#include <string>
struct S {
int* f;
S(int i) {
f = new int;
*f = i;
}
// missing: operator= to copy the pointer. double delete can happen if
// operator= is called
~S() { delete f; }
};
// destructor called at end of function, no issues
void normal_scope_destructor_ok() { S s(1); }
void nested_scope_destructor_ok() {
{ S s(1); }
}
int FP_reinit_after_explicit_destructor_ok() {
S s(1);
s.~S();
s = S(2);
return *s.f;
}
void placement_new_explicit_destructor_ok() {
char buf[sizeof(S)];
{
S* s = new (buf) S(1);
s->~S();
}
{
// this use of [buf] shouldn't be flagged
S* s = new (buf) S(2);
s->~S();
}
}
void double_destructor_bad() {
S s(1);
s.~S();
// destructor will be called again after S goes out of scope, which is
// undefined behavior
}
int use_after_destructor_bad() {
S s(1);
s.~S();
int ret = *s.f;
s = S{2};
return ret;
}
// can't get this yet because we assume operator= copies resources correctly
// (but this isn't true for S)
void FN_use_after_scope1_bad() {
S s(1);
{
S tmp(2);
s = tmp; // translated as operator=(s, tmp)
} // destructor for tmp runs here
// destructor for s here; second time the value held by s has been destructed
}
void FN_use_after_scope2_bad() {
S s(1);
{
s = S(1);
} // destructor runs here, but our frontend currently doesn't insert it
}
struct POD {
int f;
};
// this code is ok since double-destructing POD struct is ok
void destruct_twice_ok() {
POD p{1};
{
POD tmp{2};
p = tmp;
} // destructor for tmp
} // destructor for p runs here, but it's harmless
class Subclass : virtual POD {
int* f;
Subclass() { f = new int; }
/** frontend code for this destructor will be:
* ~Subclass:
* __infer_inner_destructor_~Subclass(this)
* __infer_inner_destructor_~POD(this)
*
* __infer_inner_destructor_~Subclass:
* delete f;
*
* We need to be careful not to warn that this has been double-destructed
*/
~Subclass() { delete f; }
};
void FP_basic_placement_new_ok() {
S* ptr = new S(1);
S* tptr = new (ptr) S(1);
tptr->~S();
delete[] ptr;
}
S* destruct_pointer_contents_then_placement_new1_ok(S* s) {
s->~S();
new (s) S(1);
return s;
}
S* placement_new_aliasing1_bad() {
S* s = new S(1);
s->~S();
auto alias = new (s) S(2);
delete alias; // this deletes s too
return s; // bad, returning freed memory
}
S* placement_new_aliasing2_bad() {
S* s = new S(1);
s->~S();
auto alias = new (s) S(2);
delete s; // this deletes alias too
return alias; // bad, returning freed memory
}
void placement_new_non_var_ok() {
struct M {
S* s;
} m;
m.s = new S(1);
m.s->~S();
new (m.s) S(2);
delete m.s;
}
void return_placement_new_ok() {
auto mem = new S(1);
return new (mem) S(2);
}
void FP_destructor_in_loop_ok() {
for (int i = 0; i < 10; i++) {
S s(1);
}
}
int FN_use_after_scope3_bad() {
int* p;
{
int value = 3;
p = &value;
} // we do not know in the plugin that value is out of scope
return *p;
}
struct C {
C(int v) : f(v){};
~C();
int f;
};
int use_after_scope4_bad() {
C* pc;
{
C c(3);
pc = &c;
}
return pc->f;
}
struct B {
~B();
};
struct A {
~A() { (void)*f; }
const B* f;
};
void FN_destructor_order_bad() {
A a;
B b;
a.f = &b;
}
struct A2 {
~A2() {}
const B* f;
};
// need interprocedural analysis to fix this
void FP_destructor_order_empty_destructor_ok() {
A2 a;
B b;
a.f = &b;
}
Loading…
Cancel
Save