Migrate sanitizer related macros from port_def into internal functions in

port.h.
This reduces the cost of port_def.inc includes.

PiperOrigin-RevId: 696931317
pull/19182/head
Protobuf Team Bot 3 months ago committed by Copybara-Service
parent b6b774ee14
commit 1d55c2b52a
  1. 17
      csharp/src/Google.Protobuf/Reflection/FeatureSetDescriptor.g.cs
  2. 2
      src/google/protobuf/BUILD.bazel
  3. 12
      src/google/protobuf/arena.cc
  4. 31
      src/google/protobuf/arena_unittest.cc
  5. 2
      src/google/protobuf/generated_message_reflection.cc
  6. 9
      src/google/protobuf/generated_message_tctable_lite_test.cc
  7. 1
      src/google/protobuf/io/zero_copy_stream_unittest.cc
  8. 3
      src/google/protobuf/map_field.h
  9. 12
      src/google/protobuf/message.h
  10. 12
      src/google/protobuf/parse_context.h
  11. 57
      src/google/protobuf/port.h
  12. 53
      src/google/protobuf/port_def.inc
  13. 5
      src/google/protobuf/port_undef.inc
  14. 10
      src/google/protobuf/proto3_arena_unittest.cc
  15. 4
      src/google/protobuf/repeated_field.h
  16. 16
      src/google/protobuf/repeated_field_unittest.cc
  17. 1
      src/google/protobuf/repeated_ptr_field_unittest.cc
  18. 10
      src/google/protobuf/serial_arena.h

@ -1,17 +0,0 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#endregion
namespace Google.Protobuf.Reflection;
internal sealed partial class FeatureSetDescriptor
{
// Canonical serialized form of the edition defaults, generated by embed_edition_defaults.
private const string DefaultsBase64 =
"ChMYhAciACoMCAEQAhgCIAMoATACChMY5wciACoMCAIQARgBIAIoATABChMY6AciDAgBEAEYASACKAEwASoAIOYHKOgH";
}

