Commit eb02c609 authored by Nick Terrell's avatar Nick Terrell Committed by Facebook Github Bot

Add leak sanitize annotations

Summary:
Add a function `folly::annotate_object_leaked(void const*)`
that suppresses LSAN warnings for the passed pointer. It does so
by keeping a static map of all the pointers passed to the function.
Objects can later be unsuppressed with
`annotate_object_collected(void const*)`.

When ASAN is disabled the function is an inlinable no-op.

This function is useful when you want to intentionally leak
memory, and LSAN isn't smart enough to detect the pointer
stored in memory. For example at the time of writing LSAN
cannot follow the pointer in `folly::atomic_shared_ptr`.

Reviewed By: yfeldblum

Differential Revision: D15755788

fbshipit-source-id: d9be0cfb253f1b89d691892eb085c0b2349c4438
parent cbca4ccd
/*
* Copyright 2016-present Facebook, Inc.
*
* 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
*
* http://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.
*/
#include <mutex>
#include <unordered_set>
#include <folly/memory/SanitizeLeak.h>
namespace folly {
namespace detail {
namespace {
struct LeakedPtrs {
std::mutex mutex;
std::unordered_set<void const*> set;
static LeakedPtrs& instance() {
static auto* ptrs = new LeakedPtrs();
return *ptrs;
}
};
} // namespace
void annotate_object_leaked_impl(void const* ptr) {
if (ptr == nullptr) {
return;
}
auto& ptrs = LeakedPtrs::instance();
std::lock_guard<std::mutex> lg(ptrs.mutex);
ptrs.set.insert(ptr);
}
void annotate_object_collected_impl(void const* ptr) {
if (ptr == nullptr) {
return;
}
auto& ptrs = LeakedPtrs::instance();
std::lock_guard<std::mutex> lg(ptrs.mutex);
ptrs.set.erase(ptr);
}
} // namespace detail
} // namespace folly
/*
* Copyright 2016-present Facebook, Inc.
*
* 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
*
* http://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.
*/
#pragma once
#include <folly/Portability.h>
namespace folly {
namespace detail {
void annotate_object_leaked_impl(void const* ptr);
void annotate_object_collected_impl(void const* ptr);
} // namespace detail
/**
* When the current compilation unit is being compiled with ASAN enabled this
* function will suppress an intentionally leaked pointer from the LSAN report.
* Otherwise, this function is an inlinable no-op.
*
* NOTE: This function will suppress LSAN leak reporting when the current
* compilation unit is being compiled with ASAN, independent of whether folly
* itself was compiled with ASAN enabled.
*/
FOLLY_ALWAYS_INLINE static void annotate_object_leaked(void const* ptr) {
if (kIsSanitizeAddress) {
detail::annotate_object_leaked_impl(static_cast<void const*>(ptr));
}
}
/**
* Annotate that an object previously passed to annotate_object_leaked() has
* been collected, so we should no longer suppress it from the LSAN report.
* This function is an inlinable no-op when ASAN is not enabled for the current
* compilation unit.
*/
FOLLY_ALWAYS_INLINE static void annotate_object_collected(void const* ptr) {
if (kIsSanitizeAddress) {
detail::annotate_object_collected_impl(static_cast<void const*>(ptr));
}
}
} // namespace folly
/*
* Copyright 2017-present Facebook, Inc.
*
* 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
*
* http://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.
*/
#include <array>
#include <memory>
#include <thread>
#include <folly/memory/SanitizeLeak.h>
#include <folly/portability/GTest.h>
namespace folly {
TEST(SanitizeLeak, ImplementationAlwaysWorks) {
int* ptr = new int(5);
EXPECT_EQ(*ptr, 5);
detail::annotate_object_leaked_impl(ptr);
}
TEST(SanitizeLeak, Basic) {
int* ptr = new int(5);
EXPECT_EQ(*ptr, 5);
annotate_object_leaked(ptr);
}
TEST(SanitizeLeak, TwoLeaks) {
int* ptr0 = new int(0);
int* ptr1 = new int(1);
EXPECT_EQ(*ptr0, 0);
EXPECT_EQ(*ptr1, 1);
annotate_object_leaked(ptr0);
annotate_object_leaked(ptr1);
}
TEST(SanitizeLeak, Nullptr) {
annotate_object_leaked(nullptr);
annotate_object_collected(nullptr);
detail::annotate_object_leaked_impl(nullptr);
detail::annotate_object_collected_impl(nullptr);
}
TEST(SanitizeLeak, NotLeaked) {
int* ptr0 = new int(0);
annotate_object_leaked(ptr0);
{
int* ptr1 = new int(0);
delete ptr0;
annotate_object_collected(ptr0);
ptr0 = ptr1;
}
annotate_object_leaked(ptr0);
}
TEST(SanitizeLeak, Concurrent) {
std::vector<std::thread> threads;
for (size_t t = 0; t < 8; ++t) {
threads.emplace_back([]() {
for (size_t i = 0; i < 1000; ++i) {
annotate_object_leaked(new int(2 * i));
// Call the impl directly so we can test it in TSAN mode
detail::annotate_object_leaked_impl(new int(2 * i + 1));
int* x = new int(i);
annotate_object_leaked(x);
delete x;
annotate_object_collected(x);
detail::annotate_object_collected_impl(x);
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
} // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment