This is an archive of the discontinued LLVM Phabricator instance.

[UBSan] Disable the function sanitizer on an execute-only target.
ClosedPublic

Authored by MaggieYi on Aug 23 2023, 6:49 AM.

Details

Summary

PR for https://github.com/llvm/llvm-project/issues/64931.

Disable the function and kcfi sanitizers on an execute-only target.

An execute-only target disallows data access to code sections. -fsanitize=function and -fsanitize=kcfi instrument indirect function calls to load a type hash before the function label. This results in a non-execute access to the code section and a runtime error.

To solve the issue, -fsanitize=function should not be included in any check group (e.g. undefined) on an execute-only target. If a user passes -fsanitize=undefined, there is no error and no warning. However, if the user explicitly passes -fsanitize=function or -fsanitize=kcfi on an execute-only target, an error will be emitted.

Diff Detail

Event Timeline

MaggieYi created this revision.Aug 23 2023, 6:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 23 2023, 6:49 AM
MaggieYi requested review of this revision.Aug 23 2023, 6:49 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 23 2023, 6:49 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

The PS5 bits LGTM, but as I'm not familiar with the ARM aspects I won't give final approval.

I think this is also true of -fsanitize=kcfi, isn't it? That also works by writing a cookie just before the function entry point. If we're diagnosing incompatibilities with execute-only, then perhaps we should do it for both.

simon_tatham added inline comments.Aug 23 2023, 8:15 AM
clang/lib/Basic/Sanitizers.cpp
132–141

Why do we need to check both of -mexecute-only and the +execute-only target feature? It looks to me as if the code in Driver/ToolChains/Arch/ARM.cpp that handles -mexecute-only ends up setting the same target feature anyway. And it checks the supported architecture versions first.

Would it not be better to just check the target feature here, and avoid having a duplicated copy of the rest of this logic which needs to be kept in sync with the ARM driver?

Does something go wrong if you do it that way?

