Skip to content

Commit c115b14

Browse files
committed
[libc++][hardening] Always enable all checks during constant evaluation
1 parent cf11eb6 commit c115b14

File tree

3 files changed

+115
-30
lines changed

3 files changed

+115
-30
lines changed

libcxx/docs/ReleaseNotes/20.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Improvements and New Features
5151

5252
- The ``_LIBCPP_ENABLE_CXX20_REMOVED_UNCAUGHT_EXCEPTION`` macro has been added to make ``std::uncaught_exception`` available in C++20 and later modes.
5353

54+
- ``_LIBCPP_ASSUME(expression)`` now checks the expression is ``true`` during constant evaluation.
55+
This means that all assertions are now checked regardless of hardening mode in constant expressions.
5456

5557
Deprecations and Removals
5658
-------------------------

libcxx/include/__assert

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,45 @@
1717
# pragma GCC system_header
1818
#endif
1919

20+
_LIBCPP_BEGIN_NAMESPACE_STD
21+
22+
_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR int
23+
__assertion_failed_during_constant_evaluation(bool __value, const char*) _NOEXCEPT {
24+
return __value ? 0 : (__builtin_unreachable(), 0);
25+
}
26+
27+
_LIBCPP_END_NAMESPACE_STD
28+
29+
#define _LIBCPP_ASSERT_IS_CONSTANT_EVALUATED \
30+
(_LIBCPP_DIAGNOSTIC_PUSH _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wconstant-evaluated") __builtin_is_constant_evaluated() \
31+
_LIBCPP_DIAGNOSTIC_POP)
32+
33+
#define _LIBCPP_ASSERTTION_MESSAGE(__message) \
34+
__FILE__ ":" _LIBCPP_TOSTRING(__LINE__) ": assertion " _LIBCPP_TOSTRING(expression) " failed: " __message "\n"
35+
2036
#define _LIBCPP_ASSERT(expression, message) \
21-
(__builtin_expect(static_cast<bool>(expression), 1) \
22-
? (void)0 \
23-
: _LIBCPP_ASSERTION_HANDLER(__FILE__ ":" _LIBCPP_TOSTRING(__LINE__) ": assertion " _LIBCPP_TOSTRING( \
24-
expression) " failed: " message "\n"))
37+
(__builtin_expect(static_cast<bool>(expression), 1) ? (void)0 \
38+
: _LIBCPP_ASSERT_IS_CONSTANT_EVALUATED \
39+
? (void)__assertion_failed_during_constant_evaluation(false, _LIBCPP_ASSERTTION_MESSAGE(message)) \
40+
: _LIBCPP_ASSERTION_HANDLER(_LIBCPP_ASSERTTION_MESSAGE(message)))
2541

2642
// TODO: __builtin_assume can currently inhibit optimizations. Until this has been fixed and we can add
2743
// assumptions without a clear optimization intent, disable that to avoid worsening the code generation.
2844
// See https://discourse.llvm.org/t/llvm-assume-blocks-optimization/71609 for a discussion.
2945
#if 0 && __has_builtin(__builtin_assume)
30-
# define _LIBCPP_ASSUME(expression) \
46+
# define _LIBCPP_RUNTIME_ASSUME(expression) \
3147
(_LIBCPP_DIAGNOSTIC_PUSH _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wassume") \
3248
__builtin_assume(static_cast<bool>(expression)) _LIBCPP_DIAGNOSTIC_POP)
3349
#else
34-
# define _LIBCPP_ASSUME(expression) ((void)0)
50+
# define _LIBCPP_RUNTIME_ASSUME(expression) ((void)0)
3551
#endif
3652

53+
#define _LIBCPP_ASSUME(expression, message) \
54+
(_LIBCPP_ASSERT_IS_CONSTANT_EVALUATED \
55+
? (void)__assertion_failed_during_constant_evaluation( \
56+
static_cast<bool>(expression), _LIBCPP_ASSERTTION_MESSAGE(message)) \
57+
: _LIBCPP_RUNTIME_ASSUME(expression))
58+
3759
// clang-format off
3860
// Fast hardening mode checks.
3961

@@ -44,18 +66,18 @@
4466
# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSERT(expression, message)
4567
// Disabled checks.
4668
// On most modern platforms, dereferencing a null pointer does not lead to an actual memory access.
47-
# define _LIBCPP_ASSERT_NON_NULL(expression, message) _LIBCPP_ASSUME(expression)
69+
# define _LIBCPP_ASSERT_NON_NULL(expression, message) _LIBCPP_ASSUME(expression, message)
4870
// Overlapping ranges will make algorithms produce incorrect results but don't directly lead to a security
4971
// vulnerability.
50-
# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression)
51-
# define _LIBCPP_ASSERT_VALID_DEALLOCATION(expression, message) _LIBCPP_ASSUME(expression)
52-
# define _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(expression, message) _LIBCPP_ASSUME(expression)
53-
# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression)
54-
# define _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(expression, message) _LIBCPP_ASSUME(expression)
55-
# define _LIBCPP_ASSERT_PEDANTIC(expression, message) _LIBCPP_ASSUME(expression)
56-
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression)
57-
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression)
58-
# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression)
72+
# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression, message)
73+
# define _LIBCPP_ASSERT_VALID_DEALLOCATION(expression, message) _LIBCPP_ASSUME(expression, message)
74+
# define _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(expression, message) _LIBCPP_ASSUME(expression, message)
75+
# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression, message)
76+
# define _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(expression, message) _LIBCPP_ASSUME(expression, message)
77+
# define _LIBCPP_ASSERT_PEDANTIC(expression, message) _LIBCPP_ASSUME(expression, message)
78+
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression, message)
79+
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression, message)
80+
# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression, message)
5981

