I did a bit of testing, and confirmed none of meson-python, scikit-build-core, maturin and Bazel look at sysconfig’s CFLAGS.
After having a look at how that could actually be implemented in Meson, I think I changed my mind. Reason: these flags are both compiler- and platform-dependent. It’s possible to do it at the build system level, just not very clean. It’d require something like a base option with options auto (follow toolchain defaults) / true / false. And then in meson-python passing that flag with a different default. It can’t be extension_module-specific, because it’d miss common things like static libraries that are built separately and then linked into the extension module.
Distros seem to be doing this with compiler spec files, and that’s the cleaner way of doing it. It can be done in manylinux, which sidesteps the compiler/platform-specific issues. Here’s an experiment of adding both -fcf-protection=return and the frame pointer flags in numpy wheel builds, via adding a spec file into the manylinux containers: diff, CI log:
Control flow protection and frame pointer results per .so in the numpy wheel
With the hardening we get:
| object | control-flow protection | frame pointers |
|---|---|---|
| `_operand_flag_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 1/6 (16.7%) |
| `_struct_ufunc_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 1/7 (14.3%) |
| `_reduction_loop_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 4/14 (28.6%) |
| `_multiarray_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 52/299 (17.4%) |
| `_simd.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 30/2479 (1.2%) |
| `_rational_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 41/85 (48.2%) |
| `_multiarray_umath.cpython-313-x86_64-linux-gnu.so` | **none** | 4265/8657 (49.3%) |
| `_umath_tests.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 19/40 (47.5%) |
| `lapack_lite.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 13/18 (72.2%) |
| `_umath_linalg.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 95/291 (32.6%) |
| `_pocketfft_umath.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 124/185 (67.0%) |
| `bit_generator.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 90/149 (60.4%) |
| `_bounded_integers.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 87/121 (71.9%) |
| `_generator.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 208/332 (62.7%) |
| `mtrand.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 169/275 (61.5%) |
| `_mt19937.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 57/93 (61.3%) |
| `_pcg64.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 72/109 (66.1%) |
| `_sfc64.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 46/78 (59.0%) |
| `_philox.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 53/86 (61.6%) |
| `_common.cpython-313-x86_64-linux-gnu.so` | x86 feature: IBT, SHSTK | 69/92 (75.0%) |
| `libscipy_openblas64_-61654e39.so` | **none** | 399/12752 (3.1%) |
| `libgfortran-83c28eba-468e71e5.so.5.0.0` | x86 feature: IBT, SHSTK | 0/1359 (0.0%) |
| `libquadmath-2284e583-a9307bba.so.0.0.0` | x86 feature: IBT, SHSTK | 0/93 (0.0%) |
and without it we get:
| object | control-flow protection | frame pointers |
|---|---|---|
| `_operand_flag_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/6 (0.0%) |
| `_struct_ufunc_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/7 (0.0%) |
| `_reduction_loop_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/14 (0.0%) |
| `_multiarray_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/299 (0.0%) |
| `_simd.cpython-313-x86_64-linux-gnu.so` | **none** | 6/2479 (0.2%) |
| `_rational_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/85 (0.0%) |
| `_multiarray_umath.cpython-313-x86_64-linux-gnu.so` | **none** | 471/8657 (5.4%) |
| `_umath_tests.cpython-313-x86_64-linux-gnu.so` | **none** | 0/40 (0.0%) |
| `lapack_lite.cpython-313-x86_64-linux-gnu.so` | **none** | 0/18 (0.0%) |
| `_umath_linalg.cpython-313-x86_64-linux-gnu.so` | **none** | 0/291 (0.0%) |
| `_pocketfft_umath.cpython-313-x86_64-linux-gnu.so` | **none** | 12/185 (6.5%) |
| `bit_generator.cpython-313-x86_64-linux-gnu.so` | **none** | 0/149 (0.0%) |
| `_bounded_integers.cpython-313-x86_64-linux-gnu.so` | **none** | 0/121 (0.0%) |
| `_generator.cpython-313-x86_64-linux-gnu.so` | **none** | 0/332 (0.0%) |
| `mtrand.cpython-313-x86_64-linux-gnu.so` | **none** | 0/275 (0.0%) |
| `_mt19937.cpython-313-x86_64-linux-gnu.so` | **none** | 0/93 (0.0%) |
| `_pcg64.cpython-313-x86_64-linux-gnu.so` | **none** | 0/109 (0.0%) |
| `_sfc64.cpython-313-x86_64-linux-gnu.so` | **none** | 0/78 (0.0%) |
| `_philox.cpython-313-x86_64-linux-gnu.so` | **none** | 0/86 (0.0%) |
| `_common.cpython-313-x86_64-linux-gnu.so` | **none** | 0/92 (0.0%) |
| `libscipy_openblas64_-61654e39.so` | **none** | 399/12752 (3.1%) |
| `libgfortran-83c28eba-468e71e5.so.5.0.0` | x86 feature: IBT, SHSTK | 0/1359 (0.0%) |
| `libquadmath-2284e583-a9307bba.so.0.0.0` | x86 feature: IBT, SHSTK | 0/93 (0.0%) |
Frame-pointer percentages are a disassembly heuristic: leaf functions and functions touching no stack never get a frame pointer regardless of flags.
Binary sizes aren’t affected (by much at least), 16.8 MB in both cases.
That all looks about as expected. The extensions that end up missing protection are _multiarray_umath.so (due to it including already-built assembly objects from SVML) and libscipy_openblas64_ (vendored in by auditwheel) - both fixable with a bit of effort.
For -fcf_protection this is likely enough, also for local development, given that the compiler toolchains on distros that want to flip the switch already use -fcf_protection=full.
For frame pointers, that won’t be the default in toolchains, so if the desired behavior really is “do what CPython did” also for local dev builds, then I think adding it to build-details.json as semantic info rather than as a “inherit the flags for the compiler with which the target interpreter was built” might be necessary - that’s a fair amount of work per build system and build backend though.