146 lines · cpp
1//===- bolt/Passes/MarkRAStates.cpp ---------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the MarkRAStates class.10// Three CFIs have an influence on the RA State of an instruction:11// - NegateRAState flips the RA State,12// - RememberState pushes the RA State to a stack,13// - RestoreState pops the RA State from the stack.14// These are saved as MCAnnotations on instructions they refer to at CFI15// reading (in CFIReaderWriter::fillCFIInfoFor). In this pass, we can work out16// the RA State of each instruction, and save it as new MCAnnotations. The new17// annotations are Signing, Signed, Authenticating and Unsigned. After18// optimizations, .cfi_negate_ra_state CFIs are added to the places where the19// state changes in InsertNegateRAStatePass.20//21//===----------------------------------------------------------------------===//22#include "bolt/Passes/MarkRAStates.h"23#include "bolt/Core/BinaryFunction.h"24#include "bolt/Core/ParallelUtilities.h"25#include <cstdlib>26#include <optional>27#include <stack>28 29using namespace llvm;30 31namespace llvm {32namespace bolt {33 34bool MarkRAStates::runOnFunction(BinaryFunction &BF) {35 36 BinaryContext &BC = BF.getBinaryContext();37 38 for (const BinaryBasicBlock &BB : BF) {39 for (const MCInst &Inst : BB) {40 if ((BC.MIB->isPSignOnLR(Inst) ||41 (BC.MIB->isPAuthOnLR(Inst) && !BC.MIB->isPAuthAndRet(Inst))) &&42 !BC.MIB->hasNegateRAState(Inst)) {43 // Not all functions have .cfi_negate_ra_state in them. But if one does,44 // we expect psign/pauth instructions to have the hasNegateRAState45 // annotation.46 BC.outs() << "BOLT-INFO: inconsistent RAStates in function "47 << BF.getPrintName()48 << ": ptr sign/auth inst without .cfi_negate_ra_state\n";49 std::lock_guard<std::mutex> Lock(IgnoreMutex);50 BF.setIgnored();51 return false;52 }53 }54 }55 56 bool RAState = BF.getInitialRAState();57 std::stack<bool> RAStateStack;58 RAStateStack.push(RAState);59 60 for (BinaryBasicBlock &BB : BF) {61 for (MCInst &Inst : BB) {62 if (BC.MIB->isCFI(Inst))63 continue;64 65 if (BC.MIB->isPSignOnLR(Inst)) {66 if (RAState) {67 // RA signing instructions should only follow unsigned RA state.68 BC.outs() << "BOLT-INFO: inconsistent RAStates in function "69 << BF.getPrintName()70 << ": ptr signing inst encountered in Signed RA state\n";71 std::lock_guard<std::mutex> Lock(IgnoreMutex);72 BF.setIgnored();73 return false;74 }75 } else if (BC.MIB->isPAuthOnLR(Inst)) {76 if (!RAState) {77 // RA authenticating instructions should only follow signed RA state.78 BC.outs() << "BOLT-INFO: inconsistent RAStates in function "79 << BF.getPrintName()80 << ": ptr authenticating inst encountered in Unsigned RA "81 "state\n";82 std::lock_guard<std::mutex> Lock(IgnoreMutex);83 BF.setIgnored();84 return false;85 }86 }87 88 BC.MIB->setRAState(Inst, RAState);89 90 // Updating RAState. All updates are valid from the next instruction.91 // Because the same instruction can have remember and restore, the order92 // here is relevant. This is the reason to loop over Annotations instead93 // of just checking each in a predefined order.94 for (unsigned int Idx = 0; Idx < Inst.getNumOperands(); Idx++) {95 std::optional<int64_t> Annotation =96 BC.MIB->getAnnotationAtOpIndex(Inst, Idx);97 if (!Annotation)98 continue;99 if (Annotation == MCPlus::MCAnnotation::kNegateState)100 RAState = !RAState;101 else if (Annotation == MCPlus::MCAnnotation::kRememberState)102 RAStateStack.push(RAState);103 else if (Annotation == MCPlus::MCAnnotation::kRestoreState) {104 RAState = RAStateStack.top();105 RAStateStack.pop();106 }107 }108 }109 }110 return true;111}112 113Error MarkRAStates::runOnFunctions(BinaryContext &BC) {114 std::atomic<uint64_t> FunctionsIgnored{0};115 ParallelUtilities::WorkFuncTy WorkFun = [&](BinaryFunction &BF) {116 if (!runOnFunction(BF)) {117 FunctionsIgnored++;118 }119 };120 121 ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) {122 // We can skip functions which did not include negate-ra-state CFIs. This123 // includes code using pac-ret hardening as well, if the binary is124 // compiled with `-fno-exceptions -fno-unwind-tables125 // -fno-asynchronous-unwind-tables`126 return !BF.containedNegateRAState() || BF.isIgnored();127 };128 129 int Total = llvm::count_if(BC.getBinaryFunctions(), [&](auto &P) {130 return P.second.containedNegateRAState() && !P.second.isIgnored();131 });132 133 ParallelUtilities::runOnEachFunction(134 BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFun,135 SkipPredicate, "MarkRAStates");136 BC.outs() << "BOLT-INFO: MarkRAStates ran on " << Total137 << " functions. Ignored " << FunctionsIgnored << " functions "138 << format("(%.2lf%%)", (100.0 * FunctionsIgnored) / Total)139 << " because of CFI inconsistencies\n";140 141 return Error::success();142}143 144} // end namespace bolt145} // end namespace llvm146