@ -661,6 +661,7 @@ cc_library(
"//third_party/utf8_range:utf8_validity",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/base",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:dynamic_annotations",
"@com_google_absl//absl/cleanup",
@ -1965,6 +1966,7 @@ cc_test(
"//src/google/protobuf/stubs",
"//src/google/protobuf/testing",
"//src/google/protobuf/testing:file",
"@com_google_absl//absl/base:config",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/numeric:bits",
"@com_google_absl//absl/random",

@ -338,7 +338,7 @@ void SerialArena::AllocateNewBlock(size_t n) {
// Previous writes must take effect before writing new head.
head_.store(new_head, std::memory_order_release);
PROTOBUF_POISON_MEMORY_REGION(ptr(), limit_ - ptr());
internal::PoisonMemoryRegion(ptr(), limit_ - ptr());
}
uint64_t SerialArena::SpaceUsed() const {
@ -716,7 +716,7 @@ void ThreadSafeArena::UnpoisonAllArenaBlocks() const {
VisitSerialArena([](const SerialArena* serial) {
for (const auto* b = serial->head(); b != nullptr && !b->IsSentry();
b = b->next) {
PROTOBUF_UNPOISON_MEMORY_REGION(b, b->size);
internal::UnpoisonMemoryRegion(b, b->size);
}
});
}
@ -746,7 +746,7 @@ ThreadSafeArena::~ThreadSafeArena() {
auto mem = Free();
if (alloc_policy_.is_user_owned_initial_block()) {
// Unpoison the initial block, now that it's going back to the user.
PROTOBUF_UNPOISON_MEMORY_REGION(mem.p, mem.n);
internal::UnpoisonMemoryRegion(mem.p, mem.n);
} else if (mem.n > 0) {
GetDeallocator(alloc_policy_.get())(mem);
}
@ -922,9 +922,9 @@ template void*
ThreadSafeArena::AllocateAlignedFallback<AllocationClient::kArray>(size_t);
void ThreadSafeArena::CleanupList() {
#ifdef PROTOBUF_ASAN
UnpoisonAllArenaBlocks();
#endif
if constexpr (HasMemoryPoisoning()) {
UnpoisonAllArenaBlocks();
}
WalkSerialArenaChunk([](SerialArenaChunk* chunk) {
absl::Span<std::atomic<SerialArena*>> span = chunk->arenas();

@ -1539,13 +1539,13 @@ TEST(ArenaTest, ClearOneofMessageOnArena) {
child->set_moo_int(100);
message->clear_foo_message();
#ifndef PROTOBUF_ASAN
EXPECT_NE(child->moo_int(), 100);
#else
if (internal::HasMemoryPoisoning()) {
#if GTEST_HAS_DEATH_TEST
EXPECT_DEATH(EXPECT_EQ(child->moo_int(), 0), "use-after-poison");
EXPECT_DEATH(EXPECT_EQ(child->moo_int(), 0), "use-after-poison");
#endif // !GTEST_HAS_DEATH_TEST
#endif // !PROTOBUF_ASAN
} else {
EXPECT_NE(child->moo_int(), 100);
}
}
TEST(ArenaTest, CopyValuesWithinOneof) {
@ -1840,7 +1840,10 @@ TEST(ArenaTest, SpaceReuseForArraysSizeChecks) {
}
TEST(ArenaTest, SpaceReusePoisonsAndUnpoisonsMemory) {
#ifdef PROTOBUF_ASAN
if constexpr (!internal::HasMemoryPoisoning()) {
GTEST_SKIP() << "Memory poisoning not enabled.";
}
char buf[1024]{};
constexpr int kSize = 32;
{
@ -1849,19 +1852,21 @@ TEST(ArenaTest, SpaceReusePoisonsAndUnpoisonsMemory) {
for (int i = 0; i < 100; ++i) {
void* p = Arena::CreateArray<char>(&arena, kSize);
// Simulate other ASan client managing shadow memory.
ASAN_POISON_MEMORY_REGION(p, kSize);
ASAN_UNPOISON_MEMORY_REGION(p, kSize - 4);
internal::PoisonMemoryRegion(p, kSize);
internal::UnpoisonMemoryRegion(p, kSize - 4);
pointers.push_back(p);
}
for (void* p : pointers) {
internal::ArenaTestPeer::ReturnArrayMemory(&arena, p, kSize);
// The first one is not poisoned because it becomes the freelist.
if (p != pointers[0]) EXPECT_TRUE(__asan_address_is_poisoned(p));
if (p != pointers[0]) {
EXPECT_TRUE(internal::IsMemoryPoisoned(p));
}
}
bool found_poison = false;
for (char& c : buf) {
if (__asan_address_is_poisoned(&c)) {
if (internal::IsMemoryPoisoned(&c)) {
found_poison = true;
break;
}
@ -1871,12 +1876,8 @@ TEST(ArenaTest, SpaceReusePoisonsAndUnpoisonsMemory) {
// Should not be poisoned after destruction.
for (char& c : buf) {
ASSERT_FALSE(__asan_address_is_poisoned(&c));
ASSERT_FALSE(internal::IsMemoryPoisoned(&c));
}
#else // PROTOBUF_ASAN
GTEST_SKIP();
#endif // PROTOBUF_ASAN
}

@ -1374,7 +1374,7 @@ void Reflection::MaybePoisonAfterClear(Message& root) const {
for (auto it : nodes) {
(void)it;
PROTOBUF_POISON_MEMORY_REGION(it.ptr, it.size);
internal::PoisonMemoryRegion(it.ptr, it.size);
}
}

@ -25,6 +25,7 @@
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/message_lite.h"
#include "google/protobuf/parse_context.h"
#include "google/protobuf/port.h"
#include "google/protobuf/unittest.pb.h"
#include "google/protobuf/wire_format_lite.h"
@ -916,10 +917,10 @@ TEST(GeneratedMessageTctableLiteTest, PackedEnumSmallRange) {
// This test checks that the parser doesn't overflow an int32 when computing the
// array's new length.
TEST(GeneratedMessageTctableLiteTest, PackedEnumSmallRangeLargeSize) {
#ifdef PROTOBUF_MSAN
// This test attempts to allocate 8GB of memory, which OOMs MSAN.
return;
#endif
if constexpr (internal::HasAnySanitizer()) {
GTEST_SKIP() << "This test attempts to allocate 8GB of memory, which OOMs "
"in sanitizer mode.";
}
#ifdef _WIN32
// This test OOMs on Windows. I think this is because Windows is committing

@ -57,6 +57,7 @@
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/io_win32.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
#include "google/protobuf/port.h"
#include "google/protobuf/test_util2.h"
#if HAVE_ZLIB

@ -17,6 +17,7 @@
#include <utility>
#include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/hash/hash.h"
#include "absl/log/absl_check.h"
#include "absl/log/absl_log.h"
@ -437,7 +438,7 @@ class PROTOBUF_EXPORT MapFieldBase : public MapFieldBaseForParse {
// thread calls either ConstAccess() or MutableAccess(), on the same
// MapFieldBase-derived object, and there is no synchronization going
// on between them, tsan will alert.
#if defined(PROTOBUF_TSAN)
#if defined(ABSL_HAVE_THREAD_SANITIZER)
void ConstAccess() const { ABSL_CHECK_EQ(seq1_, seq2_); }
void MutableAccess() {
if (seq1_ & 1) {

@ -1604,12 +1604,12 @@ bool SplitFieldHasExtraIndirectionStatic(const FieldDescriptor* field) {
inline void MaybePoisonAfterClear(Message* root) {
if (root == nullptr) return;
#ifndef PROTOBUF_ASAN
root->Clear();
#else
const Reflection* reflection = root->GetReflection();
reflection->MaybePoisonAfterClear(*root);
#endif
if constexpr (HasMemoryPoisoning()) {
const Reflection* reflection = root->GetReflection();
reflection->MaybePoisonAfterClear(*root);
} else {
root->Clear();
}
}
} // namespace internal

@ -123,10 +123,10 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
// __asan_address_is_poisoned is allowed to have false negatives.
class LimitToken {
public:
LimitToken() { PROTOBUF_POISON_MEMORY_REGION(&token_, sizeof(token_)); }
LimitToken() { internal::PoisonMemoryRegion(&token_, sizeof(token_)); }
explicit LimitToken(int token) : token_(token) {
PROTOBUF_UNPOISON_MEMORY_REGION(&token_, sizeof(token_));
internal::UnpoisonMemoryRegion(&token_, sizeof(token_));
}
LimitToken(const LimitToken&) = delete;
@ -135,17 +135,17 @@ class PROTOBUF_EXPORT EpsCopyInputStream {
LimitToken(LimitToken&& other) { *this = std::move(other); }
LimitToken& operator=(LimitToken&& other) {
PROTOBUF_UNPOISON_MEMORY_REGION(&token_, sizeof(token_));
internal::UnpoisonMemoryRegion(&token_, sizeof(token_));
token_ = other.token_;
PROTOBUF_POISON_MEMORY_REGION(&other.token_, sizeof(token_));
internal::PoisonMemoryRegion(&other.token_, sizeof(token_));
return *this;
}
~LimitToken() { PROTOBUF_UNPOISON_MEMORY_REGION(&token_, sizeof(token_)); }
~LimitToken() { internal::UnpoisonMemoryRegion(&token_, sizeof(token_)); }
int token() && {
int t = token_;
PROTOBUF_POISON_MEMORY_REGION(&token_, sizeof(token_));
internal::PoisonMemoryRegion(&token_, sizeof(token_));
return t;
}

@ -29,6 +29,10 @@
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
#include <sanitizer/asan_interface.h>
#endif
// must be last
#include "google/protobuf/port_def.inc"
@ -258,9 +262,18 @@ inline constexpr bool DebugHardenClearOneofMessageOnArena() {
#endif
}
constexpr bool HasAnySanitizer() {
#if defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
defined(ABSL_HAVE_MEMORY_SANITIZER) || defined(ABSL_HAVE_THREAD_SANITIZER)
return true;
#else
return false;
#endif
}
constexpr bool PerformDebugChecks() {
#if defined(NDEBUG) && !defined(PROTOBUF_ASAN) && !defined(PROTOBUF_MSAN) && \
!defined(PROTOBUF_TSAN)
if (HasAnySanitizer()) return true;
#if defined(NDEBUG)
return false;
#else
return true;
@ -355,13 +368,45 @@ PROTOBUF_ALWAYS_INLINE void Prefetch5LinesFrom1Line(const void* ptr) {
}
#endif
#ifdef PROTOBUF_TSAN
constexpr bool HasMemoryPoisoning() {
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
return true;
#else
return false;
#endif
}
// Poison memory region when supported by sanitizer config.
inline void PoisonMemoryRegion(const void* p, size_t n) {
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
ASAN_POISON_MEMORY_REGION(p, n);
#else
// Nothing
#endif
}
inline void UnpoisonMemoryRegion(const void* p, size_t n) {
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
ASAN_UNPOISON_MEMORY_REGION(p, n);
#else
// Nothing
#endif
}
inline bool IsMemoryPoisoned(const void* p) {
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
return __asan_address_is_poisoned(p);
#else
return false;
#endif
}
#if defined(ABSL_HAVE_THREAD_SANITIZER)
// TODO: it would be preferable to use __tsan_external_read/
// __tsan_external_write, but they can cause dlopen issues.
template <typename T>
PROTOBUF_ALWAYS_INLINE void TSanRead(const T* impl) {
char protobuf_tsan_dummy =
*reinterpret_cast<const char*>(&impl->_tsan_detect_race);
char protobuf_tsan_dummy = impl->_tsan_detect_race;
asm volatile("" : "+r"(protobuf_tsan_dummy));
}
@ -370,7 +415,7 @@ PROTOBUF_ALWAYS_INLINE void TSanRead(const T* impl) {
// correctness of the rest of the class.
template <typename T>
PROTOBUF_ALWAYS_INLINE void TSanWrite(T* impl) {
*reinterpret_cast<char*>(&impl->_tsan_detect_race) = 0;
impl->_tsan_detect_race = 0;
}
#else
PROTOBUF_ALWAYS_INLINE void TSanRead(const void*) {}

@ -503,56 +503,9 @@ static_assert(PROTOBUF_ABSL_MIN(20230125, 3),
#define PROTOBUF_HAVE_ATTRIBUTE_WEAK 0
#endif
// Macros to detect sanitizers.
#ifdef PROTOBUF_ASAN
#error PROTOBUF_ASAN was previously defined
#endif
#ifdef PROTOBUF_MSAN
#error PROTOBUF_MSAN was previously defined
#endif
#ifdef PROTOBUF_TSAN
#error PROTOBUF_TSAN was previously defined
#endif
#if defined(__clang__)
#if ABSL_HAVE_FEATURE(address_sanitizer)
# define PROTOBUF_ASAN 1
# endif
#if ABSL_HAVE_FEATURE(thread_sanitizer)
# define PROTOBUF_TSAN 1
# endif
#if ABSL_HAVE_FEATURE(memory_sanitizer)
# define PROTOBUF_MSAN 1
# endif
#elif defined(__GNUC__)
// Double-guard is needed for -Wundef:
# ifdef __SANITIZE_ADDRESS__
# if __SANITIZE_ADDRESS__
# define PROTOBUF_ASAN 1
# endif
# endif
# ifdef __SANITIZE_THREAD__
# if __SANITIZE_THREAD__
# define PROTOBUF_TSAN 1
# endif
# endif
# ifdef __SANITIZE_MEMORY__
# if __SANITIZE_MEMORY__
# define PROTOBUF_ASAN 1
# endif
# endif
#endif
#ifdef PROTOBUF_ASAN
#include <sanitizer/asan_interface.h>
#define PROTOBUF_POISON_MEMORY_REGION(p, n) ASAN_POISON_MEMORY_REGION(p, n)
#define PROTOBUF_UNPOISON_MEMORY_REGION(p, n) ASAN_UNPOISON_MEMORY_REGION(p, n)
#else // PROTOBUF_ASAN
#define PROTOBUF_POISON_MEMORY_REGION(p, n)
#define PROTOBUF_UNPOISON_MEMORY_REGION(p, n)
#endif // PROTOBUF_ASAN
#ifdef PROTOBUF_TSAN
#define PROTOBUF_TSAN_DECLARE_MEMBER ::uint32_t _tsan_detect_race = 0;
// Variable used to inject artificial language races to detect API level races.
#if defined(ABSL_HAVE_THREAD_SANITIZER)
#define PROTOBUF_TSAN_DECLARE_MEMBER char _tsan_detect_race = 0;
#else
#define PROTOBUF_TSAN_DECLARE_MEMBER
#endif

@ -14,8 +14,6 @@
#endif
#undef PROTOBUF_PORT_
#undef PROTOBUF_POISON_MEMORY_REGION
#undef PROTOBUF_UNPOISON_MEMORY_REGION
#undef PROTOBUF_BUILTIN_ATOMIC
#undef PROTOBUF_GNUC_MIN
#undef PROTOBUF_CLANG_MIN
@ -52,9 +50,6 @@
#undef PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
#undef PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
#undef PROTOBUF_PRAGMA_INIT_SEG
#undef PROTOBUF_ASAN
#undef PROTOBUF_MSAN
#undef PROTOBUF_TSAN
#undef PROTOBUF_TSAN_DECLARE_MEMBER
#undef PROTOBUF_BUILTIN_CONSTANT_P
#undef PROTOBUF_CUSTOM_VTABLE

@ -287,13 +287,13 @@ TEST(Proto3ArenaTest, CheckOneofMessageFieldIsCleared) {
child->set_bb(100);
msg->Clear();
#ifndef PROTOBUF_ASAN
EXPECT_EQ(child->bb(), 0);
#else
if (internal::HasMemoryPoisoning()) {
#if GTEST_HAS_DEATH_TEST
EXPECT_DEATH(EXPECT_EQ(child->bb(), 100), "use-after-poison");
EXPECT_DEATH(EXPECT_EQ(child->bb(), 100), "use-after-poison");
#endif // !GTEST_HAS_DEATH_TEST
#endif // !PROTOBUF_ASAN
} else {
EXPECT_EQ(child->bb(), 0);
}
}
TEST(Proto3OptionalTest, OptionalFieldDescriptor) {

@ -560,8 +560,8 @@ class RepeatedField final
// We need to manually unpoison the SOO buffer because in reflection for
// split repeated fields, we poison the whole SOO buffer even when we
// don't actually use the whole SOO buffer (e.g. for RepeatedField<bool>).
PROTOBUF_UNPOISON_MEMORY_REGION(soo_rep_.short_rep.data,
sizeof(soo_rep_.short_rep.data));
internal::UnpoisonMemoryRegion(soo_rep_.short_rep.data,
sizeof(soo_rep_.short_rep.data));
}
}

@ -30,6 +30,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/base/config.h"
#include "absl/numeric/bits.h"
#include "absl/strings/cord.h"
#include "absl/types/span.h"
@ -38,6 +39,7 @@
#include "google/protobuf/io/coded_stream.h"
#include "google/protobuf/io/zero_copy_stream_impl_lite.h"
#include "google/protobuf/parse_context.h"
#include "google/protobuf/port.h"
// TODO: Remove.
#include "google/protobuf/repeated_ptr_field.h"
#include "google/protobuf/unittest.pb.h"
@ -474,9 +476,9 @@ TEST(RepeatedField, ReserveLarge) {
}
TEST(RepeatedField, ReserveHuge) {
#if defined(PROTOBUF_ASAN) || defined(PROTOBUF_MSAN)
GTEST_SKIP() << "Disabled because sanitizer is active";
#endif
if (internal::HasAnySanitizer()) {
GTEST_SKIP() << "Disabled because sanitizer is active";
}
// Largest value that does not clamp to the large limit:
constexpr int non_clamping_limit =
(std::numeric_limits<int>::max() - sizeof(Arena*)) / 2;
@ -1134,17 +1136,17 @@ TEST(RepeatedField, HardenAgainstBadTruncate) {
}
}
#if defined(GTEST_HAS_DEATH_TEST) && \
(defined(PROTOBUF_ASAN) || defined(PROTOBUF_MSAN))
#if defined(GTEST_HAS_DEATH_TEST) && (defined(ABSL_HAVE_ADDRESS_SANITIZER) || \
defined(ABSL_HAVE_MEMORY_SANITIZER))
// This function verifies that the code dies under ASAN or MSAN trying to both
// read and write the reserved element directly beyond the last element.
void VerifyDeathOnWriteAndReadAccessBeyondEnd(RepeatedField<int64_t>& field) {
auto* end = field.Mutable(field.size() - 1) + 1;
#if defined(PROTOBUF_ASAN)
#if defined(ABSL_HAVE_ADDRESS_SANITIZER)
EXPECT_DEATH(*end = 1, "container-overflow");
EXPECT_DEATH(EXPECT_NE(*end, 1), "container-overflow");
#elif defined(PROTOBUF_MSAN)
#elif defined(ABSL_HAVE_MEMORY_SANITIZER)
EXPECT_DEATH(EXPECT_NE(*end, 1), "use-of-uninitialized-value");
#endif

@ -22,6 +22,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "absl/base/config.h"
#include "absl/log/absl_check.h"
#include "absl/numeric/bits.h"
#include "absl/strings/str_cat.h"

@ -100,7 +100,7 @@ class PROTOBUF_EXPORT SerialArena {
if (cached_head == nullptr) return nullptr;
void* ret = cached_head;
PROTOBUF_UNPOISON_MEMORY_REGION(ret, size);
internal::UnpoisonMemoryRegion(ret, size);
cached_head = cached_head->next;
return ret;
}
@ -174,7 +174,7 @@ class PROTOBUF_EXPORT SerialArena {
// We need to unpoison this memory before filling it in case it has been
// poisoned by another sanitizer client.
PROTOBUF_UNPOISON_MEMORY_REGION(
internal::UnpoisonMemoryRegion(
new_list + cached_block_length_,
(new_size - cached_block_length_) * sizeof(CachedBlock*));
@ -193,7 +193,7 @@ class PROTOBUF_EXPORT SerialArena {
auto* new_node = static_cast<CachedBlock*>(p);
new_node->next = cached_head;
cached_head = new_node;
PROTOBUF_POISON_MEMORY_REGION(p, size);
internal::PoisonMemoryRegion(p, size);
}
public:
@ -209,7 +209,7 @@ class PROTOBUF_EXPORT SerialArena {
reinterpret_cast<uintptr_t>(limit_))) {
return false;
}
PROTOBUF_UNPOISON_MEMORY_REGION(ret, n);
internal::UnpoisonMemoryRegion(ret, n);
*out = ret;
char* next = ret + n;
set_ptr(next);
@ -235,7 +235,7 @@ class PROTOBUF_EXPORT SerialArena {
reinterpret_cast<uintptr_t>(limit_))) {
return AllocateAlignedWithCleanupFallback(n, align, destructor);
}
PROTOBUF_UNPOISON_MEMORY_REGION(ret, n);
internal::UnpoisonMemoryRegion(ret, n);
char* next = ret + n;
set_ptr(next);
AddCleanup(ret, destructor);

Loading…
Cancel
Save