6082
// Extensive hardening mode checks.
6183

@@ -73,8 +95,8 @@
7395
# define _LIBCPP_ASSERT_PEDANTIC(expression, message) _LIBCPP_ASSERT(expression, message)
7496
# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSERT(expression, message)
7597
// Disabled checks.
76-
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression)
77-
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression)
98+
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression, message)
99+
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression, message)
78100

79101
// Debug hardening mode checks.
80102

@@ -99,18 +121,18 @@
99121
#else
100122

101123
// All checks disabled.
102-
# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSUME(expression)
103-
# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSUME(expression)
104-
# define _LIBCPP_ASSERT_NON_NULL(expression, message) _LIBCPP_ASSUME(expression)
105-
# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression)
106-
# define _LIBCPP_ASSERT_VALID_DEALLOCATION(expression, message) _LIBCPP_ASSUME(expression)
107-
# define _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(expression, message) _LIBCPP_ASSUME(expression)
108-
# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression)
109-
# define _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(expression, message) _LIBCPP_ASSUME(expression)
110-
# define _LIBCPP_ASSERT_PEDANTIC(expression, message) _LIBCPP_ASSUME(expression)
111-
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression)
112-
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression)
113-
# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression)
124+
# define _LIBCPP_ASSERT_VALID_INPUT_RANGE(expression, message) _LIBCPP_ASSUME(expression, message)
125+
# define _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(expression, message) _LIBCPP_ASSUME(expression, message)
126+
# define _LIBCPP_ASSERT_NON_NULL(expression, message) _LIBCPP_ASSUME(expression, message)
127+
# define _LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(expression, message) _LIBCPP_ASSUME(expression, message)
128+
# define _LIBCPP_ASSERT_VALID_DEALLOCATION(expression, message) _LIBCPP_ASSUME(expression, message)
129+
# define _LIBCPP_ASSERT_VALID_EXTERNAL_API_CALL(expression, message) _LIBCPP_ASSUME(expression, message)
130+
# define _LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(expression, message) _LIBCPP_ASSUME(expression, message)
131+
# define _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(expression, message) _LIBCPP_ASSUME(expression, message)
132+
# define _LIBCPP_ASSERT_PEDANTIC(expression, message) _LIBCPP_ASSUME(expression, message)
133+
# define _LIBCPP_ASSERT_SEMANTIC_REQUIREMENT(expression, message) _LIBCPP_ASSUME(expression, message)
134+
# define _LIBCPP_ASSERT_INTERNAL(expression, message) _LIBCPP_ASSUME(expression, message)
135+
# define _LIBCPP_ASSERT_UNCATEGORIZED(expression, message) _LIBCPP_ASSUME(expression, message)
114136

115137
#endif // _LIBCPP_HARDENING_MODE == _LIBCPP_HARDENING_MODE_FAST
116138
// clang-format on
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// Make sure that both `_LIBCPP_ASSERT(false, ...)` and `_LIBCPP_ASSUME(false)`
10+
// mean that a constant expression cannot be formed.
11+
12+
#include <__assert>
13+
#include "test_macros.h"
14+
15+
// expected-note@*:* 0+ {{expanded from macro}}
16+
17+
static_assert((_LIBCPP_ASSERT(false, "message"), true), "");
18+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
19+
20+
static_assert((_LIBCPP_ASSUME(false), true), "");
21+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
22+
23+
const int i = (_LIBCPP_ASSERT(false, "message"), 1);
24+
const int j = (_LIBCPP_ASSUME(false), 1);
25+
26+
static_assert(i, "");
27+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
28+
static_assert(j, "");
29+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
30+
31+
#if TEST_STD_VER >= 11
32+
constexpr int f(int x) {
33+
return (_LIBCPP_ASSERT(x != 6, "message"), x);
34+
// expected-note@-1 {{subexpression not valid in a constant expression}}
35+
}
36+
constexpr int g(int x) {
37+
return (_LIBCPP_ASSUME(x != 6), x);
38+
// expected-note@-1 {{subexpression not valid in a constant expression}}
39+
}
40+
static_assert(f(6) == 6, "");
41+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
42+
static_assert(g(6) == 6, "");
43+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
44+
#endif
45+
46+
#if TEST_STD_VER >= 14
47+
constexpr int ff(int x) {
48+
_LIBCPP_ASSERT(x != 6, "message");
49+
// expected-note@-1 {{subexpression not valid in a constant expression}}
50+
return x;
51+
}
52+
constexpr int gg(int x) {
53+
_LIBCPP_ASSUME(x != 6);
54+
// expected-note@-1 {{subexpression not valid in a constant expression}}
55+
return x;
56+
}
57+
static_assert(ff(6) == 6, "");
58+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
59+
static_assert(gg(6) == 6, "");
60+
// expected-error@-1 {{static assertion expression is not an integral constant expression}}
61+
#endif

0 commit comments

Comments
 (0)