Skip to content

Commit

Permalink
[DeviceSanitizer] Support detecting out-of-bounds error on DeviceGlob…
Browse files Browse the repository at this point in the history
  • Loading branch information
zhaomaosu authored Feb 29, 2024
1 parent 7d913bc commit a14cfdd
Show file tree
Hide file tree
Showing 18 changed files with 485 additions and 69 deletions.
4 changes: 1 addition & 3 deletions libdevice/include/device-sanitizer-report.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@
//===----------------------------------------------------------------------===//
#pragma once

// Treat this header as system one to workaround frontend's restriction
#pragma clang system_header

#include <cinttypes>

enum class DeviceSanitizerErrorType : int32_t {
Expand All @@ -29,6 +26,7 @@ enum class DeviceSanitizerMemoryType : int32_t {
LOCAL,
PRIVATE,
MEM_BUFFER,
DEVICE_GLOBAL,
};

// NOTE Layout of this structure should be aligned with the one in
Expand Down
8 changes: 1 addition & 7 deletions libdevice/include/sanitizer_device_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,10 @@
#include "spir_global_var.hpp"
#include <cstdint>

// Treat this header as system one to workaround frontend's restriction
#pragma clang system_header

template <typename T>
class
#ifdef __SYCL_DEVICE_ONLY__
[[__sycl_detail__::global_variable_allowed, __sycl_detail__::device_global,
__sycl_detail__::add_ir_attributes_global_variable(
"sycl-device-global-size", "sycl-device-image-scope", sizeof(T),
nullptr)]]
[[__sycl_detail__::global_variable_allowed, __sycl_detail__::device_global]]
#endif
DeviceGlobal {
public:
Expand Down
5 changes: 5 additions & 0 deletions libdevice/sanitizer_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const int kUsmDeviceRedzoneMagic = (char)0x81;
const int kUsmHostRedzoneMagic = (char)0x82;
const int kUsmSharedRedzoneMagic = (char)0x83;
const int kMemBufferRedzoneMagic = (char)0x84;
const int kDeviceGlobalRedZoneMagic = (char)0x85;

const int kUsmDeviceDeallocatedMagic = (char)0x91;
const int kUsmHostDeallocatedMagic = (char)0x92;
Expand Down Expand Up @@ -366,6 +367,10 @@ void __asan_report_access_error(uptr addr, int32_t as, size_t size,
memory_type = DeviceSanitizerMemoryType::LOCAL;
error_type = DeviceSanitizerErrorType::OUT_OF_BOUND;
break;
case kDeviceGlobalRedZoneMagic:
memory_type = DeviceSanitizerMemoryType::DEVICE_GLOBAL;
error_type = DeviceSanitizerErrorType::OUT_OF_BOUND;
break;
default:
memory_type = DeviceSanitizerMemoryType::UNKNOWN;
error_type = DeviceSanitizerErrorType::UNKNOWN;
Expand Down
23 changes: 23 additions & 0 deletions llvm/include/llvm/SYCLLowerIR/SanitizeDeviceGlobal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//===-- SanitizeDeviceGlobal.h - instrument device global for sanitizer ---===//
//
// 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 pass adds red zone to each image scope device global and record the
// information like size, red zone size and beginning address. The information
// will be used by address sanitizer.
//===----------------------------------------------------------------------===//

#include "llvm/IR/PassManager.h"

namespace llvm {

class SanitizeDeviceGlobalPass
: public PassInfoMixin<SanitizeDeviceGlobalPass> {
public:
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM);
};

} // namespace llvm
1 change: 1 addition & 0 deletions llvm/lib/SYCLLowerIR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ add_llvm_component_library(LLVMSYCLLowerIR
SYCLPropagateAspectsUsage.cpp
SYCLPropagateJointMatrixUsage.cpp
SYCLUtils.cpp
SanitizeDeviceGlobal.cpp

LocalAccessorToSharedMemory.cpp
GlobalOffset.cpp
Expand Down
144 changes: 144 additions & 0 deletions llvm/lib/SYCLLowerIR/SanitizeDeviceGlobal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===-- SanitizeDeviceGlobal.cpp - instrument device global for sanitizer -===//
//
// 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 pass adds red zone to each image scope device global and record the
// information like size, red zone size and beginning address. The information
// will be used by address sanitizer.
// TODO: Do this in AddressSanitizer pass when urProgramGetGlobalVariablePointer
// is implemented.
//===----------------------------------------------------------------------===//

#include "llvm/SYCLLowerIR/SanitizeDeviceGlobal.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/SYCLLowerIR/DeviceGlobals.h"

#define DEBUG_TYPE "SanitizeDeviceGlobal"

using namespace llvm;

namespace {

// Add extra red zone to each image scope device globals if the module has been
// instrumented by sanitizer pass. And record their infomation like size, red
// zone size, beginning address.
static bool instrumentDeviceGlobal(Module &M) {
auto &DL = M.getDataLayout();
IRBuilder<> IRB(M.getContext());
SmallVector<GlobalVariable *, 8> GlobalsToRemove;
SmallVector<GlobalVariable *, 8> NewDeviceGlobals;
SmallVector<Constant *, 8> DeviceGlobalMetadata;

constexpr uint64_t MaxRZ = 1 << 18;
constexpr uint64_t MinRZ = 32;

Type *IntTy = Type::getIntNTy(M.getContext(), DL.getPointerSizeInBits());

// Device global meta data is described by a structure
// size_t device_global_size
// size_t device_global_size_with_red_zone
// size_t beginning address of the device global
StructType *StructTy = StructType::get(IntTy, IntTy, IntTy);

for (auto &G : M.globals()) {
// Non image scope device globals are implemented by device USM, and the
// out-of-bounds check for them will be done by sanitizer USM part. So we
// exclude them here.
if (!isDeviceGlobalVariable(G) || !hasDeviceImageScopeProperty(G))
continue;

Type *Ty = G.getValueType();
const uint64_t SizeInBytes = DL.getTypeAllocSize(Ty);
const uint64_t RightRedzoneSize = [&] {
// The algorithm for calculating red zone size comes from
// llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
uint64_t RZ = 0;
if (SizeInBytes <= MinRZ / 2) {
// Reduce redzone size for small size objects, e.g. int, char[1].
// Optimize when SizeInBytes is less than or equal to half of MinRZ.
RZ = MinRZ - SizeInBytes;
} else {
// Calculate RZ, where MinRZ <= RZ <= MaxRZ, and RZ ~ 1/4 *
// SizeInBytes.
RZ = std::clamp((SizeInBytes / MinRZ / 4) * MinRZ, MinRZ, MaxRZ);

// Round up to multiple of MinRZ.
if (SizeInBytes % MinRZ)
RZ += MinRZ - (SizeInBytes % MinRZ);
}

assert((RZ + SizeInBytes) % MinRZ == 0);
return RZ;
}();
Type *RightRedZoneTy = ArrayType::get(IRB.getInt8Ty(), RightRedzoneSize);
StructType *NewTy = StructType::get(Ty, RightRedZoneTy);
Constant *NewInitializer = ConstantStruct::get(
NewTy, G.getInitializer(), Constant::getNullValue(RightRedZoneTy));

// Create a new global variable with enough space for a redzone.
GlobalVariable *NewGlobal = new GlobalVariable(
M, NewTy, G.isConstant(), G.getLinkage(), NewInitializer, "", &G,
G.getThreadLocalMode(), G.getAddressSpace());
NewGlobal->copyAttributesFrom(&G);
NewGlobal->setComdat(G.getComdat());
NewGlobal->setAlignment(Align(MinRZ));
NewGlobal->copyMetadata(&G, 0);

Value *Indices2[2];
Indices2[0] = IRB.getInt32(0);
Indices2[1] = IRB.getInt32(0);

G.replaceAllUsesWith(
ConstantExpr::getGetElementPtr(NewTy, NewGlobal, Indices2, true));
NewGlobal->takeName(&G);
GlobalsToRemove.push_back(&G);
NewDeviceGlobals.push_back(NewGlobal);
DeviceGlobalMetadata.push_back(ConstantStruct::get(
StructTy, ConstantInt::get(IntTy, SizeInBytes),
ConstantInt::get(IntTy, SizeInBytes + RightRedzoneSize),
ConstantExpr::getPointerCast(NewGlobal, IntTy)));
}

if (GlobalsToRemove.empty())
return false;

// Create global to record number of device globals
GlobalVariable *NumOfDeviceGlobals = new GlobalVariable(
M, IntTy, false, GlobalValue::ExternalLinkage,
ConstantInt::get(IntTy, NewDeviceGlobals.size()),
"__AsanDeviceGlobalCount", nullptr, GlobalValue::NotThreadLocal, 1);
NumOfDeviceGlobals->setUnnamedAddr(GlobalValue::UnnamedAddr::Local);

// Create meta data global to record device globals' information
ArrayType *ArrayTy = ArrayType::get(StructTy, NewDeviceGlobals.size());
Constant *MetadataInitializer =
ConstantArray::get(ArrayTy, DeviceGlobalMetadata);
GlobalVariable *AsanDeviceGlobalMetadata = new GlobalVariable(
M, MetadataInitializer->getType(), false, GlobalValue::ExternalLinkage,
MetadataInitializer, "__AsanDeviceGlobalMetadata", nullptr,
GlobalValue::NotThreadLocal, 1);
AsanDeviceGlobalMetadata->setUnnamedAddr(GlobalValue::UnnamedAddr::Local);

for (auto *G : GlobalsToRemove)
G->eraseFromParent();

return true;
}

}

namespace llvm {

PreservedAnalyses SanitizeDeviceGlobalPass::run(Module &M,
ModuleAnalysisManager &MAM) {
bool Modified = false;

Modified |= instrumentDeviceGlobal(M);

return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all();
}

}
Loading

0 comments on commit a14cfdd

Please sign in to comment.