Skip to content

Commit

Permalink
[msan] Add handleIntrinsicByApplyingToShadow; support NEON tbl/tbx (l…
Browse files Browse the repository at this point in the history
…lvm#114490)

This adds a general function that handles intrinsics by applying the
intrinsic to the shadows, and applies it to the specific case of Arm
NEON TBL/TBX intrinsics.

This also updates the tests from
llvm#114462
  • Loading branch information
thurstond authored and smallp-o-p committed Nov 3, 2024
1 parent b546a42 commit fdaedcb
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 236 deletions.
72 changes: 72 additions & 0 deletions llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3944,6 +3944,56 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
}
}

/// Handle intrinsics by applying the intrinsic to the shadows.
///
/// The trailing arguments are passed verbatim to the intrinsic, though any
/// uninitialized trailing arguments can also taint the shadow e.g., for an
/// intrinsic with one trailing verbatim argument:
/// out = intrinsic(var1, var2, opType)
/// we compute:
/// shadow[out] =
/// intrinsic(shadow[var1], shadow[var2], opType) | shadow[opType]
///
/// For example, this can be applied to the Arm NEON vector table intrinsics
/// (tbl{1,2,3,4}).
///
/// The origin is approximated using setOriginForNaryOp.
void handleIntrinsicByApplyingToShadow(IntrinsicInst &I,
unsigned int trailingVerbatimArgs) {
IRBuilder<> IRB(&I);

assert(trailingVerbatimArgs < I.arg_size());

SmallVector<Value *, 8> ShadowArgs;
// Don't use getNumOperands() because it includes the callee
for (unsigned int i = 0; i < I.arg_size() - trailingVerbatimArgs; i++) {
Value *Shadow = getShadow(&I, i);
ShadowArgs.push_back(Shadow);
}

for (unsigned int i = I.arg_size() - trailingVerbatimArgs; i < I.arg_size();
i++) {
Value *Arg = I.getArgOperand(i);
ShadowArgs.push_back(Arg);
}

CallInst *CI =
IRB.CreateIntrinsic(I.getType(), I.getIntrinsicID(), ShadowArgs);
Value *CombinedShadow = CI;

// Combine the computed shadow with the shadow of trailing args
for (unsigned int i = I.arg_size() - trailingVerbatimArgs; i < I.arg_size();
i++) {
Value *Shadow =
CreateShadowCast(IRB, getShadow(&I, i), CombinedShadow->getType());
CombinedShadow = IRB.CreateOr(Shadow, CombinedShadow, "_msprop");
}

setShadow(&I, CombinedShadow);

setOriginForNaryOp(I);
}

void visitIntrinsicInst(IntrinsicInst &I) {
switch (I.getIntrinsicID()) {
case Intrinsic::uadd_with_overflow:
Expand Down Expand Up @@ -4319,6 +4369,28 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
break;
}

// Arm NEON vector table intrinsics have the source/table register(s) as
// arguments, followed by the index register. They return the output.
//
// 'TBL writes a zero if an index is out-of-range, while TBX leaves the
// original value unchanged in the destination register.'
// Conveniently, zero denotes a clean shadow, which means out-of-range
// indices for TBL will initialize the user data with zero and also clean
// the shadow. (For TBX, neither the user data nor the shadow will be
// updated, which is also correct.)
case Intrinsic::aarch64_neon_tbl1:
case Intrinsic::aarch64_neon_tbl2:
case Intrinsic::aarch64_neon_tbl3:
case Intrinsic::aarch64_neon_tbl4:
case Intrinsic::aarch64_neon_tbx1:
case Intrinsic::aarch64_neon_tbx2:
case Intrinsic::aarch64_neon_tbx3:
case Intrinsic::aarch64_neon_tbx4: {
// The last trailing argument (index register) should be handled verbatim
handleIntrinsicByApplyingToShadow(I, 1);
break;
}

default:
if (!handleUnknownIntrinsic(I))
visitInstruction(I);
Expand Down
Loading

0 comments on commit fdaedcb

Please sign in to comment.