diff --git a/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h new file mode 100644 index 0000000000000..b141ad27996b7 --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/RealtimeSanitizer.h @@ -0,0 +1,41 @@ +//===- RealtimeSanitizer.h - RealtimeSanitizer instrumentation --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the RealtimeSanitizer, an LLVM transformation for +// detecting and reporting realtime safety violations. +// +// The instrumentation pass inserts calls to __rtsan_realtime_enter and +// __rtsan_realtime_exit at the entry and exit points of functions that are +// marked with the appropriate attribute. +// +// See also: llvm-project/compiler-rt/lib/rtsan/ +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H + +#include "llvm/IR/PassManager.h" + +namespace llvm { + +struct RealtimeSanitizerOptions {}; + +class RealtimeSanitizerPass : public PassInfoMixin { +public: + RealtimeSanitizerPass(const RealtimeSanitizerOptions &Options); + PreservedAnalyses run(Function &F, AnalysisManager &AM); + + static bool isRequired() { return true; } + +private: + RealtimeSanitizerOptions Options{}; +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_INSTRUMENTATION_REALTIMESANITIZER_H diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index bcc69d5ac3db6..7bc1c870ce519 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -199,6 +199,7 @@ #include "llvm/Transforms/Instrumentation/PGOForceFunctionAttrs.h" #include "llvm/Transforms/Instrumentation/PGOInstrumentation.h" #include "llvm/Transforms/Instrumentation/PoisonChecking.h" +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" #include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h" #include "llvm/Transforms/Instrumentation/SanitizerCoverage.h" #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" @@ -1210,6 +1211,11 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) { return Opts; } +Expected parseRtSanPassOptions(StringRef Params) { + RealtimeSanitizerOptions Result; + return Result; +} + } // namespace /// Tests whether a pass name starts with a valid prefix for a default pipeline diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 61a5bab92927f..95842d15a35bf 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -592,6 +592,10 @@ FUNCTION_PASS_WITH_PARAMS( return WinEHPreparePass(DemoteCatchSwitchPHIOnly); }, parseWinEHPrepareOptions, "demote-catchswitch-only") +FUNCTION_PASS_WITH_PARAMS( + "rtsan", "RealtimeSanitizerPass", + [](RealtimeSanitizerOptions Opts) { return RealtimeSanitizerPass(Opts); }, + parseRtSanPassOptions, "") #undef FUNCTION_PASS_WITH_PARAMS #ifndef LOOPNEST_PASS diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 4e3f9e27e0c34..deab37801ff1d 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_component_library(LLVMInstrumentation ValueProfileCollector.cpp ThreadSanitizer.cpp HWAddressSanitizer.cpp + RealtimeSanitizer.cpp ADDITIONAL_HEADER_DIRS ${LLVM_MAIN_INCLUDE_DIR}/llvm/Transforms diff --git a/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp new file mode 100644 index 0000000000000..d3028ee97c3eb --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/RealtimeSanitizer.cpp @@ -0,0 +1,61 @@ +//===- RealtimeSanitizer.cpp - RealtimeSanitizer instrumentation *- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of the RealtimeSanitizer, an LLVM transformation for +// detecting and reporting realtime safety violations. +// +// See also: llvm-project/compiler-rt/lib/rtsan/ +// +//===----------------------------------------------------------------------===// + +#include "llvm/IR/Analysis.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" + +#include "llvm/Transforms/Instrumentation/RealtimeSanitizer.h" + +using namespace llvm; + +static void insertCallBeforeInstruction(Function &Fn, Instruction &Instruction, + const char *FunctionName) { + LLVMContext &Context = Fn.getContext(); + FunctionType *FuncType = FunctionType::get(Type::getVoidTy(Context), false); + FunctionCallee Func = + Fn.getParent()->getOrInsertFunction(FunctionName, FuncType); + IRBuilder<> Builder{&Instruction}; + Builder.CreateCall(Func, {}); +} + +static void insertCallAtFunctionEntryPoint(Function &Fn, + const char *InsertFnName) { + + insertCallBeforeInstruction(Fn, Fn.front().front(), InsertFnName); +} + +static void insertCallAtAllFunctionExitPoints(Function &Fn, + const char *InsertFnName) { + for (auto &BB : Fn) + for (auto &I : BB) + if (auto *RI = dyn_cast(&I)) + insertCallBeforeInstruction(Fn, I, InsertFnName); +} + +RealtimeSanitizerPass::RealtimeSanitizerPass( + const RealtimeSanitizerOptions &Options) + : Options{Options} {} + +PreservedAnalyses RealtimeSanitizerPass::run(Function &F, + AnalysisManager &AM) { + if (F.hasFnAttribute(Attribute::SanitizeRealtime)) { + insertCallAtFunctionEntryPoint(F, "__rtsan_realtime_enter"); + insertCallAtAllFunctionExitPoints(F, "__rtsan_realtime_exit"); + return PreservedAnalyses::none(); + } + + return PreservedAnalyses::all(); +} diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll new file mode 100644 index 0000000000000..a0bc4aef2cc31 --- /dev/null +++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan.ll @@ -0,0 +1,27 @@ +; RUN: opt < %s -passes=rtsan -S | FileCheck %s + +define void @violation() #0 { + %1 = alloca ptr, align 8 + %2 = call ptr @malloc(i64 noundef 2) #3 + store ptr %2, ptr %1, align 8 + ret void +} + +declare ptr @malloc(i64 noundef) #1 + +define noundef i32 @main() #2 { + %1 = alloca i32, align 4 + store i32 0, ptr %1, align 4 + call void @violation() #4 + ret i32 0 +} + +attributes #0 = { mustprogress noinline sanitize_realtime optnone ssp uwtable(sync) } + +; RealtimeSanitizer pass should insert __rtsan_realtime_enter right after function definition +; CHECK-LABEL: @violation() +; CHECK-NEXT: call{{.*}}@__rtsan_realtime_enter + +; RealtimeSanitizer pass should insert __rtsan_realtime_exit right before function return +; CHECK: call{{.*}}@__rtsan_realtime_exit +; CHECK-NEXT: ret{{.*}}void diff --git a/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_multi_return.ll b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_multi_return.ll new file mode 100644 index 0000000000000..39a1ff0b7c442 --- /dev/null +++ b/llvm/test/Instrumentation/RealtimeSanitizer/rtsan_multi_return.ll @@ -0,0 +1,30 @@ +; RUN: opt < %s -passes=rtsan -S | FileCheck %s + +define i32 @example(i32 %x) #0 { +entry: + %retval = alloca i32 + %cmp = icmp sgt i32 %x, 10 + br i1 %cmp, label %then, label %else + +then: + ret i32 1 + +else: + ret i32 0 +} + +attributes #0 = { mustprogress noinline sanitize_realtime optnone ssp uwtable(sync) } + +; RealtimeSanitizer pass should insert __rtsan_realtime_enter right after function definition +; CHECK-LABEL: @example( +; CHECK-NEXT: entry: +; CHECK-NEXT: call{{.*}}@__rtsan_realtime_enter + +; RealtimeSanitizer pass should insert the call at both function returns +; CHECK-LABEL: then: +; CHECK-NEXT: call{{.*}}@__rtsan_realtime_exit +; CHECK-NEXT: ret i32 1 + +; CHECK-LABEL: else: +; CHECK-NEXT: call{{.*}}@__rtsan_realtime_exit +; CHECK-NEXT: ret i32 0