Export of internal Abseil changes

--
79913a12f0cad4baf948430315aabf53f03b6475 by Abseil Team <absl-team@google.com>:

Don't inline (Un)LockSlow.

PiperOrigin-RevId: 302502344

--
6b340e80f0690655f24799c8de6707b3a95b8579 by Derek Mauro <dmauro@google.com>:

Add hardening assertions to absl::optional's dereference operators

PiperOrigin-RevId: 302492862

--
a9951bf4852d8c1aec472cb4b539830411270e4c by Derek Mauro <dmauro@google.com>:

Correctly add hardware AES compiler flags under Linux X86-64
Fixes #643

PiperOrigin-RevId: 302490673

--
314c3621ee4d57b6bc8d64338a1f1d48a69741d1 by Derek Mauro <dmauro@google.com>:

Upgrade to hardening assertions in absl::Span::remove_prefix and absl::Span::remove_suffix

PiperOrigin-RevId: 302481191

--
a142b8c6c62705c5f0d4fe3113150f0c0b7822b9 by Derek Mauro <dmauro@google.com>:

Update docker containers to Bazel 2.2.0, GCC 9.3, and new Clang snapshot

PiperOrigin-RevId: 302454042

--
afedeb70a2adc87010030c9ba6f06fe35ec26407 by Derek Mauro <dmauro@google.com>:

Add hardening assertions for the preconditions of absl::FixedArray

PiperOrigin-RevId: 302441767

--
44442bfbc0a9a742df32f07cee86a47712efb8b4 by Derek Mauro <dmauro@google.com>:

Fix new Clang warning about SpinLock doing operations on enums of different types

PiperOrigin-RevId: 302430387

--
69eaff7f97231779f696321c2ba8b88debf6dd9e by Derek Mauro <dmauro@google.com>:

Convert precondition assertions to ABSL_HARDENING_ASSERT for
absl::InlinedVector

PiperOrigin-RevId: 302427894

--
26b6db906a0942fd18583dc2cdd1bab32919d964 by Gennadiy Rozental <rogeeff@google.com>:

Internal change

PiperOrigin-RevId: 302425283

--
e62e81422979e922505d2cd9000e1de58123c088 by Derek Mauro <dmauro@google.com>:

Add an option to build Abseil in hardened mode

In hardened mode, the ABSL_HARDENING_ASSERT() macro is active even
when NDEBUG is defined. This allows Abseil to perform runtime checks
even in release mode. This should be used to implement things like
bounds checks that could otherwise lead to security vulnerabilities.

Use the new assertion in absl::string_view and absl::Span to test it.

PiperOrigin-RevId: 302119187
GitOrigin-RevId: 79913a12f0cad4baf948430315aabf53f03b6475
Change-Id: I0cc3341fd333a1df313167bab72dc5a759c4a048
This commit is contained in:
Abseil Team 2020-03-23 13:16:18 -07:00 committed by Xiaoyi Zhang
parent 092ed9793a
commit 518f17501e
22 changed files with 360 additions and 224 deletions

View file

@ -190,30 +190,32 @@ void SpinLock::SlowUnlock(uint32_t lock_value) {
// We use the upper 29 bits of the lock word to store the time spent waiting to // We use the upper 29 bits of the lock word to store the time spent waiting to
// acquire this lock. This is reported by contentionz profiling. Since the // acquire this lock. This is reported by contentionz profiling. Since the
// lower bits of the cycle counter wrap very quickly on high-frequency // lower bits of the cycle counter wrap very quickly on high-frequency
// processors we divide to reduce the granularity to 2^PROFILE_TIMESTAMP_SHIFT // processors we divide to reduce the granularity to 2^kProfileTimestampShift
// sized units. On a 4Ghz machine this will lose track of wait times greater // sized units. On a 4Ghz machine this will lose track of wait times greater
// than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare. // than (2^29/4 Ghz)*128 =~ 17.2 seconds. Such waits should be extremely rare.
enum { PROFILE_TIMESTAMP_SHIFT = 7 }; static constexpr int kProfileTimestampShift = 7;
enum { LOCKWORD_RESERVED_SHIFT = 3 }; // We currently reserve the lower 3 bits.
// We currently reserve the lower 3 bits.
static constexpr int kLockwordReservedShift = 3;
uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time, uint32_t SpinLock::EncodeWaitCycles(int64_t wait_start_time,
int64_t wait_end_time) { int64_t wait_end_time) {
static const int64_t kMaxWaitTime = static const int64_t kMaxWaitTime =
std::numeric_limits<uint32_t>::max() >> LOCKWORD_RESERVED_SHIFT; std::numeric_limits<uint32_t>::max() >> kLockwordReservedShift;
int64_t scaled_wait_time = int64_t scaled_wait_time =
(wait_end_time - wait_start_time) >> PROFILE_TIMESTAMP_SHIFT; (wait_end_time - wait_start_time) >> kProfileTimestampShift;
// Return a representation of the time spent waiting that can be stored in // Return a representation of the time spent waiting that can be stored in
// the lock word's upper bits. // the lock word's upper bits.
uint32_t clamped = static_cast<uint32_t>( uint32_t clamped = static_cast<uint32_t>(
std::min(scaled_wait_time, kMaxWaitTime) << LOCKWORD_RESERVED_SHIFT); std::min(scaled_wait_time, kMaxWaitTime) << kLockwordReservedShift);
if (clamped == 0) { if (clamped == 0) {
return kSpinLockSleeper; // Just wake waiters, but don't record contention. return kSpinLockSleeper; // Just wake waiters, but don't record contention.
} }
// Bump up value if necessary to avoid returning kSpinLockSleeper. // Bump up value if necessary to avoid returning kSpinLockSleeper.
const uint32_t kMinWaitTime = const uint32_t kMinWaitTime =
kSpinLockSleeper + (1 << LOCKWORD_RESERVED_SHIFT); kSpinLockSleeper + (1 << kLockwordReservedShift);
if (clamped == kSpinLockSleeper) { if (clamped == kSpinLockSleeper) {
return kMinWaitTime; return kMinWaitTime;
} }
@ -224,8 +226,7 @@ uint64_t SpinLock::DecodeWaitCycles(uint32_t lock_value) {
// Cast to uint32_t first to ensure bits [63:32] are cleared. // Cast to uint32_t first to ensure bits [63:32] are cleared.
const uint64_t scaled_wait_time = const uint64_t scaled_wait_time =
static_cast<uint32_t>(lock_value & kWaitTimeMask); static_cast<uint32_t>(lock_value & kWaitTimeMask);
return scaled_wait_time return scaled_wait_time << (kProfileTimestampShift - kLockwordReservedShift);
<< (PROFILE_TIMESTAMP_SHIFT - LOCKWORD_RESERVED_SHIFT);
} }
} // namespace base_internal } // namespace base_internal

View file

