How clang compares with gcc on security hardening ?

Hello,

Looking at a Fedora thread ( ) about changing the compiler from gcc to clang to build Firefox, I noticed the following statement:

“Clang lags significantly behind GCC on security hardening”

Some of the arguments or missing features are discussed here:

and other answers

I am wondering if these statements are accurate and if they are enough of a worry?

Thanks,
Sylvestre

TL;DR: I think “lags significantly” is overstating the case. The only feature I can identify that Clang doesn’t have (wrt the things brought up in that Fedora post) is a real -fstack-clash-protection implementation.

I read through that post a couple of times looking for a list, and eventually noticed a reference to

https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/master/f/buildflags.md

which is probably as close to an actual list of security options as we’re going to get (it talks about many different options and doesn’t explicitly call out those that are security-related). Between that reference and other comments in the Fedora issue, the list is probably something like this:

-fstack-protector-strong

Clang has this.

-fcf-protection

Clang has this.

-D_FORTIFY_SOURCE=2

AFAICT, Clang supports what is needed by this.

Being a preprocessor symbol, this is mostly about glibc changes to support protections on various unsafe library calls; those in turn depend on a pile of builtins, which are likely the ones implemented in this GCC patch:

https://gcc.gnu.org/ml/gcc-patches/2004-09/msg02055.html

I see a bunch of builtins with these sorts of names in Clang, so if nobody has filed bugs, I suspect this should all work (when using glibc).

-fstack-clash-protection

Clang does not recognize this option. Curiously I see two patches to add it, neither of them has been committed. I think there’s a reasonable argument that a key security option should not be recognized and ignored.

FTR, the buildflags.md documentation says this keeps large stack allocations from skipping over a check of the guard page.

@Sylvestre do you want to file a bug requesting this option?

–paulr

My (possibly not fully informed) understanding of the situation is basically what this commenter said:
https://pagure.io/fesco/issue/2020#comment-545782

(llvm doesn’t implement e.g. _D_FORTIFY_SOURCE=2 properly)

As a clang developer, the way I see the issue is that -D_FORTIFY_SOURCE=2 liberally uses GCC extensions. Many changes could be made upstream in glibc to make fortify source work better with clang. Implementing the extensions needed in clang is a bit heroic.

I just want to push back on the narrative that we “haven’t implemented” _FORTIFY_SOURCE. Clang hasn’t implemented some collection of hard-to-implement GCC extensions, and
_FORTIFY_SOURCE happens to depend on them. Many of these extensions are borderline incompatible with fundamental LLVM and Clang design decisions (__builtin_va_arg_pack_len, see https://clang.llvm.org/docs/UsersManual.html#gcc-extensions-not-implemented-yet), and relaxing them isn’t fun or easy. Many contributors are busy doing other security related things, see all the work on shadow call stack, CFI, speculative load hardening, sanitizers, coverage directed fuzzing, etc.

I also don’t think there is clear consensus that _FORTIFY_SOURCE is a valuable mitigation for most codebases. In C++ code, data is in vectors and strings, not stack allocated arrays. It’s not immediately clear that _FORTIFY_SOURCE helps with this kind of code. So, for many contributors, it is much lower priority than hardening virtual and indirect calls (CFI).

Regarding security, I don’t think it’s a clear cut case that one compiler is “better”. The user needs to be able to assess the inherent value of the security mitigations provided by the compiler, and right now the compilers just have different strengths, weaknesses, and depth of integration.

and as mentioned above debuginfo issues.

This is a real problem, unfortunately, and it’s been fairly well documented in presentations, blog posts, etc. However, I subscribe to the debuginfo project on Phabricator, and I regularly see reviews go by to do the work necessary to improve variable location tracking, so hopefully things will get better.

TL;DR: I think "lags significantly" is overstating the case. The only feature I can identify that Clang doesn't have (wrt the things brought up in that Fedora post) is a real `-fstack-clash-protection` implementation.

I read through that post a couple of times looking for a list, and eventually noticed a reference to

https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/master/f/buildflags.md

which is probably as close to an actual list of security options as we're going to get (it talks about many different options and doesn't explicitly call out those that are security-related). Between that reference and other comments in the Fedora issue, the list is probably something like this:

-fstack-protector-strong

Clang has this.

This comment mentions possible bugs in clang's implementation:
https://pagure.io/fesco/issue/2020#comment-546820

-fcf-protection

Clang has this.

-D_FORTIFY_SOURCE=2

AFAICT, Clang supports what is needed by this.

Being a preprocessor symbol, this is mostly about glibc changes to support protections on various unsafe library calls; those in turn depend on a pile of builtins, which are likely the ones implemented in this GCC patch:

Jakub Jelinek - [PATCH] Object size checking to prevent (some) buffer overflows

I see a bunch of builtins with these sorts of names in Clang, so if nobody has filed bugs, I suspect this should all work (when using glibc).

-fstack-clash-protection

Clang does not recognize this option. Curiously I see two patches to add it, neither of them has been committed. I think there's a reasonable argument that a key security option should not be recognized and ignored.

I agree. When I wrote ⚙ D42593 GCC compatibility: Ignore -fstack-clash-protection, I
thought it would be OK to ignore, because -fstack-check
was also ignored, but I changed my mind after Joerg's comment.

-Tom

Exactly – _FORTIFY_SOURCE is a libc feature, not a clang feature. Here we’re talking about the implementation in glibc – which was developed for GCC, and nobody in the glibc community has yet been bothered to implement proper support in clang.

