[RFC] Hardening in libc++

At link time (we define _LIBCPP_AVAILABILITY_CUSTOM_VERBOSE_ABORT_PROVIDED=1 and link in a definition of the __libcpp_verbose_abort function).

I’m not sure why we didn’t go with defining _LIBCPP_VERBOSE_ABORT, but I think that should also work for us. Maybe @ayzhao remembers more details.

IIRC the reason Chrome overrides this at link time was because in Chrome’s build system, the termination function lives in and uses methods from a separate build component and migrating all of that into Chrome’s libc++ component would’ve been very difficult and messy if not impossible.

See the commit description in https://crrev.com/c/4193285

Our current thinking is to only provide two ways of customizing verbose abort:

  1. A new way that would use __config_site.in and essentially allow a vendor to patch the code in __assert that defines the verbose abort function.
  2. The current way that allows redefining the macro at compile time (aimed at users).

We’d prefer to not support link-time overriding. The reason is that we would like the default implementation to compile to a single instruction. The mechanism for link-time overriding would necessarily incur a function call that would be impossible to optimize out. We presume it would still allow your use case (you can use __config_site.in to define the verbose abort function to do a function call that is only resolved at link time) – please let us know if you see any issues with this plan.

Thanks, I think that should work well for us. If nothing else, we could just define the macro to call our current function.

Are there any details about what he __config_site.in way will look like?

Sorry about being a little slow with updates. We’re currently working on the story for overriding the termination handler, I hope to post an update on that topic soon. One thing I wanted to mention is that we also published the following related proposal to add __builtin_verbose_trap to Clang and use that in our default handler when an assertion is triggered. We hope it would provide better user experience than __builtin_trap by providing an error message while avoiding binary bloat.

Opened the first related patch: [libc++][hardening] Rework how the assertion handler can be overridden. by var-const · Pull Request #77883 · llvm/llvm-project · GitHub

The details changed a little from how we originally imagined it but the main idea is still to make overriding this happen at the CMake configuration time. When a hardening assertion fails, the library would call _LIBCPP_ASSERTION_HANDLER. The default definition of the assertion handler will directly call __builtin_trap (or __builtin_verbose_trap once that becomes available), so that overriding the verbose abort function will no longer have any effect on hardening. There will be a new CMake variable, tentatively named LIBCXX_ASSERTION_HANDLER_FILE, that will allow the vendor to specify the path to a custom header that provides its own definition of _LIBCPP_ASSERTION_HANDLER. If provided, the contents of the header will be injected into library code during the CMake configuration step, replacing the default assertion handler. The header needs to be available during the CMake configuration time but, since its contents are copied into the library (similar to __config_site), it needs not be available at compile time.

Using a header should give more freedom compared to directly overriding a macro – in particular, it should make it possible to e.g. forward-declare a function. It’s also possible for the header to include other headers (but including almost anything from the standard library will create circular dependencies, so that should be avoided).

Please let us know if you have any concerns with this approach!