This is an archive of the discontinued LLVM Phabricator instance.

[clang] Allow `no_sanitize("all")` on global variables
AbandonedPublic

Authored by abrachet on Aug 31 2022, 9:03 AM.

Details

Summary

This is a trivial change because "all" is already correctly handled

Diff Detail

Event Timeline

abrachet created this revision.Aug 31 2022, 9:03 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 31 2022, 9:03 AM
abrachet requested review of this revision.Aug 31 2022, 9:03 AM
aaron.ballman requested changes to this revision.Aug 31 2022, 9:59 AM

Generally heading in the right direction, but missing some things:

  1. Test coverage where you try to apply the attribute to something other than a global variable (like a function)
  2. Changes to AttrDocs.td (it currently says what's supported is everything -fno-sanitize supports, but https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation doesn't list all as an option)
  3. Release notes
This revision now requires changes to proceed.Aug 31 2022, 9:59 AM
abrachet updated this revision to Diff 457027.Aug 31 2022, 11:05 AM
aaron.ballman accepted this revision.Aug 31 2022, 11:31 AM

LGTM assuming precommit CI comes back clean, thanks!

This revision is now accepted and ready to land.Aug 31 2022, 11:31 AM

Gosh, I really don't like the no_sanitize("all") - IMHO it's cutting off your nose to spite your face. Generally there's a specific reason to disable sanitization of a function / global variable - and that reason is almost always specific to a sanitizer. Disabling all sanitizers is a maintenance nightmare when you want to roll out a new sanitizer.

We have a similar problem in Android, where the build system supports a concept called sanitize: { none: true } which disables all sanitizers for a source file. It's a very poor decision in hindsight.

I would strongly discourage anyone from using this feature. We also already have __attribute__((disable_sanitizer_instrumentation)) which is the same thing, and from my understanding, is much more used.

Can you describe a use case where you'd use this, that isn't better served by individually disabling sanitizers?

Gosh, I really don't like the no_sanitize("all") - IMHO it's cutting off your nose to spite your face. Generally there's a specific reason to disable sanitization of a function / global variable - and that reason is almost always specific to a sanitizer. Disabling all sanitizers is a maintenance nightmare when you want to roll out a new sanitizer.

We have a similar problem in Android, where the build system supports a concept called sanitize: { none: true } which disables all sanitizers for a source file. It's a very poor decision in hindsight.

I would strongly discourage anyone from using this feature. We also already have __attribute__((disable_sanitizer_instrumentation)) which is the same thing, and from my understanding, is much more used.

Can you describe a use case where you'd use this, that isn't better served by individually disabling sanitizers?

no_sanitize("all") is certainly a heavy hand, we use it sparingly, it's useful in code like ldso/libc before sanitizer runtimes are available. That doesn't actually apply to global variables, but it's common to just have a macro to disable sanitizers and having a separate one for globals and functions is inconvenient. For globals in particularly where we ran into this where a global is put into a section and dumped but the redzone was undesirable, we currently just use no_sanitize("address", "hwaddress"). Also as a side note/rant, I wish no_sanitize("address") implied no_sanitize("hwaddress").

Regarding __attribute__((disable_sanitizer_instrumentation)), I actually just found out about this when working on this patch, we plan on using it, but it isn't supported by gcc. Though as it turns out no_sanitize("...") is ignored and warned about for globals on gcc.

It is surprising that no_sanitize("all") is available for functions but not for globals, but if you feel strongly that no_sanitize("all") shouldn't be supported in more places then I don't have a huge preference that this be landed.

no_sanitize("all") is certainly a heavy hand, we use it sparingly, it's useful in code like ldso/libc before sanitizer runtimes are available.

Yeah, we have to do the same in Android's libc (example), but it's still almost always more useful to be specific with the sanitizer. For example, if the code was updated to have ubsan-int-overflow checks with fsanitize-trap, and we used no_sanitize("all"), then we'd silently just never sanitize this code.

That doesn't actually apply to global variables, but it's common to just have a macro to disable sanitizers and having a separate one for globals and functions is inconvenient.