But I’m fairly sure that Clang does have all of the functionality necessary to implement all of the fortifications enabled by _FORTIFY_SOURCE=2 in glibc, because it has been implemented in Bionic libc – using an entirely different set of compiler extensions which are supported only in Clang.

If someone wishes to contribute support to glibc to better support these fortifications with clang, it should be pretty easy. Simply look at how FORTIFY_SOURCE is implemented in the Bionic libc, and do something similar to that (with appropriate ifdeffery, because that version is not going to work in GCC).

My (possibly not fully informed) understanding of the situation is basically what this commenter said:
https://pagure.io/fesco/issue/2020#comment-545782

(llvm doesn’t implement e.g. _D_FORTIFY_SOURCE=2 properly)

As a clang developer, the way I see the issue is that -D_FORTIFY_SOURCE=2 liberally uses GCC extensions. Many changes could be made upstream in glibc to make fortify source work better with clang. Implementing the extensions needed in clang is a bit heroic.

I just want to push back on the narrative that we “haven’t implemented” _FORTIFY_SOURCE. Clang hasn’t implemented some collection of hard-to-implement GCC extensions, and
_FORTIFY_SOURCE happens to depend on them. Many of these extensions are borderline incompatible with fundamental LLVM and Clang design decisions (__builtin_va_arg_pack_len, see https://clang.llvm.org/docs/UsersManual.html#gcc-extensions-not-implemented-yet), and relaxing them isn’t fun or easy.

Exactly – _FORTIFY_SOURCE is a libc feature, not a clang feature. Here we’re talking about the implementation in glibc – which was developed for GCC, and nobody in the glibc community has yet been bothered to implement proper support in clang.

For clarity here, let me just restate that:

I meant to say “proper support with clang”, not "in clang".

It is unfortunate, however, that the two communities haven’t settled on an agreeable set of extensions to implement this feature. I can understand why glibc wouldn’t want a second parallel implementation with it’s own set of bugs requiring its own tests. Getting this right really requires someone who cares about it enough to get involved in all of the relevant projects here: glibc, clang, gcc, and maybe even bionic. That sounds like a lot of work, so I understand why it hasn’t been done.

If someone wishes to contribute support to glibc to better support these fortifications with clang, it should be pretty easy. Simply look at how FORTIFY_SOURCE is implemented in the Bionic libc, and do something similar to that (with appropriate ifdeffery, because that version is not going to work in GCC).

It is unfortunate, however, that the two communities haven’t settled on an agreeable set of extensions to implement this feature. I can understand why glibc wouldn’t want a second parallel implementation with it’s own set of bugs requiring its own tests. Getting this right really requires someone who cares about it enough to get involved in all of the relevant projects here: glibc, clang, gcc, and maybe even bionic. That sounds like a lot of work, so I understand why it hasn’t been done.

I’ve been working on improving _FORTIFY_SOURCE support for apple’s c standard library. I added the attribute fortify_stdlib (https://clang.llvm.org/docs/AttributeReference.html#fortify-stdlib) so it should just be a matter of glibc slapping it on a few functions and clang will do the rest, which doesn’t seem like much of a burden. The narrative that clang is far behind GCC for _FORTIFY doesn’t really add up to me either, there are some clang extensions that GCC doesn’t support either, for instance: __builtin_dynamic_object_size and pass_object_size.

FYI, I will be upstreaming a new implementation of stack checking support for Darwin enabled by -fstack-check in the near future, for arm64 and x86_64.

However, these will be Darwin specific as we will have a custom ABI and multiple stack checking techniques to achieve this. Other platforms are welcome to adapt the patches and standardize on an ABI (for arm64, for x86 we use the existing ABI from the windows stack probing implementation).

Amara

Neither the docs nor the review seem to explain why this new special-purpose “fortify-stdlib” attribute is necessary. Were you were unaware that there’s already an implementation of a fortified libc built on existing clang features, or is that existing mechanism unsatisfactory?

If we do need a new attribute for this for some reason, then having it special-case a list of function names is quite unfortunate – the list it supports now certainly isn’t the complete list of functions that need fortified versions, and some of those functions which need fortified versions in a given libc aren’t defined by any standard. Having to add support for them all into the compiler seems rather unsatisfactory.

So, if this new feature is needed for some reason, it would seem much better to make the behavior explicitly specified in the attribute, rather than special cased for a given set of known function-names. (E.g. the attribute could specify exactly what edits to make to the function call: which name to call instead, and what extra arguments to insert where into the argument list.)

Whoops, I also meant to include a link in that last email:
https://android.googlesource.com/platform/bionic/+/11dff3e91c6afd68e4e6c46f8249d10f07f307a7/libc/include/bits/fortify/stdio.h#75

(Note, the key here is the “pass_object_size” attribute, which allows __builtin_object_size work on an argument to a non-inlined function).

There are a couple of things that can be implemented nicer with more
support in the backend, pure macros and a few inline function tricks get
you very far.

Joerg

First, thanks for all the answers, super interesting!

TL;DR: I think "lags significantly" is overstating the case. The only feature I can identify that Clang doesn't have (wrt the things brought up in that Fedora post) is a real `-fstack-clash-protection` implementation.

[...]

-fstack-clash-protection

Clang does not recognize this option. Curiously I see two patches to add it, neither of them has been committed. I think there's a reasonable argument that a key security option should not be recognized and ignored.

FTR, the `buildflags.md` documentation says this keeps large stack allocations from skipping over a check of the guard page.

@Sylvestre do you want to file a bug requesting this option?

Done: 40802 – gcc parity: -fstack-clash-protection isn't implemented in clang
Thanks again,
S