MaskRay added inline comments.Aug 23 2023, 3:06 PM
clang/include/clang/Basic/DiagnosticCommonKinds.td
326 ↗(On Diff #552694)

We don't need this diagnostic as a common kind (we only use it in driver).

I think we can reuse err_drv_argument_not_allowed_with . Though for PS5 you will get ... allowed with '-mexecute-only' even if the user doesn't specify -mexecute-only, but I hope it is fine.

clang/lib/Basic/Sanitizers.cpp
132–141

I think we only need to check the +execute-only target feature and remove driver option -mexecute-only check.

if (Triple.isPS5())
  return true;
if (!Triple.isARM() && !Triple.isThumb())
  return false;
return features contains "+execute-only" ;
clang/lib/Driver/SanitizerArgs.cpp
407

I think it's clear not not to add the variable NotAllowedWithExecuteOnly.

Currently, I need to check the definition of NotAllowedWithExecuteOnly to understand that this comment does what it says. For now, just encoding Function here is clearer.

clang/lib/Frontend/CompilerInvocation.cpp
4405 ↗(On Diff #552694)

Remove.

We don't perform rigid error checking for cc1. If the user bypass the driver check with -Xclang -fsanitize=function, we don't provide more diagnostics.

clang/test/CodeGen/ubsan-function.c
2 ↗(On Diff #552694)

remove new tests. we only need test/Driver test.

MaggieYi updated this revision to Diff 553409.Aug 25 2023, 1:40 AM
MaggieYi edited the summary of this revision. (Show Details)

The changes include:

  1. Added an ARM::supportedExecuteOnly function to avoid the duplicated code.
  2. Modified the isExecuteOnlyTarget function to only deal with the -mexecute-only option.
  3. Added SanitizerKind::KCFI to the SanitizerMask of NotAllowedWithExecuteOnly.
Herald added a project: Restricted Project. · View Herald TranscriptAug 25 2023, 1:40 AM

Thanks @simon_tatham and @MaskRay for the quick code review.

When I work on this issue, I want to add an error for both clang and clang -cc1.
-mexecute-only is an ARM-only compiler option. The clang Driver will convert -mexecute-only to -target-feature +execute-only in the clang cc1 command.
To pass the execute-only option, the clang command is: clang -target=armv6t2-eabi -mexecute-only …. The Arm clang cc1 command:
clang -cc1 -triple armv6t2-unknown-unknown-eabi -target-feature +execute-only….

If I move the change to the code in Driver/ToolChains/Arch/ARM.cpp, the error will only deal with the -mexecute-only option, but not handle the -target-feature +execute-only in the clang -cc1 command.

As @MaskRay commented that we don't perform rigid error checking for cc1. In this case, we could handle the error in the Driver/ToolChains/Arch/ARM.cpp. However, the target-specific predicate function (named isExecuteOnlyTarget) allows any other targets that support execute-only could get the effect by modifying just the isExecuteOnlyTarget function. Therefore, I have modified the isExecuteOnlyTarget function to only deal with the -mexecute-only option. I have also added a function (named ARM::supportedExecuteOnly) to avoid the duplicated code.

clang/include/clang/Basic/DiagnosticCommonKinds.td
326 ↗(On Diff #552694)

Since err_drv_argument_not_allowed_with is an ARM-only option, We cannot reuse it.

clang/lib/Driver/SanitizerArgs.cpp
407

NotAllowedWithExecuteOnly is modified to include the SanitizerKind::KCFI, therefore I keep it in the code.

MaskRay added inline comments.Aug 25 2023, 10:21 AM
clang/include/clang/Basic/DiagnosticCommonKinds.td
326 ↗(On Diff #552694)

err_drv_argument_not_allowed_with is a generic diagnostic. My point is that we don't need an extra err_unsupported_opt_for_execute_only_target.

clang/lib/Basic/Sanitizers.cpp
125

The idiom is hasFlag(clang::driver::options::OPT_mexecute_only, clang::driver::options::OPT_mno_execute_only, false)

129

I don't think we need an extra llvm::ARM::supportedExecuteOnly check. We just return true when -mexecute-only is in effect.

clang/lib/Driver/SanitizerArgs.cpp
478

`-fsanitize=function => NotAllowedWithExecuteOnly

since we now handle kcfi as well.

481

omit braces

clang/test/Driver/fsanitize.c
977

Drop not %clang --target=armv6t2-eabi -mexecute-only -fsanitize=undefined -fsanitize=function.
Testing just -fsanitize=function for the negative test is sufficient.

979

Drop -fsanitize=function -fsanitize=kcfi line. They already lead to an error.

980

--target=armv6t2-eabi -mexecute-only -fsanitize=undefined needs a custom check prefix that checks function is not enabled.

llvm/lib/TargetParser/ARMTargetParser.cpp
602 ↗(On Diff #553409)

We don't need to extract the function.

vitalybuka resigned from this revision.Aug 25 2023, 3:00 PM
MaggieYi updated this revision to Diff 554474.Aug 29 2023, 1:30 PM
MaggieYi marked 7 inline comments as done.

Thanks @MaskRay, I have updated the patch following your suggestions.

clang/include/clang/Basic/DiagnosticCommonKinds.td
326 ↗(On Diff #552694)

Sorry, I mean that -mexecute-only is the arm-only option Options.td#L4202.

As you suggested that we could use err_drv_argument_not_allowed_with by passing -fsanitize=function (not -mexecute-only) and the target triple. For example, "error: invalid argument '-fsanitize=function' not allowed with 'x86_64-sie-ps5'". I have removed err_unsupported_opt_for_execute_only_target.

MaskRay accepted this revision.Aug 29 2023, 6:53 PM

Thanks!

clang/lib/Basic/Sanitizers.cpp
125

return Args.hasFlag(clang::driver::options::OPT_mexecute_only ...

clang/test/Driver/fsanitize.c
982

Delete this -NOT. %clang returning 0 implies that there is no error.

This revision is now accepted and ready to land.Aug 29 2023, 6:53 PM
MaggieYi updated this revision to Diff 554665.Aug 30 2023, 4:26 AM
MaggieYi marked 5 inline comments as done.

Thanks @MaskRay. The patch is updated.

Hi @simon_tatham, are you happy with the patch?

Thanks

simon_tatham accepted this revision.Aug 30 2023, 5:20 AM

Yes, this looks good to me too. Thanks!

MaskRay added inline comments.Aug 30 2023, 8:14 PM
clang/lib/Basic/Sanitizers.cpp
129

Sorry, I just noticed that this adds a circular (if we consider headers) dependency from clangBasic to clangDriver. I'm removing it.