Agreed, having a single attribute is better, which is another reason why I don't like disable_sanitizer_instrumentation. If I had to pick a poison, it'd be no_sanitize("all"), but the former already exists :(.

Also as a side note/rant, I wish no_sanitize("address") implied no_sanitize("hwaddress").

Yeah, we often have the same semantics of "no asan + no hwasan [+ no memtag]". Better to keep them separate though because otherwise you need to handle the "no, ASan is fine, but seriously no hwasan" and no_sanitize("only_hwasan") makes me want to cry. This happens, for example, with global variables that are referenced by inline assembly. Fine to be ASan-ified, not okay to be HWASan-ified (as the address tag needs a special movk to materialize the tag bits) if the materialized pointer is passed to an instrumented function, for example.

Regarding __attribute__((disable_sanitizer_instrumentation)), I actually just found out about this when working on this patch, we plan on using it, but it isn't supported by gcc. Though as it turns out no_sanitize("...") is ignored and warned about for globals on gcc.

It is surprising that no_sanitize("all") is available for functions but not for globals, but if you feel strongly that no_sanitize("all") shouldn't be supported in more places then I don't have a huge preference that this be landed.

In a perfect world, I'd

  1. delete disable_sanitizer_instrumentation and
  2. delete no_sanitize("all") for code.

I don't think #2 is feasible, but maybe it's better we follow the gcc approach and make no_sanitize("all") a warning on global variables ("Hey, this doesn't work, and also you should really reconsider doing this for code as well").

I tried looking for references on #1 - only finding a linux kernel kcsan reference, which I'm wondering whether we can fix... I looked at a lot of the indexed code I have access to and only found that, tried grepping github but ended up getting rate-limited and not seeing results correctly (https://github.com/search?q=disable_sanitizer_instrumentation+-path%3Allvm+-path%3Atest%2FCodeGen+-path%3Aclang+-path%3Allvm-project+-filename%3Acompiler_attributes.h+-filename%3Aattr-disable-sanitizer-instrumentation.c&type=Code).

Sorry for the bad news, but yeah, I think I'd prefer not to add no_sanitize("all") for globals. Definitely willing to change my mind though if someone comes along with a compelling reason why they need the mega-hammer, but I really think disablement is a decision that should be done on a per-sanitizer basis.

aaron.ballman requested changes to this revision.Sep 1 2022, 4:26 AM

That doesn't actually apply to global variables, but it's common to just have a macro to disable sanitizers and having a separate one for globals and functions is inconvenient.

Agreed, having a single attribute is better, which is another reason why I don't like disable_sanitizer_instrumentation. If I had to pick a poison, it'd be no_sanitize("all"), but the former already exists :(.

That's not the end of the world; we can deprecate attributes if we think that's valuable to do so.

In a perfect world, I'd

  1. delete disable_sanitizer_instrumentation and
  2. delete no_sanitize("all") for code.

I don't think #2 is feasible, but maybe it's better we follow the gcc approach and make no_sanitize("all") a warning on global variables ("Hey, this doesn't work, and also you should really reconsider doing this for code as well").

I tried looking for references on #1 - only finding a linux kernel kcsan reference, which I'm wondering whether we can fix... I looked at a lot of the indexed code I have access to and only found that, tried grepping github but ended up getting rate-limited and not seeing results correctly (https://github.com/search?q=disable_sanitizer_instrumentation+-path%3Allvm+-path%3Atest%2FCodeGen+-path%3Aclang+-path%3Allvm-project+-filename%3Acompiler_attributes.h+-filename%3Aattr-disable-sanitizer-instrumentation.c&type=Code).

Other interesting information:
https://sourcegraph.com/search?q=context:global+disable_sanitizer_instrumentation+-file:.*test.*&patternType=standard
https://sourcegraph.com/search?q=context:global+no_sanitize%28%22all%22%29+-file:.*test.*&patternType=standard

Sorry for the bad news, but yeah, I think I'd prefer not to add no_sanitize("all") for globals. Definitely willing to change my mind though if someone comes along with a compelling reason why they need the mega-hammer, but I really think disablement is a decision that should be done on a per-sanitizer basis.

I don't have a horse in this race, but if I had a time machine, I would try to get us to a world where disable_sanitizer_instrumentation was always spelled no_sanitizer("all") because that fits with the other no_sanitizer arguments nicely without needing to introduce an entirely separate attribute to control sanitization behavior. disable_sanitizer_instrumentation came from an era where we introduced no_sanitize_address, no_sanitize_memory, and so on.

Marking as requesting changes so we don't accidentally land this while still under discussion.

This revision now requires changes to proceed.Sep 1 2022, 4:26 AM
abrachet abandoned this revision.Sep 1 2022, 8:35 AM

I'm fine abandoning this after discussion. I've created D133117 so that we at least get a better diagnostic message :)