@ -148,12 +148,13 @@ class ABSL_LOCKABLE SpinLock {
// bit[1] encodes whether a lock uses cooperative scheduling. // bit[1] encodes whether a lock uses cooperative scheduling.
// bit[2] encodes whether a lock disables scheduling. // bit[2] encodes whether a lock disables scheduling.
// bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int. // bit[3:31] encodes time a lock spent on waiting as a 29-bit unsigned int.
enum { kSpinLockHeld = 1 }; static constexpr uint32_t kSpinLockHeld = 1;
enum { kSpinLockCooperative = 2 }; static constexpr uint32_t kSpinLockCooperative = 2;
enum { kSpinLockDisabledScheduling = 4 }; static constexpr uint32_t kSpinLockDisabledScheduling = 4;
enum { kSpinLockSleeper = 8 }; static constexpr uint32_t kSpinLockSleeper = 8;
enum { kWaitTimeMask = // Includes kSpinLockSleeper. // Includes kSpinLockSleeper.
~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling) }; static constexpr uint32_t kWaitTimeMask =
~(kSpinLockHeld | kSpinLockCooperative | kSpinLockDisabledScheduling);
// Returns true if the provided scheduling mode is cooperative. // Returns true if the provided scheduling mode is cooperative.
static constexpr bool IsCooperative( static constexpr bool IsCooperative(

View file

@ -32,6 +32,7 @@
#include <cstddef> #include <cstddef>
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/config.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/base/port.h" #include "absl/base/port.h"
@ -207,6 +208,40 @@ ABSL_NAMESPACE_END
: [] { assert(false && #expr); }()) // NOLINT : [] { assert(false && #expr); }()) // NOLINT
#endif #endif
// `ABSL_INTERNAL_HARDENING_ABORT()` controls how `ABSL_HARDENING_ASSERT()`
// aborts the program in release mode (when NDEBUG is defined). The
// implementation should abort the program as quickly as possible and ideally it
// should not be possible to ignore the abort request.
#if ABSL_HAVE_BUILTIN(__builtin_trap) || \
(defined(__GNUC__) && !defined(__clang__))
#define ABSL_INTERNAL_HARDENING_ABORT() \
do { \
__builtin_trap(); \
__builtin_unreachable(); \
} while (false)
#else
#define ABSL_INTERNAL_HARDENING_ABORT() abort()
#endif
// ABSL_HARDENING_ASSERT()
//
// `ABSL_HARDENED_ASSERT()` is like `ABSL_ASSERT()`, but used to implement
// runtime assertions that should be enabled in hardened builds even when
// `NDEBUG` is defined.
//
// When `NDEBUG` is not defined, `ABSL_HARDENED_ASSERT()` is identical to
// `ABSL_ASSERT()`.
//
// See `ABSL_OPTION_HARDENED` in `absl/base/options.h` for more information on
// hardened mode.
#if ABSL_OPTION_HARDENED == 1 && defined(NDEBUG)
#define ABSL_HARDENING_ASSERT(expr) \
(ABSL_PREDICT_TRUE((expr)) ? static_cast<void>(0) \
: [] { ABSL_INTERNAL_HARDENING_ABORT(); }())
#else
#define ABSL_HARDENING_ASSERT(expr) ABSL_ASSERT(expr)
#endif
#ifdef ABSL_HAVE_EXCEPTIONS #ifdef ABSL_HAVE_EXCEPTIONS
#define ABSL_INTERNAL_TRY try #define ABSL_INTERNAL_TRY try
#define ABSL_INTERNAL_CATCH_ANY catch (...) #define ABSL_INTERNAL_CATCH_ANY catch (...)

View file

@ -1,6 +1,3 @@
#ifndef ABSL_BASE_OPTIONS_H_
#define ABSL_BASE_OPTIONS_H_
// Copyright 2019 The Abseil Authors. // Copyright 2019 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -67,6 +64,9 @@
// proper Abseil implementation at compile-time, which will not be sufficient // proper Abseil implementation at compile-time, which will not be sufficient
// to guarantee ABI stability to package managers. // to guarantee ABI stability to package managers.
#ifndef ABSL_BASE_OPTIONS_H_
#define ABSL_BASE_OPTIONS_H_
// Include a standard library header to allow configuration based on the // Include a standard library header to allow configuration based on the
// standard library in use. // standard library in use.
#ifdef __cplusplus #ifdef __cplusplus
@ -208,4 +208,31 @@
#define ABSL_OPTION_USE_INLINE_NAMESPACE 0 #define ABSL_OPTION_USE_INLINE_NAMESPACE 0
#define ABSL_OPTION_INLINE_NAMESPACE_NAME head #define ABSL_OPTION_INLINE_NAMESPACE_NAME head
// ABSL_OPTION_HARDENED
//
// This option enables a "hardened" build in release mode (in this context,
// release mode is defined as a build where the `NDEBUG` macro is defined).
//
// A value of 0 means that "hardened" mode is not enabled.
//
// A value of 1 means that "hardened" mode is enabled.
//
// Hardened builds have additional security checks enabled when `NDEBUG` is
// defined. Defining `NDEBUG` is normally used to turn `assert()` macro into a
// no-op, as well as disabling other bespoke program consistency checks. By
// defining ABSL_OPTION_HARDENED to 1, a select set of checks remain enabled in
// release mode. These checks guard against programming errors that may lead to
// security vulnerabilities. In release mode, when one of these programming
// errors is encountered, the program will immediately abort, possibly without
// any attempt at logging.
//
// The checks enabled by this option are not free; they do incur runtime cost.
//
// The checks enabled by this option are always active when `NDEBUG` is not
// defined, even in the case when ABSL_OPTION_HARDENED is defined to 0. The
// checks enabled by this option may abort the program in a different way and
// log additional information when `NDEBUG` is not defined.
#define ABSL_OPTION_HARDENED 0
#endif // ABSL_BASE_OPTIONS_H_ #endif // ABSL_BASE_OPTIONS_H_

View file

@ -74,6 +74,7 @@ cc_test(
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [ deps = [
":fixed_array", ":fixed_array",
"//absl/base:config",
"//absl/base:exception_testing", "//absl/base:exception_testing",
"//absl/hash:hash_testing", "//absl/hash:hash_testing",
"//absl/memory", "//absl/memory",
@ -153,6 +154,7 @@ cc_test(
":counting_allocator", ":counting_allocator",
":inlined_vector", ":inlined_vector",
":test_instance_tracker", ":test_instance_tracker",
"//absl/base:config",
"//absl/base:core_headers", "//absl/base:core_headers",
"//absl/base:exception_testing", "//absl/base:exception_testing",
"//absl/base:raw_logging_internal", "//absl/base:raw_logging_internal",

View file

@ -147,6 +147,7 @@ absl_cc_test(
${ABSL_TEST_COPTS} ${ABSL_TEST_COPTS}
DEPS DEPS
absl::fixed_array absl::fixed_array
absl::config
absl::exception_testing absl::exception_testing
absl::hash_testing absl::hash_testing
absl::memory absl::memory
@ -221,6 +222,7 @@ absl_cc_test(
absl::counting_allocator absl::counting_allocator
absl::inlined_vector absl::inlined_vector
absl::test_instance_tracker absl::test_instance_tracker
absl::config
absl::core_headers absl::core_headers
absl::exception_testing absl::exception_testing
absl::hash_testing absl::hash_testing

View file

@ -217,7 +217,7 @@ class FixedArray {
// Returns a reference the ith element of the fixed array. // Returns a reference the ith element of the fixed array.
// REQUIRES: 0 <= i < size() // REQUIRES: 0 <= i < size()
reference operator[](size_type i) { reference operator[](size_type i) {
assert(i < size()); ABSL_HARDENING_ASSERT(i < size());
return data()[i]; return data()[i];
} }
@ -225,7 +225,7 @@ class FixedArray {
// ith element of the fixed array. // ith element of the fixed array.
// REQUIRES: 0 <= i < size() // REQUIRES: 0 <= i < size()
const_reference operator[](size_type i) const { const_reference operator[](size_type i) const {
assert(i < size()); ABSL_HARDENING_ASSERT(i < size());
return data()[i]; return data()[i];
} }
@ -252,20 +252,32 @@ class FixedArray {
// FixedArray::front() // FixedArray::front()
// //
// Returns a reference to the first element of the fixed array. // Returns a reference to the first element of the fixed array.
reference front() { return *begin(); } reference front() {
ABSL_HARDENING_ASSERT(!empty());
return data()[0];
}
// Overload of FixedArray::front() to return a reference to the first element // Overload of FixedArray::front() to return a reference to the first element
// of a fixed array of const values. // of a fixed array of const values.
const_reference front() const { return *begin(); } const_reference front() const {
ABSL_HARDENING_ASSERT(!empty());
return data()[0];
}
// FixedArray::back() // FixedArray::back()
// //
// Returns a reference to the last element of the fixed array. // Returns a reference to the last element of the fixed array.
reference back() { return *(end() - 1); } reference back() {
ABSL_HARDENING_ASSERT(!empty());
return data()[size() - 1];
}
// Overload of FixedArray::back() to return a reference to the last element // Overload of FixedArray::back() to return a reference to the last element
// of a fixed array of const values. // of a fixed array of const values.
const_reference back() const { return *(end() - 1); } const_reference back() const {
ABSL_HARDENING_ASSERT(!empty());
return data()[size() - 1];
}
// FixedArray::begin() // FixedArray::begin()
// //

View file

@ -28,6 +28,7 @@
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/internal/exception_testing.h" #include "absl/base/internal/exception_testing.h"
#include "absl/base/options.h"
#include "absl/hash/hash_testing.h" #include "absl/hash/hash_testing.h"
#include "absl/memory/memory.h" #include "absl/memory/memory.h"
@ -188,6 +189,21 @@ TEST(FixedArrayTest, AtThrows) {
"failed bounds check"); "failed bounds check");
} }
TEST(FixedArrayTest, Hardened) {
#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
absl::FixedArray<int> a = {1, 2, 3};
EXPECT_EQ(a[2], 3);
EXPECT_DEATH_IF_SUPPORTED(a[3], "");
EXPECT_DEATH_IF_SUPPORTED(a[-1], "");
absl::FixedArray<int> empty(0);
EXPECT_DEATH_IF_SUPPORTED(empty[0], "");
EXPECT_DEATH_IF_SUPPORTED(empty[-1], "");
EXPECT_DEATH_IF_SUPPORTED(empty.front(), "");
EXPECT_DEATH_IF_SUPPORTED(empty.back(), "");
#endif
}
TEST(FixedArrayRelationalsTest, EqualArrays) { TEST(FixedArrayRelationalsTest, EqualArrays) {
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
absl::FixedArray<int, 5> a1(i); absl::FixedArray<int, 5> a1(i);

View file

@ -48,6 +48,7 @@
#include "absl/algorithm/algorithm.h" #include "absl/algorithm/algorithm.h"
#include "absl/base/internal/throw_delegate.h" #include "absl/base/internal/throw_delegate.h"
#include "absl/base/macros.h"
#include "absl/base/optimization.h" #include "absl/base/optimization.h"
#include "absl/base/port.h" #include "absl/base/port.h"
#include "absl/container/internal/inlined_vector.h" #include "absl/container/internal/inlined_vector.h"
@ -307,16 +308,14 @@ class InlinedVector {
// //
// Returns a `reference` to the `i`th element of the inlined vector. // Returns a `reference` to the `i`th element of the inlined vector.
reference operator[](size_type i) { reference operator[](size_type i) {
assert(i < size()); ABSL_HARDENING_ASSERT(i < size());
return data()[i]; return data()[i];
} }
// Overload of `InlinedVector::operator[](...)` that returns a // Overload of `InlinedVector::operator[](...)` that returns a
// `const_reference` to the `i`th element of the inlined vector. // `const_reference` to the `i`th element of the inlined vector.
const_reference operator[](size_type i) const { const_reference operator[](size_type i) const {
assert(i < size()); ABSL_HARDENING_ASSERT(i < size());
return data()[i]; return data()[i];
} }
@ -331,7 +330,6 @@ class InlinedVector {
base_internal::ThrowStdOutOfRange( base_internal::ThrowStdOutOfRange(
"`InlinedVector::at(size_type)` failed bounds check"); "`InlinedVector::at(size_type)` failed bounds check");
} }
return data()[i]; return data()[i];
} }
@ -345,7 +343,6 @@ class InlinedVector {
base_internal::ThrowStdOutOfRange( base_internal::ThrowStdOutOfRange(
"`InlinedVector::at(size_type) const` failed bounds check"); "`InlinedVector::at(size_type) const` failed bounds check");
} }
return data()[i]; return data()[i];
} }
@ -353,16 +350,14 @@ class InlinedVector {
// //
// Returns a `reference` to the first element of the inlined vector. // Returns a `reference` to the first element of the inlined vector.
reference front() { reference front() {
assert(!empty()); ABSL_HARDENING_ASSERT(!empty());
return at(0); return at(0);
} }
// Overload of `InlinedVector::front()` that returns a `const_reference` to // Overload of `InlinedVector::front()` that returns a `const_reference` to
// the first element of the inlined vector. // the first element of the inlined vector.
const_reference front() const { const_reference front() const {
assert(!empty()); ABSL_HARDENING_ASSERT(!empty());
return at(0); return at(0);
} }
@ -370,16 +365,14 @@ class InlinedVector {
// //
// Returns a `reference` to the last element of the inlined vector. // Returns a `reference` to the last element of the inlined vector.
reference back() { reference back() {
assert(!empty()); ABSL_HARDENING_ASSERT(!empty());
return at(size() - 1); return at(size() - 1);
} }
// Overload of `InlinedVector::back()` that returns a `const_reference` to the // Overload of `InlinedVector::back()` that returns a `const_reference` to the
// last element of the inlined vector. // last element of the inlined vector.
const_reference back() const { const_reference back() const {
assert(!empty()); ABSL_HARDENING_ASSERT(!empty());
return at(size() - 1); return at(size() - 1);
} }
@ -573,8 +566,8 @@ class InlinedVector {
// of `v` starting at `pos`, returning an `iterator` pointing to the first of // of `v` starting at `pos`, returning an `iterator` pointing to the first of
// the newly inserted elements. // the newly inserted elements.
iterator insert(const_iterator pos, size_type n, const_reference v) { iterator insert(const_iterator pos, size_type n, const_reference v) {
assert(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
assert(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
if (ABSL_PREDICT_TRUE(n != 0)) { if (ABSL_PREDICT_TRUE(n != 0)) {
value_type dealias = v; value_type dealias = v;
@ -600,8 +593,8 @@ class InlinedVector {
EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr> EnableIfAtLeastForwardIterator<ForwardIterator>* = nullptr>
iterator insert(const_iterator pos, ForwardIterator first, iterator insert(const_iterator pos, ForwardIterator first,
ForwardIterator last) { ForwardIterator last) {
assert(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
assert(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
if (ABSL_PREDICT_TRUE(first != last)) { if (ABSL_PREDICT_TRUE(first != last)) {
return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first), return storage_.Insert(pos, IteratorValueAdapter<ForwardIterator>(first),
@ -619,8 +612,8 @@ class InlinedVector {
template <typename InputIterator, template <typename InputIterator,
DisableIfAtLeastForwardIterator<InputIterator>* = nullptr> DisableIfAtLeastForwardIterator<InputIterator>* = nullptr>
iterator insert(const_iterator pos, InputIterator first, InputIterator last) { iterator insert(const_iterator pos, InputIterator first, InputIterator last) {
assert(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
assert(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
size_type index = std::distance(cbegin(), pos); size_type index = std::distance(cbegin(), pos);
for (size_type i = index; first != last; ++i, static_cast<void>(++first)) { for (size_type i = index; first != last; ++i, static_cast<void>(++first)) {
@ -636,8 +629,8 @@ class InlinedVector {
// `pos`, returning an `iterator` pointing to the newly emplaced element. // `pos`, returning an `iterator` pointing to the newly emplaced element.
template <typename... Args> template <typename... Args>
iterator emplace(const_iterator pos, Args&&... args) { iterator emplace(const_iterator pos, Args&&... args) {
assert(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
assert(pos <= end()); ABSL_HARDENING_ASSERT(pos <= end());
value_type dealias(std::forward<Args>(args)...); value_type dealias(std::forward<Args>(args)...);
return storage_.Insert(pos, return storage_.Insert(pos,
@ -670,7 +663,7 @@ class InlinedVector {
// //
// Destroys the element at `back()`, reducing the size by `1`. // Destroys the element at `back()`, reducing the size by `1`.
void pop_back() noexcept { void pop_back() noexcept {
assert(!empty()); ABSL_HARDENING_ASSERT(!empty());
AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1)); AllocatorTraits::destroy(*storage_.GetAllocPtr(), data() + (size() - 1));
storage_.SubtractSize(1); storage_.SubtractSize(1);
@ -683,8 +676,8 @@ class InlinedVector {
// //
// NOTE: may return `end()`, which is not dereferencable. // NOTE: may return `end()`, which is not dereferencable.
iterator erase(const_iterator pos) { iterator erase(const_iterator pos) {
assert(pos >= begin()); ABSL_HARDENING_ASSERT(pos >= begin());
assert(pos < end()); ABSL_HARDENING_ASSERT(pos < end());
return storage_.Erase(pos, pos + 1); return storage_.Erase(pos, pos + 1);
} }
@ -695,9 +688,9 @@ class InlinedVector {
// //
// NOTE: may return `end()`, which is not dereferencable. // NOTE: may return `end()`, which is not dereferencable.
iterator erase(const_iterator from, const_iterator to) { iterator erase(const_iterator from, const_iterator to) {
assert(from >= begin()); ABSL_HARDENING_ASSERT(from >= begin());
assert(from <= to); ABSL_HARDENING_ASSERT(from <= to);
assert(to <= end()); ABSL_HARDENING_ASSERT(to <= end());
if (ABSL_PREDICT_TRUE(from != to)) { if (ABSL_PREDICT_TRUE(from != to)) {
return storage_.Erase(from, to); return storage_.Erase(from, to);

View file

@ -30,6 +30,7 @@
#include "absl/base/internal/exception_testing.h" #include "absl/base/internal/exception_testing.h"
#include "absl/base/internal/raw_logging.h" #include "absl/base/internal/raw_logging.h"
#include "absl/base/macros.h" #include "absl/base/macros.h"
#include "absl/base/options.h"
#include "absl/container/internal/counting_allocator.h" #include "absl/container/internal/counting_allocator.h"
#include "absl/container/internal/test_instance_tracker.h" #include "absl/container/internal/test_instance_tracker.h"
#include "absl/hash/hash_testing.h" #include "absl/hash/hash_testing.h"
@ -247,6 +248,16 @@ TEST(IntVec, Erase) {
} }
} }
TEST(IntVec, Hardened) {
IntVec v;
Fill(&v, 10);
EXPECT_EQ(v[9], 9);
#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
EXPECT_DEATH_IF_SUPPORTED(v[10], "");
EXPECT_DEATH_IF_SUPPORTED(v[-1], "");
#endif
}
// At the end of this test loop, the elements between [erase_begin, erase_end) // At the end of this test loop, the elements between [erase_begin, erase_end)
// should have reference counts == 0, and all others elements should have // should have reference counts == 0, and all others elements should have
// reference counts == 1. // reference counts == 1.

View file

@ -48,7 +48,7 @@ ABSL_RANDOM_RANDEN_COPTS = select({
":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_darwin": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, ":cpu_x64_windows_msvc": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS, ":cpu_x64_windows": ABSL_RANDOM_HWAES_MSVC_X64_FLAGS,
":cpu_haswell": ABSL_RANDOM_HWAES_X64_FLAGS, ":cpu_k8": ABSL_RANDOM_HWAES_X64_FLAGS,
":cpu_ppc": ["-mcrypto"], ":cpu_ppc": ["-mcrypto"],
# Supported by default or unsupported. # Supported by default or unsupported.
@ -65,7 +65,7 @@ def absl_random_randen_copts_init():
# These configs have consistent flags to enable HWAES intsructions. # These configs have consistent flags to enable HWAES intsructions.
cpu_configs = [ cpu_configs = [
"ppc", "ppc",
"haswell", "k8",
"darwin_x86_64", "darwin_x86_64",
"darwin", "darwin",
"x64_windows_msvc", "x64_windows_msvc",

View file

@ -74,6 +74,59 @@ class MutexRelock {
} // namespace } // namespace
///////////////////////////////////////////////////////////////////////////////
// Persistent state of the flag data.
class FlagImpl;
class FlagState : public flags_internal::FlagStateInterface {
public:
template <typename V>
FlagState(FlagImpl* flag_impl, const V& v, bool modified,
bool on_command_line, int64_t counter)
: flag_impl_(flag_impl),
value_(v),
modified_(modified),
on_command_line_(on_command_line),
counter_(counter) {}
~FlagState() override {
if (flag_impl_->ValueStorageKind() != FlagValueStorageKind::kHeapAllocated)
return;
flags_internal::Delete(flag_impl_->op_, value_.dynamic);
}
private:
friend class FlagImpl;
// Restores the flag to the saved state.
void Restore() const override {
if (!flag_impl_->RestoreState(*this)) return;
ABSL_INTERNAL_LOG(
INFO, absl::StrCat("Restore saved value of ", flag_impl_->Name(),
" to: ", flag_impl_->CurrentValue()));
}
// Flag and saved flag data.
FlagImpl* flag_impl_;
union SavedValue {
explicit SavedValue(void* v) : dynamic(v) {}
explicit SavedValue(int64_t v) : one_word(v) {}
explicit SavedValue(flags_internal::AlignedTwoWords v) : two_words(v) {}
void* dynamic;
int64_t one_word;
flags_internal::AlignedTwoWords two_words;
} value_;
bool modified_;
bool on_command_line_;
int64_t counter_;
};
///////////////////////////////////////////////////////////////////////////////
// Flag implementation, which does not depend on flag value type.
void FlagImpl::Init() { void FlagImpl::Init() {
new (&data_guard_) absl::Mutex; new (&data_guard_) absl::Mutex;
@ -86,13 +139,13 @@ void FlagImpl::Init() {
break; break;
case FlagValueStorageKind::kOneWordAtomic: { case FlagValueStorageKind::kOneWordAtomic: {
int64_t atomic_value; int64_t atomic_value;
std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_));
value_.one_word_atomic.store(atomic_value, std::memory_order_release); value_.one_word_atomic.store(atomic_value, std::memory_order_release);
break; break;
} }
case FlagValueStorageKind::kTwoWordsAtomic: { case FlagValueStorageKind::kTwoWordsAtomic: {
AlignedTwoWords atomic_value{0, 0}; AlignedTwoWords atomic_value{0, 0};
std::memcpy(&atomic_value, init_value.get(), Sizeof(op_)); std::memcpy(&atomic_value, init_value.get(), flags_internal::Sizeof(op_));
value_.two_words_atomic.store(atomic_value, std::memory_order_release); value_.two_words_atomic.store(atomic_value, std::memory_order_release);
break; break;
} }
@ -145,17 +198,17 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::MakeInitValue() const {
void FlagImpl::StoreValue(const void* src) { void FlagImpl::StoreValue(const void* src) {
switch (ValueStorageKind()) { switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: case FlagValueStorageKind::kHeapAllocated:
Copy(op_, src, value_.dynamic); flags_internal::Copy(op_, src, value_.dynamic);
break; break;
case FlagValueStorageKind::kOneWordAtomic: { case FlagValueStorageKind::kOneWordAtomic: {
int64_t one_word_val; int64_t one_word_val;
std::memcpy(&one_word_val, src, Sizeof(op_)); std::memcpy(&one_word_val, src, flags_internal::Sizeof(op_));
value_.one_word_atomic.store(one_word_val, std::memory_order_release); value_.one_word_atomic.store(one_word_val, std::memory_order_release);
break; break;
} }
case FlagValueStorageKind::kTwoWordsAtomic: { case FlagValueStorageKind::kTwoWordsAtomic: {
AlignedTwoWords two_words_val{0, 0}; AlignedTwoWords two_words_val{0, 0};
std::memcpy(&two_words_val, src, Sizeof(op_)); std::memcpy(&two_words_val, src, flags_internal::Sizeof(op_));
value_.two_words_atomic.store(two_words_val, std::memory_order_release); value_.two_words_atomic.store(two_words_val, std::memory_order_release);
break; break;
} }
@ -172,11 +225,17 @@ std::string FlagImpl::Filename() const {
return flags_internal::GetUsageConfig().normalize_filename(filename_); return flags_internal::GetUsageConfig().normalize_filename(filename_);
} }
absl::string_view FlagImpl::Typename() const { return ""; }
std::string FlagImpl::Help() const { std::string FlagImpl::Help() const {
return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal return HelpSourceKind() == FlagHelpKind::kLiteral ? help_.literal
: help_.gen_func(); : help_.gen_func();
} }
FlagStaticTypeId FlagImpl::TypeId() const {
return flags_internal::StaticTypeId(op_);
}
bool FlagImpl::IsModified() const { bool FlagImpl::IsModified() const {
absl::MutexLock l(DataGuard()); absl::MutexLock l(DataGuard());
return modified_; return modified_;
@ -195,10 +254,10 @@ std::string FlagImpl::DefaultValue() const {
} }
std::string FlagImpl::CurrentValue() const { std::string FlagImpl::CurrentValue() const {
DataGuard(); // Make sure flag initialized auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) { switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: { case FlagValueStorageKind::kHeapAllocated: {
absl::MutexLock l(DataGuard()); absl::MutexLock l(guard);
return flags_internal::Unparse(op_, value_.dynamic); return flags_internal::Unparse(op_, value_.dynamic);
} }
case FlagValueStorageKind::kOneWordAtomic: { case FlagValueStorageKind::kOneWordAtomic: {
@ -250,23 +309,53 @@ void FlagImpl::InvokeCallback() const {
cb(); cb();
} }
bool FlagImpl::RestoreState(const void* value, bool modified, std::unique_ptr<FlagStateInterface> FlagImpl::SaveState() {
bool on_command_line, int64_t counter) { absl::MutexLock l(DataGuard());
{
absl::MutexLock l(DataGuard());
if (counter_ == counter) return false; bool modified = modified_;
bool on_command_line = on_command_line_;
switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: {
return absl::make_unique<FlagState>(
this, flags_internal::Clone(op_, value_.dynamic), modified,
on_command_line, counter_);
}
case FlagValueStorageKind::kOneWordAtomic: {
return absl::make_unique<FlagState>(
this, value_.one_word_atomic.load(std::memory_order_acquire),
modified, on_command_line, counter_);
}
case FlagValueStorageKind::kTwoWordsAtomic: {
return absl::make_unique<FlagState>(
this, value_.two_words_atomic.load(std::memory_order_acquire),
modified, on_command_line, counter_);
}
}
return nullptr;
}
bool FlagImpl::RestoreState(const FlagState& flag_state) {
absl::MutexLock l(DataGuard());
if (flag_state.counter_ == counter_) {
return false;
} }
Write(value); switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated:
{ StoreValue(flag_state.value_.dynamic);
absl::MutexLock l(DataGuard()); break;
case FlagValueStorageKind::kOneWordAtomic:
modified_ = modified; StoreValue(&flag_state.value_.one_word);
on_command_line_ = on_command_line; break;
case FlagValueStorageKind::kTwoWordsAtomic:
StoreValue(&flag_state.value_.two_words);
break;
} }
modified_ = flag_state.modified_;
on_command_line_ = flag_state.on_command_line_;
return true; return true;
} }
@ -290,10 +379,10 @@ std::unique_ptr<void, DynValueDeleter> FlagImpl::TryParse(
} }
void FlagImpl::Read(void* dst) const { void FlagImpl::Read(void* dst) const {
DataGuard(); // Make sure flag initialized auto* guard = DataGuard(); // Make sure flag initialized
switch (ValueStorageKind()) { switch (ValueStorageKind()) {
case FlagValueStorageKind::kHeapAllocated: { case FlagValueStorageKind::kHeapAllocated: {
absl::MutexLock l(DataGuard()); absl::MutexLock l(guard);
flags_internal::CopyConstruct(op_, value_.dynamic, dst); flags_internal::CopyConstruct(op_, value_.dynamic, dst);
break; break;
@ -301,13 +390,13 @@ void FlagImpl::Read(void* dst) const {
case FlagValueStorageKind::kOneWordAtomic: { case FlagValueStorageKind::kOneWordAtomic: {
const auto one_word_val = const auto one_word_val =
value_.one_word_atomic.load(std::memory_order_acquire); value_.one_word_atomic.load(std::memory_order_acquire);
std::memcpy(dst, &one_word_val, Sizeof(op_)); std::memcpy(dst, &one_word_val, flags_internal::Sizeof(op_));
break; break;
} }
case FlagValueStorageKind::kTwoWordsAtomic: { case FlagValueStorageKind::kTwoWordsAtomic: {
const auto two_words_val = const auto two_words_val =
value_.two_words_atomic.load(std::memory_order_acquire); value_.two_words_atomic.load(std::memory_order_acquire);
std::memcpy(dst, &two_words_val, Sizeof(op_)); std::memcpy(dst, &two_words_val, flags_internal::Sizeof(op_));
break; break;
} }
} }

View file

@ -40,9 +40,6 @@ namespace absl {
ABSL_NAMESPACE_BEGIN ABSL_NAMESPACE_BEGIN
namespace flags_internal { namespace flags_internal {
template <typename T>
class Flag;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Flag value type operations, eg., parsing, copying, etc. are provided // Flag value type operations, eg., parsing, copying, etc. are provided
// by function specific to that type with a signature matching FlagOpFn. // by function specific to that type with a signature matching FlagOpFn.
@ -148,36 +145,6 @@ inline FlagStaticTypeId StaticTypeId(FlagOpFn op) {
op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr)); op(FlagOp::kStaticTypeId, nullptr, nullptr, nullptr));
} }
///////////////////////////////////////////////////////////////////////////////
// Persistent state of the flag data.
template <typename T>
class FlagState : public flags_internal::FlagStateInterface {
public:
FlagState(Flag<T>* flag, T&& cur, bool modified, bool on_command_line,
int64_t counter)
: flag_(flag),
cur_value_(std::move(cur)),
modified_(modified),
on_command_line_(on_command_line),
counter_(counter) {}
~FlagState() override = default;
private:
friend class Flag<T>;
// Restores the flag to the saved state.
void Restore() const override;
// Flag and saved flag data.
Flag<T>* flag_;
T cur_value_;
bool modified_;
bool on_command_line_;
int64_t counter_;
};
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Flag help auxiliary structs. // Flag help auxiliary structs.
@ -341,13 +308,15 @@ struct FlagCallback {
struct DynValueDeleter { struct DynValueDeleter {
explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {} explicit DynValueDeleter(FlagOpFn op_arg = nullptr) : op(op_arg) {}
void operator()(void* ptr) const { void operator()(void* ptr) const {
if (op != nullptr) Delete(op, ptr); if (op != nullptr) flags_internal::Delete(op, ptr);
} }
FlagOpFn op; FlagOpFn op;
}; };
class FlagImpl { class FlagState;
class FlagImpl final : public flags_internal::CommandLineFlag {
public: public:
constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op, constexpr FlagImpl(const char* name, const char* filename, FlagOpFn op,
FlagHelpArg help, FlagValueStorageKind value_kind, FlagHelpArg help, FlagValueStorageKind value_kind,
@ -368,15 +337,7 @@ class FlagImpl {
data_guard_{} {} data_guard_{} {}
// Constant access methods // Constant access methods
absl::string_view Name() const; void Read(void* dst) const override ABSL_LOCKS_EXCLUDED(*DataGuard());
std::string Filename() const;
std::string Help() const;
bool IsModified() const ABSL_LOCKS_EXCLUDED(*DataGuard());
bool IsSpecifiedOnCommandLine() const ABSL_LOCKS_EXCLUDED(*DataGuard());
std::string DefaultValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
std::string CurrentValue() const ABSL_LOCKS_EXCLUDED(*DataGuard());
void Read(void* dst) const ABSL_LOCKS_EXCLUDED(*DataGuard());
template <typename T, typename std::enable_if<FlagUseHeapStorage<T>::value, template <typename T, typename std::enable_if<FlagUseHeapStorage<T>::value,
int>::type = 0> int>::type = 0>
void Get(T* dst) const { void Get(T* dst) const {
@ -404,34 +365,12 @@ class FlagImpl {
// Mutating access methods // Mutating access methods
void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard()); void Write(const void* src) ABSL_LOCKS_EXCLUDED(*DataGuard());
bool ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* err)
ABSL_LOCKS_EXCLUDED(*DataGuard());
// Interfaces to operate on callbacks. // Interfaces to operate on callbacks.
void SetCallback(const FlagCallbackFunc mutation_callback) void SetCallback(const FlagCallbackFunc mutation_callback)
ABSL_LOCKS_EXCLUDED(*DataGuard()); ABSL_LOCKS_EXCLUDED(*DataGuard());
void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard()); void InvokeCallback() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(*DataGuard());
// Interfaces to save/restore mutable flag data
template <typename T>
std::unique_ptr<FlagStateInterface> SaveState(Flag<T>* flag) const
ABSL_LOCKS_EXCLUDED(*DataGuard()) {
T&& cur_value = flag->Get();
absl::MutexLock l(DataGuard());
return absl::make_unique<FlagState<T>>(
flag, std::move(cur_value), modified_, on_command_line_, counter_);
}
bool RestoreState(const void* value, bool modified, bool on_command_line,
int64_t counter) ABSL_LOCKS_EXCLUDED(*DataGuard());
// Value validation interfaces.
void CheckDefaultValueParsingRoundtrip() const
ABSL_LOCKS_EXCLUDED(*DataGuard());
bool ValidateInputValue(absl::string_view value) const
ABSL_LOCKS_EXCLUDED(*DataGuard());
// Used in read/write operations to validate source/target has correct type. // Used in read/write operations to validate source/target has correct type.
// For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to // For example if flag is declared as absl::Flag<int> FLAGS_foo, a call to
// absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed // absl::GetFlag(FLAGS_foo) validates that the type of FLAGS_foo is indeed
@ -441,6 +380,10 @@ class FlagImpl {
void AssertValidType(FlagStaticTypeId type_id) const; void AssertValidType(FlagStaticTypeId type_id) const;
private: private:
template <typename T>
friend class Flag;
friend class FlagState;
// Ensures that `data_guard_` is initialized and returns it. // Ensures that `data_guard_` is initialized and returns it.
absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_); absl::Mutex* DataGuard() const ABSL_LOCK_RETURNED((absl::Mutex*)&data_guard_);
// Returns heap allocated value of type T initialized with default value. // Returns heap allocated value of type T initialized with default value.
@ -467,6 +410,37 @@ class FlagImpl {
return static_cast<FlagDefaultKind>(def_kind_); return static_cast<FlagDefaultKind>(def_kind_);
} }
// CommandLineFlag interface implementation
absl::string_view Name() const override;
std::string Filename() const override;
absl::string_view Typename() const override;
std::string Help() const override;
FlagStaticTypeId TypeId() const override;
bool IsModified() const override ABSL_LOCKS_EXCLUDED(*DataGuard());
bool IsSpecifiedOnCommandLine() const override
ABSL_LOCKS_EXCLUDED(*DataGuard());
std::string DefaultValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard());
std::string CurrentValue() const override ABSL_LOCKS_EXCLUDED(*DataGuard());
bool ValidateInputValue(absl::string_view value) const override
ABSL_LOCKS_EXCLUDED(*DataGuard());
void CheckDefaultValueParsingRoundtrip() const override
ABSL_LOCKS_EXCLUDED(*DataGuard());
// Interfaces to save and restore flags to/from persistent state.
// Returns current flag state or nullptr if flag does not support
// saving and restoring a state.
std::unique_ptr<FlagStateInterface> SaveState() override
ABSL_LOCKS_EXCLUDED(*DataGuard());
// Restores the flag state to the supplied state object. If there is
// nothing to restore returns false. Otherwise returns true.
bool RestoreState(const FlagState& flag_state)
ABSL_LOCKS_EXCLUDED(*DataGuard());
bool ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* error) override
ABSL_LOCKS_EXCLUDED(*DataGuard());
// Immutable flag's state. // Immutable flag's state.
// Flags name passed to ABSL_FLAG as second arg. // Flags name passed to ABSL_FLAG as second arg.
@ -536,7 +510,7 @@ class FlagImpl {
// flag reflection handle interface. // flag reflection handle interface.
template <typename T> template <typename T>
class Flag final : public flags_internal::CommandLineFlag { class Flag {
public: public:
constexpr Flag(const char* name, const char* filename, const FlagHelpArg help, constexpr Flag(const char* name, const char* filename, const FlagHelpArg help,
const FlagDfltGenFunc default_value_gen) const FlagDfltGenFunc default_value_gen)
@ -568,60 +542,24 @@ class Flag final : public flags_internal::CommandLineFlag {
} }
// CommandLineFlag interface // CommandLineFlag interface
absl::string_view Name() const override { return impl_.Name(); } absl::string_view Name() const { return impl_.Name(); }
std::string Filename() const override { return impl_.Filename(); } std::string Filename() const { return impl_.Filename(); }
absl::string_view Typename() const override { return ""; } absl::string_view Typename() const { return ""; }
std::string Help() const override { return impl_.Help(); } std::string Help() const { return impl_.Help(); }
bool IsModified() const override { return impl_.IsModified(); } bool IsModified() const { return impl_.IsModified(); }
bool IsSpecifiedOnCommandLine() const override { bool IsSpecifiedOnCommandLine() const {
return impl_.IsSpecifiedOnCommandLine(); return impl_.IsSpecifiedOnCommandLine();
} }
std::string DefaultValue() const override { return impl_.DefaultValue(); } std::string DefaultValue() const { return impl_.DefaultValue(); }
std::string CurrentValue() const override { return impl_.CurrentValue(); } std::string CurrentValue() const { return impl_.CurrentValue(); }
bool ValidateInputValue(absl::string_view value) const override {
return impl_.ValidateInputValue(value);
}
// Interfaces to save and restore flags to/from persistent state.
// Returns current flag state or nullptr if flag does not support
// saving and restoring a state.
std::unique_ptr<FlagStateInterface> SaveState() override {
return impl_.SaveState(this);
}
// Restores the flag state to the supplied state object. If there is
// nothing to restore returns false. Otherwise returns true.
bool RestoreState(const FlagState<T>& flag_state) {
return impl_.RestoreState(&flag_state.cur_value_, flag_state.modified_,
flag_state.on_command_line_, flag_state.counter_);
}
bool ParseFrom(absl::string_view value, FlagSettingMode set_mode,
ValueSource source, std::string* error) override {
return impl_.ParseFrom(value, set_mode, source, error);
}
void CheckDefaultValueParsingRoundtrip() const override {
impl_.CheckDefaultValueParsingRoundtrip();
}
private: private:
friend class FlagState<T>; template <typename U, bool do_register>
friend class FlagRegistrar;
void Read(void* dst) const override { impl_.Read(dst); }
FlagStaticTypeId TypeId() const override { return &FlagStaticTypeIdGen<T>; }
// Flag's data // Flag's data
FlagImpl impl_; FlagImpl impl_;
}; };
template <typename T>
void FlagState<T>::Restore() const {
if (flag_->RestoreState(*this)) {
ABSL_INTERNAL_LOG(INFO,
absl::StrCat("Restore saved value of ", flag_->Name(),
" to: ", flag_->CurrentValue()));
}
}
// This class facilitates Flag object registration and tail expression-based // This class facilitates Flag object registration and tail expression-based
// flag definition, for example: // flag definition, for example:
// ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher); // ABSL_FLAG(int, foo, 42, "Foo help").OnUpdate(NotifyFooWatcher);
@ -629,7 +567,7 @@ template <typename T, bool do_register>
class FlagRegistrar { class FlagRegistrar {
public: public:
explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) { explicit FlagRegistrar(Flag<T>* flag) : flag_(flag) {
if (do_register) flags_internal::RegisterCommandLineFlag(flag_); if (do_register) flags_internal::RegisterCommandLineFlag(&flag_->impl_);
} }
FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && { FlagRegistrar& OnUpdate(FlagCallbackFunc cb) && {

View file

@ -283,7 +283,7 @@ class string_view {
// Returns the ith element of the `string_view` using the array operator. // Returns the ith element of the `string_view` using the array operator.
// Note that this operator does not perform any bounds checking. // Note that this operator does not perform any bounds checking.
constexpr const_reference operator[](size_type i) const { constexpr const_reference operator[](size_type i) const {
return ABSL_ASSERT(i < size()), ptr_[i]; return ABSL_HARDENING_ASSERT(i < size()), ptr_[i];
} }
// string_view::at() // string_view::at()
@ -303,14 +303,14 @@ class string_view {
// //
// Returns the first element of a `string_view`. // Returns the first element of a `string_view`.
constexpr const_reference front() const { constexpr const_reference front() const {
return ABSL_ASSERT(!empty()), ptr_[0]; return ABSL_HARDENING_ASSERT(!empty()), ptr_[0];
} }
// string_view::back() // string_view::back()
// //
// Returns the last element of a `string_view`. // Returns the last element of a `string_view`.
constexpr const_reference back() const { constexpr const_reference back() const {
return ABSL_ASSERT(!empty()), ptr_[size() - 1]; return ABSL_HARDENING_ASSERT(!empty()), ptr_[size() - 1];
} }
// string_view::data() // string_view::data()
@ -329,7 +329,7 @@ class string_view {
// Removes the first `n` characters from the `string_view`. Note that the // Removes the first `n` characters from the `string_view`. Note that the
// underlying string is not changed, only the view. // underlying string is not changed, only the view.
void remove_prefix(size_type n) { void remove_prefix(size_type n) {
assert(n <= length_); ABSL_HARDENING_ASSERT(n <= length_);
ptr_ += n; ptr_ += n;
length_ -= n; length_ -= n;
} }
@ -339,7 +339,7 @@ class string_view {
// Removes the last `n` characters from the `string_view`. Note that the // Removes the last `n` characters from the `string_view`. Note that the
// underlying string is not changed, only the view. // underlying string is not changed, only the view.
void remove_suffix(size_type n) { void remove_suffix(size_type n) {
assert(n <= length_); ABSL_HARDENING_ASSERT(n <= length_);
length_ -= n; length_ -= n;
} }
@ -520,7 +520,7 @@ class string_view {
(std::numeric_limits<difference_type>::max)(); (std::numeric_limits<difference_type>::max)();
static constexpr size_type CheckLengthInternal(size_type len) { static constexpr size_type CheckLengthInternal(size_type len) {
return (void)ABSL_ASSERT(len <= kMaxSize), len; return ABSL_HARDENING_ASSERT(len <= kMaxSize), len;
} }
static constexpr size_type StrlenInternal(const char* str) { static constexpr size_type StrlenInternal(const char* str) {

View file

@ -28,6 +28,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/dynamic_annotations.h" #include "absl/base/dynamic_annotations.h"
#include "absl/base/options.h"
#if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__) #if defined(ABSL_HAVE_STD_STRING_VIEW) || defined(__ANDROID__)
// We don't control the death messaging when using std::string_view. // We don't control the death messaging when using std::string_view.
@ -820,7 +821,7 @@ TEST(StringViewTest, FrontBackSingleChar) {
TEST(StringViewTest, FrontBackEmpty) { TEST(StringViewTest, FrontBackEmpty) {
#ifndef ABSL_USES_STD_STRING_VIEW #ifndef ABSL_USES_STD_STRING_VIEW
#ifndef NDEBUG #if !defined(NDEBUG) || ABSL_OPTION_HARDENED
// Abseil's string_view implementation has debug assertions that check that // Abseil's string_view implementation has debug assertions that check that
// front() and back() are not called on an empty string_view. // front() and back() are not called on an empty string_view.
absl::string_view sv; absl::string_view sv;
@ -1130,7 +1131,7 @@ TEST(StringViewTest, Noexcept) {
TEST(StringViewTest, BoundsCheck) { TEST(StringViewTest, BoundsCheck) {
#ifndef ABSL_USES_STD_STRING_VIEW #ifndef ABSL_USES_STD_STRING_VIEW
#ifndef NDEBUG #if !defined(NDEBUG) || ABSL_OPTION_HARDENED
// Abseil's string_view implementation has bounds-checking in debug mode. // Abseil's string_view implementation has bounds-checking in debug mode.
absl::string_view h = "hello"; absl::string_view h = "hello";
ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], ""); ABSL_EXPECT_DEATH_IF_SUPPORTED(h[5], "");

View file

@ -1751,7 +1751,8 @@ static const intptr_t ignore_waiting_writers[] = {
}; };
// Internal version of LockWhen(). See LockSlowWithDeadline() // Internal version of LockWhen(). See LockSlowWithDeadline()
void Mutex::LockSlow(MuHow how, const Condition *cond, int flags) { ABSL_ATTRIBUTE_NOINLINE void Mutex::LockSlow(MuHow how, const Condition *cond,
int flags) {
ABSL_RAW_CHECK( ABSL_RAW_CHECK(
this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags), this->LockSlowWithDeadline(how, cond, KernelTimeout::Never(), flags),
"condition untrue on return from LockSlow"); "condition untrue on return from LockSlow");
@ -2017,7 +2018,7 @@ void Mutex::LockSlowLoop(SynchWaitParams *waitp, int flags) {
// which holds the lock but is not runnable because its condition is false // which holds the lock but is not runnable because its condition is false
// or it is in the process of blocking on a condition variable; it must requeue // or it is in the process of blocking on a condition variable; it must requeue
// itself on the mutex/condvar to wait for its condition to become true. // itself on the mutex/condvar to wait for its condition to become true.
void Mutex::UnlockSlow(SynchWaitParams *waitp) { ABSL_ATTRIBUTE_NOINLINE void Mutex::UnlockSlow(SynchWaitParams *waitp) {
intptr_t v = mu_.load(std::memory_order_relaxed); intptr_t v = mu_.load(std::memory_order_relaxed);
this->AssertReaderHeld(); this->AssertReaderHeld();
CheckForMutexCorruption(v, "Unlock"); CheckForMutexCorruption(v, "Unlock");

View file

@ -412,11 +412,11 @@ class optional : private optional_internal::optional_data<T>,
// //
// If you need myOpt->foo in constexpr, use (*myOpt).foo instead. // If you need myOpt->foo in constexpr, use (*myOpt).foo instead.
const T* operator->() const { const T* operator->() const {
assert(this->engaged_); ABSL_HARDENING_ASSERT(this->engaged_);
return std::addressof(this->data_); return std::addressof(this->data_);
} }
T* operator->() { T* operator->() {
assert(this->engaged_); ABSL_HARDENING_ASSERT(this->engaged_);
return std::addressof(this->data_); return std::addressof(this->data_);
} }
@ -425,17 +425,17 @@ class optional : private optional_internal::optional_data<T>,
// Accesses the underlying `T` value of an `optional`. If the `optional` is // Accesses the underlying `T` value of an `optional`. If the `optional` is
// empty, behavior is undefined. // empty, behavior is undefined.
constexpr const T& operator*() const& { constexpr const T& operator*() const& {
return ABSL_ASSERT(this->engaged_), reference(); return ABSL_HARDENING_ASSERT(this->engaged_), reference();
} }
T& operator*() & { T& operator*() & {
assert(this->engaged_); ABSL_HARDENING_ASSERT(this->engaged_);
return reference(); return reference();
} }
constexpr const T&& operator*() const && { constexpr const T&& operator*() const && {
return absl::move(reference()); return ABSL_HARDENING_ASSERT(this->engaged_), absl::move(reference());
} }
T&& operator*() && { T&& operator*() && {
assert(this->engaged_); ABSL_HARDENING_ASSERT(this->engaged_);
return std::move(reference()); return std::move(reference());
} }

View file

@ -1057,8 +1057,7 @@ TEST(optionalTest, Value) {
// test constexpr value() // test constexpr value()
constexpr absl::optional<int> o1(1); constexpr absl::optional<int> o1(1);
static_assert(1 == o1.value(), ""); // const & static_assert(1 == o1.value(), ""); // const &
#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ #if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
!defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
using COI = const absl::optional<int>; using COI = const absl::optional<int>;
static_assert(2 == COI(2).value(), ""); // const && static_assert(2 == COI(2).value(), ""); // const &&
#endif #endif
@ -1098,8 +1097,7 @@ TEST(optionalTest, DerefOperator) {
constexpr absl::optional<int> opt1(1); constexpr absl::optional<int> opt1(1);
static_assert(*opt1 == 1, ""); static_assert(*opt1 == 1, "");
#if !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_MSVC_BUG) && \ #if !defined(_MSC_VER) && !defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
!defined(ABSL_SKIP_OVERLOAD_TEST_DUE_TO_GCC_BUG)
using COI = const absl::optional<int>; using COI = const absl::optional<int>;
static_assert(*COI(2) == 2, ""); static_assert(*COI(2) == 2, "");
#endif #endif

View file

@ -276,7 +276,7 @@ class Span {
// Returns a reference to the i'th element of this span. // Returns a reference to the i'th element of this span.
constexpr reference operator[](size_type i) const noexcept { constexpr reference operator[](size_type i) const noexcept {
// MSVC 2015 accepts this as constexpr, but not ptr_[i] // MSVC 2015 accepts this as constexpr, but not ptr_[i]
return ABSL_ASSERT(i < size()), *(data() + i); return ABSL_HARDENING_ASSERT(i < size()), *(data() + i);
} }
// Span::at() // Span::at()
@ -295,7 +295,7 @@ class Span {
// Returns a reference to the first element of this span. The span must not // Returns a reference to the first element of this span. The span must not
// be empty. // be empty.
constexpr reference front() const noexcept { constexpr reference front() const noexcept {
return ABSL_ASSERT(size() > 0), *data(); return ABSL_HARDENING_ASSERT(size() > 0), *data();
} }
// Span::back() // Span::back()
@ -303,7 +303,7 @@ class Span {
// Returns a reference to the last element of this span. The span must not // Returns a reference to the last element of this span. The span must not
// be empty. // be empty.
constexpr reference back() const noexcept { constexpr reference back() const noexcept {
return ABSL_ASSERT(size() > 0), *(data() + size() - 1); return ABSL_HARDENING_ASSERT(size() > 0), *(data() + size() - 1);
} }
// Span::begin() // Span::begin()
@ -368,7 +368,7 @@ class Span {
// //
// Removes the first `n` elements from the span. // Removes the first `n` elements from the span.
void remove_prefix(size_type n) noexcept { void remove_prefix(size_type n) noexcept {
assert(size() >= n); ABSL_HARDENING_ASSERT(size() >= n);
ptr_ += n; ptr_ += n;
len_ -= n; len_ -= n;
} }
@ -377,7 +377,7 @@ class Span {
// //
// Removes the last `n` elements from the span. // Removes the last `n` elements from the span.
void remove_suffix(size_type n) noexcept { void remove_suffix(size_type n) noexcept {
assert(size() >= n); ABSL_HARDENING_ASSERT(size() >= n);
len_ -= n; len_ -= n;
} }
@ -665,7 +665,7 @@ constexpr Span<T> MakeSpan(T* ptr, size_t size) noexcept {
template <int&... ExplicitArgumentBarrier, typename T> template <int&... ExplicitArgumentBarrier, typename T>
Span<T> MakeSpan(T* begin, T* end) noexcept { Span<T> MakeSpan(T* begin, T* end) noexcept {
return ABSL_ASSERT(begin <= end), Span<T>(begin, end - begin); return ABSL_HARDENING_ASSERT(begin <= end), Span<T>(begin, end - begin);
} }
template <int&... ExplicitArgumentBarrier, typename C> template <int&... ExplicitArgumentBarrier, typename C>
@ -710,7 +710,7 @@ constexpr Span<const T> MakeConstSpan(T* ptr, size_t size) noexcept {
template <int&... ExplicitArgumentBarrier, typename T> template <int&... ExplicitArgumentBarrier, typename T>
Span<const T> MakeConstSpan(T* begin, T* end) noexcept { Span<const T> MakeConstSpan(T* begin, T* end) noexcept {
return ABSL_ASSERT(begin <= end), Span<const T>(begin, end - begin); return ABSL_HARDENING_ASSERT(begin <= end), Span<const T>(begin, end - begin);
} }
template <int&... ExplicitArgumentBarrier, typename C> template <int&... ExplicitArgumentBarrier, typename C>

View file

@ -27,6 +27,7 @@
#include "absl/base/attributes.h" #include "absl/base/attributes.h"
#include "absl/base/config.h" #include "absl/base/config.h"
#include "absl/base/internal/exception_testing.h" #include "absl/base/internal/exception_testing.h"
#include "absl/base/options.h"
#include "absl/container/fixed_array.h" #include "absl/container/fixed_array.h"
#include "absl/container/inlined_vector.h" #include "absl/container/inlined_vector.h"
#include "absl/hash/hash_testing.h" #include "absl/hash/hash_testing.h"
@ -233,7 +234,7 @@ TEST(IntSpan, ElementAccess) {
EXPECT_EQ(s.front(), s[0]); EXPECT_EQ(s.front(), s[0]);
EXPECT_EQ(s.back(), s[9]); EXPECT_EQ(s.back(), s[9]);
#ifndef NDEBUG #if !defined(NDEBUG) || ABSL_OPTION_HARDENED
EXPECT_DEATH_IF_SUPPORTED(s[-1], ""); EXPECT_DEATH_IF_SUPPORTED(s[-1], "");
EXPECT_DEATH_IF_SUPPORTED(s[10], ""); EXPECT_DEATH_IF_SUPPORTED(s[10], "");
#endif #endif
@ -273,6 +274,13 @@ TEST(IntSpan, RemovePrefixAndSuffix) {
EXPECT_EQ(s.size(), 0); EXPECT_EQ(s.size(), 0);
EXPECT_EQ(v, MakeRamp(20, 1)); EXPECT_EQ(v, MakeRamp(20, 1));
#if !defined(NDEBUG) || ABSL_OPTION_HARDENED
absl::Span<int> prefix_death(v);
EXPECT_DEATH_IF_SUPPORTED(prefix_death.remove_prefix(21), "");
absl::Span<int> suffix_death(v);
EXPECT_DEATH_IF_SUPPORTED(suffix_death.remove_suffix(21), "");
#endif
} }
TEST(IntSpan, Subspan) { TEST(IntSpan, Subspan) {

View file

@ -1,6 +1,3 @@
#ifndef ABSL_BASE_OPTIONS_H_
#define ABSL_BASE_OPTIONS_H_
// Copyright 2019 The Abseil Authors. // Copyright 2019 The Abseil Authors.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
@ -18,11 +15,15 @@
// Alternate options.h file, used in continuous integration testing to exercise // Alternate options.h file, used in continuous integration testing to exercise
// option settings not used by default. // option settings not used by default.
#ifndef ABSL_BASE_OPTIONS_H_
#define ABSL_BASE_OPTIONS_H_
#define ABSL_OPTION_USE_STD_ANY 0 #define ABSL_OPTION_USE_STD_ANY 0
#define ABSL_OPTION_USE_STD_OPTIONAL 0 #define ABSL_OPTION_USE_STD_OPTIONAL 0
#define ABSL_OPTION_USE_STD_STRING_VIEW 0 #define ABSL_OPTION_USE_STD_STRING_VIEW 0
#define ABSL_OPTION_USE_STD_VARIANT 0 #define ABSL_OPTION_USE_STD_VARIANT 0
#define ABSL_OPTION_USE_INLINE_NAMESPACE 1 #define ABSL_OPTION_USE_INLINE_NAMESPACE 1
#define ABSL_OPTION_INLINE_NAMESPACE_NAME ns #define ABSL_OPTION_INLINE_NAMESPACE_NAME ns
#define ABSL_OPTION_HARDENED 1
#endif // ABSL_BASE_OPTIONS_H_ #endif // ABSL_BASE_OPTIONS_H_

View file

@ -16,6 +16,6 @@
# Test scripts should source this file to get the identifiers. # Test scripts should source this file to get the identifiers.
readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016" readonly LINUX_ALPINE_CONTAINER="gcr.io/google.com/absl-177019/alpine:20191016"
readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200102" readonly LINUX_CLANG_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_clang-latest:20200319"
readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20200102" readonly LINUX_GCC_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-latest:20200319"
readonly LINUX_GCC_49_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018" readonly LINUX_GCC_49_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-4.9:20191018"