This patch only adds new configuration knobs -- the actual assertions
will be added in follow-up patches.
Details
- Reviewers
ldionne Mordante - Group Reviewers
Restricted Project - Commits
- rGd1367ca46ee4: [libc++][hardening][NFC] Add macros to enable hardened mode.
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
Note: this patch is split from https://reviews.llvm.org/D151637 (which is a draft but helps see the state towards which this patch is working).
Since this is not a draft, I looked more in detail. So some additional remarks.
libcxx/include/__config | ||
---|---|---|
213 | _LIBCPP_ENABLE_HARDENED_MODE_DEFAULT is undefined, or is that in one another patch? | |
227 | Maybe enabled isn't the greatest word too. However defined is wrong; you made sure both are defined. | |
231 | The comment on line 238 // TODO(hardening): more checks to be added here... seem to suggest we get quite a bit of duplication in the #if and #elif branch. So I propose #if _LIBCPP_ENABLE_HARDENED_MODE || _LIBCPP_ENABLE_HARDENED_DEBUG_MODE // do stuff for hardened and hardened debug #endif // _LIBCPP_ENABLE_HARDENED_MODE || _LIBCPP_ENABLE_HARDENED_DEBUG_MODE #if _LIBCPP_ENABLE_HARDENED_DEBUG_MODE // do stuff only for hardened debug #endif // _LIBCPP_ENABLE_HARDENED_DEBUG_MODE | |
258 | Do we really want to disable things? If I only want to define _LIBCPP_ABI_BOUNDED_ITERATORS is that prohibited? |
libcxx/include/__config | ||
---|---|---|
194 | We don't have a good story for splitting up the whole __config header, however we could pretty easily add all the hardening configuration macros to something like __hardening or __config_dir/hardening.h. IMO it would make sure that this configuration is nicely contained in a single place. You'll want to make sure to include __config in that header since that includes __config_site, and that's where you get some of the defaults from. | |
219 | IMO _LIBCPP_ENABLE_HARDENED_DEBUG_MODE is fine as a temporary name until we remove the legacy debug mode, but it's pretty confusing so we want to rename it to _LIBCPP_ENABLE_DEBUG_MODE when we can. | |
231 | Considering there will be only a small number of categories (at least at first), IMO it will end up being easier to read by listing all of them explicitly in each mode. We can also cross that bridge once we get to it in an upcoming patch. | |
248–251 | I think the separation between hardening/debug mode selection and ABI flags should be kept. Otherwise, we lose the property of having a simple and consistent usage model for the mode selection. For example, we can't claim anymore that you can change modes between translation units (since that would affect ABI). Similarly, I think it would be sensible (although a bit weird) for someone to enable the debug mode without wanting to break the ABI. For example you'd get the unspecified behavior randomization but you wouldn't get bounded iterators. That sounds like something we shouldn't preclude from the get go. So essentially I'd just remove these lines and we can figure out how to enable the bounded iterator ABI later (soon). | |
258 |
No, that's allowed. ABI flags are disjoint from the hardening checks, so you could enable bounded iterators but if you don't enable any hardening, the in-bounds assertions inside __bounded_iter would be disabled. The intent is that vendors would decide on those ABI flags at configuration time, since users can't change them on a per-TU basis. | |
libcxx/include/__config_site.in | ||
30 | We'll want to add a release note explaining that we have these two new options and what they do. | |
39 | This needs to be tied into CMake and into the CI as well. For now I'll suggest we add one configuration for each of them, however that will be offset by the fact that we're removing the legacy debug mode, and we can also remove the With Assertions Enabled CI configuration in a separate patch since it'll be covered by those two. Tying into CMakeYou can look for this in libcxx/CMakeLists.txt if (LIBCXX_ENABLE_ASSERTIONS) config_define(1 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT) else() config_define(0 _LIBCPP_ENABLE_ASSERTIONS_DEFAULT) endif() Tying into the CIYou'll want to add new CMake caches similar to libcxx/cmake/caches/Generic-assertions.cmake and then modify run-buildbot and also buildkite-pipeline.yml to add new jobs. |
libcxx/include/__config | ||
---|---|---|
186 | We should also add a .rst document that explains what this mode is. This can be similar to DebugMode.rst but it should explain the various hardening modes instead (there will be TODOs at first). |
libcxx/include/__config | ||
---|---|---|
231 | I'm open to doing that in the future if we have enough categories that duplication becomes a concern. For now, I'd prefer to have each mode separate (I find it convenient to be able to focus on one mode in isolation). | |
258 | @Mordante I'm doing this only to avoid compiler warnings -- it's to make sure every macro is defined and set to either 0 or 1. Otherwise, the compiler would issue a warning each time one of these macros is referred to. I can't set them all to 0 beforehand since that would produce a warning about redefining macros. |
libcxx/include/__config | ||
---|---|---|
194 | I think the original idea for the split was that __config will include everything from __config_dir, so that other files can simply include __config and get everything. If that's the case, __config_dir/hardening.h cannot include __config. It could include __config_site directly, but I'm concerned it will need other includes in the future (for other macros it might rely upon), necessitating more granularization, and I'm not sure we want to go that way right now. OTOH, it's straightforward to include __config from __config_dir/hardening.h, but then all the other files that use assertions would need to include __config_dir/hardening.h (or I guess we could include it in __assert and then those files would just include __assert?). What do you think? |
libcxx/include/__config | ||
---|---|---|
186 | Took a stab. Should the doc be under docs/, or docs/DesignDocs/? (DebugMode.rst is under the latter) | |
213 | It comes from __config_site.in. | |
libcxx/include/__config_site.in | ||
30 | Added a very short note. I'm not sure we want to expand it in this patch given that these variables don't currently do anything. What do you think? | |
39 | Thanks for explaining the steps! |
This is starting to look really good!
libcxx/CMakeLists.txt | ||
---|---|---|
70–81 | Do we want to make this a multi-valued option instead? Like LIBCXX_HARDENING_MODE="unsafe|hardened|debug" and then tie that into the __config_site using the same macros you added in this patch? This would make it easier to add a new mode and also it avoids the issue of mutual exclusion of CMake options. It also mirrors a bit more closely what we're trying to do with e.g. the ABI library choice. | |
libcxx/docs/DesignDocs/HardenedMode.rst | ||
23–25 ↗ | (On Diff #535946) | Can you mention how users can enable it on a per-TU basis? Basically, the setting that vendors select is only used as the *default* value for user code (and also used for building the dylib itself). We also need to make sure that we're clearly identifying what is a CMake option and what is a compiler define, otherwise casual users pointed to this documentation will be extremely confused. I generally use some wording like "the LIBCXX_ENABLE_HARDENED_MODE option at CMake configuration-time", or something like that. And when I talk about a macro, I generally say "the _LIBCPP_ENABLE_FOO macro". That makes things less ambiguous. |
27–31 ↗ | (On Diff #535946) | You're talking about LIBCXX_ENABLE_ASSERTIONS in specific terms here, and I think that adds a lot of complexity if you want to be precise. Instead, I would say: The hardened and debug modes require assertions <LINK TO THE DOCUMENTATION FOR ASSERTIONS> to work. If assertions were explicitly disabled, none of the checks of the hardened/debug mode will trigger assertions. (also note that I wouldn't document that we enable assertions automatically cause we might not want to maintain that, we only do that to be nice to early adopters) With a wording like this, you don't have to be precise about LIBCXX_ENABLE_ASSERTIONS vs _LIBCPP_ENABLE_ASSERTIONS=1. |
33 ↗ | (On Diff #535946) | We want to expand a bit more on the relationship between ABI settings and these modes. Basically, you can say that the "mode" is separate from the ABI, i.e. it never modifies the ABI. However, libc++ provides alternate ABIs under which checks from the hardened/debug mode become a lot more potent (for example bounded iterators). |
libcxx/docs/ReleaseNotes.rst | ||
74–76 ↗ | (On Diff #535946) | |
libcxx/include/__config | ||
186 | I would move to docs/. I'm not really fond of DesignDocs/ anymore, since we do most of our design work in Discourse / Discord nowadays. | |
194 | Yeah, I didn't really think about that. I think it's fine to leave as-is for this patch. I might take a stab at moving it to another header at some point, but for now this seems fine. | |
libcxx/utils/ci/buildkite-pipeline.yml | ||
523–540 ↗ | (On Diff #535946) | I think this is a leftover |
libcxx/utils/libcxx/test/params.py | ||
298 | Using a multi-valued option here would also be nicer! | |
306 | You'll need to make sure that we pass --param enable_hardened_mode=True when LIBCXX_ENABLE_HARDENED_MODE=ON. We do this for assertions in libcxx/test/CMakeLists.txt for example: if (LIBCXX_ENABLE_ASSERTIONS) serialize_lit_param(enable_assertions True) endif() |
In general quite happy with the patch, but some minor issues.
libcxx/docs/HardenedMode.rst | ||
---|---|---|
2 ↗ | (On Diff #536482) | Is this name future-proof with all the other pre-condition violation checks you want to do? |
10–11 ↗ | (On Diff #536482) | |
15–16 ↗ | (On Diff #536482) | I feel "by *security-conscious projects" is opinionated. I think the text is better without the last part. It already mentions "security" and "undefined behavior" before giving the reader enough information to whether or not they like this feature. |
25 ↗ | (On Diff #536482) | Maybe list the CMake options with a short description of what they do. |
libcxx/docs/ReleaseNotes.rst | ||
75 ↗ | (On Diff #536482) | crash has a negative meaning. |
libcxx/docs/index.rst | ||
42 ↗ | (On Diff #536482) | Please add HardenedMode here. |
187 ↗ | (On Diff #536482) | This file is not in design docs, so it does not belong here. |
libcxx/CMakeLists.txt | ||
---|---|---|
67–70 | ||
822–823 | This check is duplicated with above, I don't think it is needed. | |
libcxx/docs/ReleaseNotes.rst | ||
90–92 ↗ | (On Diff #536482) | |
libcxx/include/__algorithm/comp_ref_type.h | ||
68–69 ↗ | (On Diff #536482) | The TODO comment isn't relevant anymore. |
libcxx/include/__algorithm/three_way_comp_ref_type.h | ||
61 ↗ | (On Diff #536482) | The TODO comment isn't relevant anymore. |
74 ↗ | (On Diff #536482) | There's a similar TODO comment in __hash_table I think, look for _LIBCPP_DEBUG_ASSERT after rebasing onto main. |
libcxx/test/support/container_debug_tests.h | ||
17 ↗ | (On Diff #536482) | Let's rename libcpp-has-debug-mode to libcpp-has-legacy-debug-mode (preferably in a separate patch before this one). That'll clear up a lot of the confusion. |
libcxx/utils/ci/buildkite-pipeline.yml | ||
496 ↗ | (On Diff #536482) | Let's add ENABLE_CLANG_TIDY: "On" here and below. |
libcxx/utils/libcxx/test/params.py | ||
290 | We need tests for these macros along these lines: https://godbolt.org/z/sEKa1W3sn |
Address feedback.
libcxx/docs/HardenedMode.rst | ||
---|---|---|
2 ↗ | (On Diff #536482) | Pretty much. Hardening is the name of the whole effort, and I expect that the name of the main mode will always be "hardened". |
10–11 ↗ | (On Diff #536482) | Thanks! |
15–16 ↗ | (On Diff #536482) | This is very useful feedback, thanks! |
libcxx/docs/index.rst | ||
187 ↗ | (On Diff #536482) | Thanks! |
libcxx/test/support/container_debug_tests.h | ||
17 ↗ | (On Diff #536482) | Pushed e61764c32ff77ae85509e56234a13a47b2d36cec. |
libcxx/utils/libcxx/test/params.py | ||
290 | Added some tests -- do we want any additional test cases? |
libcxx/docs/ReleaseNotes.rst | ||
---|---|---|
91–93 ↗ | (On Diff #537798) | |
libcxx/test/libcxx/assertions/modes/debug.pass.cpp | ||
15–18 ↗ | (On Diff #537798) | |
libcxx/test/libcxx/assertions/modes/debug_no_assertions.pass.cpp | ||
9–10 ↗ | (On Diff #537798) | |
libcxx/test/libcxx/assertions/modes/hardened_and_debug_mutually_exclusive.verify.cpp | ||
13–17 ↗ | (On Diff #537798) | No need to define a main function! It makes it more obvious that we're not running this test. |
libcxx/test/libcxx/assertions/modes/hardened_no_assertions.pass.cpp | ||
15–16 ↗ | (On Diff #537798) | |
libcxx/test/libcxx/assertions/modes/unchecked.pass.cpp | ||
15 ↗ | (On Diff #537798) | I would guard this with #if !_LIBCPP_ENABLE_ASSERTIONS because the test will fail otherwise. Also add a TODO comment to remove this once we "remove" _LIBCPP_ENABLE_ASSERTIONS (in reality we'll keep it just for backwards compat). |
15–16 ↗ | (On Diff #537798) | |
18 ↗ | (On Diff #537798) | Leftover! |
libcxx/test/libcxx/assertions/modes/debug.pass.cpp | ||
---|---|---|
1 ↗ | (On Diff #537815) | I think we should add a test like this: // This test ensures that we can disable the debug mode on a per-TU basis regardless of how the library was built. // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_DEBUG_MODE=0 // now check that _LIBCPP_ASSERT_UNCATEGORIZED doesn't fire. We should do the same for the hardened mode too. |
3 ↗ | (On Diff #537815) | We also need one like this: // This test ensures that we can enable the debug mode on a per-TU basis regardless of how the library was built. // ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_DEBUG_MODE=1 // now check that _LIBCPP_ASSERT_UNCATEGORIZED fires. We should do the same for the hardened mode too. |
8–9 ↗ | (On Diff #537815) | Similar comments should also be added to the hardened mode tests. |
libcxx/test/libcxx/assertions/modes/debug_mode_not_1_or_0.verify.cpp | ||
13–17 ↗ | (On Diff #537815) |
LGTM, thanks for working on this!
libcxx/test/libcxx/algorithms/alg.sorting/assert.sort.invalid_comparator.pass.cpp | ||
---|---|---|
36 ↗ | (On Diff #538310) | Shouldn't this be XFAIL since we always expect the test to fail. |
The CI is red because a couple of back-deployment configurations timed out. I don't think it can be related to this patch, so I'll go ahead and merge this.