[esan] EfficiencySanitizer base runtime library

Summary:
Adds the initial version of a runtime library for the new
EfficiencySanitizer ("esan") family of tools.  The library includes:

+ Slowpath code via callouts from the compiler instrumentation for
  each memory access.

+ Registration of atexit() to call finalization code.

+ Runtime option flags controlled by the environment variable
  ESAN_OPTIONS.  The common sanitizer flags are supported such as
  verbosity and log_path.

+ An initial simple test.

Still TODO: common code for libc interceptors and shadow memory mapping,
and tool-specific code for shadow state updating.

Reviewers: eugenis, vitalybuka, aizatsky, filcab

Subscribers: filcab, vkalintiris, kubabrecka, llvm-commits, zhaoqin, kcc

Differential Revision: http://reviews.llvm.org/D19168

llvm-svn: 267060
This commit is contained in:
Derek Bruening 2016-04-21 21:32:25 +00:00
parent 256c2e14c7
commit af7aaae1bc
13 changed files with 476 additions and 0 deletions

View File

@ -269,6 +269,7 @@ set(ALL_UBSAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64}
${MIPS32} ${MIPS64} ${PPC64} ${S390X})
set(ALL_SAFESTACK_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM64} ${MIPS32} ${MIPS64})
set(ALL_CFI_SUPPORTED_ARCH ${X86} ${X86_64})
set(ALL_ESAN_SUPPORTED_ARCH ${X86_64})
if(APPLE)
include(CompilerRTDarwinUtils)
@ -501,6 +502,9 @@ if(APPLE)
list_intersect(CFI_SUPPORTED_ARCH
ALL_CFI_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(ESAN_SUPPORTED_ARCH
ALL_ESAN_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
else()
# Architectures supported by compiler-rt libraries.
filter_available_targets(BUILTIN_SUPPORTED_ARCH
@ -523,6 +527,7 @@ else()
filter_available_targets(SAFESTACK_SUPPORTED_ARCH
${ALL_SAFESTACK_SUPPORTED_ARCH})
filter_available_targets(CFI_SUPPORTED_ARCH ${ALL_CFI_SUPPORTED_ARCH})
filter_available_targets(ESAN_SUPPORTED_ARCH ${ALL_ESAN_SUPPORTED_ARCH})
endif()
if (MSVC)
@ -630,3 +635,10 @@ if (COMPILER_RT_HAS_SANITIZER_COMMON AND CFI_SUPPORTED_ARCH AND
else()
set(COMPILER_RT_HAS_CFI FALSE)
endif()
if (COMPILER_RT_HAS_SANITIZER_COMMON AND ESAN_SUPPORTED_ARCH AND
OS_NAME MATCHES "Linux")
set(COMPILER_RT_HAS_ESAN TRUE)
else()
set(COMPILER_RT_HAS_ESAN FALSE)
endif()

View File

@ -48,4 +48,8 @@ if(COMPILER_RT_BUILD_SANITIZERS)
if(COMPILER_RT_HAS_CFI)
add_subdirectory(cfi)
endif()
if(COMPILER_RT_HAS_ESAN)
add_subdirectory(esan)
endif()
endif()

View File

@ -0,0 +1,35 @@
# Build for the EfficiencySanitizer runtime support library.
add_custom_target(esan)
set(ESAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS})
append_rtti_flag(OFF ESAN_RTL_CFLAGS)
include_directories(..)
set(ESAN_SOURCES
esan.cpp
esan_interface.cpp)
foreach (arch ${ESAN_SUPPORTED_ARCH})
add_compiler_rt_runtime(clang_rt.esan
STATIC
ARCHS ${arch}
SOURCES ${ESAN_SOURCES}
$<TARGET_OBJECTS:RTInterception.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommon.${arch}>
$<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>
CFLAGS ${ESAN_RTL_CFLAGS})
add_sanitizer_rt_symbols(clang_rt.esan
ARCHS ${arch}
EXTRA esan.syms.extra)
add_dependencies(esan
clang_rt.esan-${arch}
clang_rt.esan-${arch}-symbols)
endforeach()
add_dependencies(compiler-rt esan)
if (COMPILER_RT_INCLUDE_TESTS)
# TODO(bruening): add tests via add_subdirectory(tests)
endif()

View File

@ -0,0 +1,112 @@
//===-- esan.cpp ----------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of EfficiencySanitizer, a family of performance tuners.
//
// Main file (entry points) for the Esan run-time.
//===----------------------------------------------------------------------===//
#include "esan.h"
#include "esan_interface_internal.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_flag_parser.h"
#include "sanitizer_common/sanitizer_flags.h"
// See comment below.
extern "C" {
extern void __cxa_atexit(void (*function)(void));
}
namespace __esan {
bool EsanIsInitialized;
ToolType WhichTool;
static const char EsanOptsEnv[] = "ESAN_OPTIONS";
// We are combining multiple performance tuning tools under the umbrella of
// one EfficiencySanitizer super-tool. Most of our tools have very similar
// memory access instrumentation, shadow memory mapping, libc interception,
// etc., and there is typically more shared code than distinct code.
//
// We are not willing to dispatch on tool dynamically in our fastpath
// instrumentation: thus, which tool to use is a static option selected
// at compile time and passed to __esan_init().
//
// We are willing to pay the overhead of tool dispatch in the slowpath to more
// easily share code. We expect to only come here rarely.
// If this becomes a performance hit, we can add separate interface
// routines for each subtool (e.g., __esan_cache_frag_aligned_load_4).
// But for libc interceptors, we'll have to do one of the following:
// A) Add multiple-include support to sanitizer_common_interceptors.inc,
// instantiate it separately for each tool, and call the selected
// tool's intercept setup code.
// B) Build separate static runtime libraries, one for each tool.
// C) Completely split the tools into separate sanitizers.
void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite) {
VPrintf(3, "in esan::%s %p: %c %p %d\n", __FUNCTION__, PC,
IsWrite ? 'w' : 'r', Addr, Size);
if (WhichTool == ESAN_CacheFrag) {
// TODO(bruening): add shadow mapping and update shadow bits here.
// We'll move this to cache_frag.cpp once we have something.
}
}
static void initializeFlags() {
// Once we add our own flags we'll parse them here.
// For now the common ones are sufficient.
FlagParser Parser;
SetCommonFlagsDefaults();
RegisterCommonFlags(&Parser);
Parser.ParseString(GetEnv(EsanOptsEnv));
InitializeCommonFlags();
if (Verbosity())
ReportUnrecognizedFlags();
if (common_flags()->help)
Parser.PrintFlagDescriptions();
__sanitizer_set_report_path(common_flags()->log_path);
}
void initializeLibrary(ToolType Tool) {
// We assume there is only one thread during init.
if (EsanIsInitialized) {
CHECK(Tool == WhichTool);
return;
}
WhichTool = Tool;
SanitizerToolName = "EfficiencySanitizer";
initializeFlags();
// Intercepting libc _exit or exit via COMMON_INTERCEPTOR_ON_EXIT only
// finalizes on an explicit exit call by the app. To handle a normal
// exit we register an atexit handler.
::__cxa_atexit((void (*)())finalizeLibrary);
VPrintf(1, "in esan::%s\n", __FUNCTION__);
if (WhichTool != ESAN_CacheFrag) {
Printf("ERROR: unknown tool %d requested\n", WhichTool);
Die();
}
EsanIsInitialized = true;
}
int finalizeLibrary() {
VPrintf(1, "in esan::%s\n", __FUNCTION__);
if (WhichTool == ESAN_CacheFrag) {
// FIXME NYI: we need to add sampling + callstack gathering and have a
// strategy for how to generate a final report.
// We'll move this to cache_frag.cpp once we have something.
Report("%s is not finished: nothing yet to report\n", SanitizerToolName);
}
return 0;
}
} // namespace __esan

View File

@ -0,0 +1,44 @@
//===-- esan.h --------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of EfficiencySanitizer, a family of performance tuners.
//
// Main internal esan header file.
//
// Ground rules:
// - C++ run-time should not be used (static CTORs, RTTI, exceptions, static
// function-scope locals)
// - All functions/classes/etc reside in namespace __esan, except for those
// declared in esan_interface_internal.h.
// - Platform-specific files should be used instead of ifdefs (*).
// - No system headers included in header files (*).
// - Platform specific headers included only into platform-specific files (*).
//
// (*) Except when inlining is critical for performance.
//===----------------------------------------------------------------------===//
#ifndef ESAN_H
#define ESAN_H
#include "sanitizer_common/sanitizer_common.h"
#include "esan_interface_internal.h"
namespace __esan {
extern bool EsanIsInitialized;
extern ToolType WhichTool;
void initializeLibrary(ToolType Tool);
int finalizeLibrary();
void processRangeAccess(uptr PC, uptr Addr, int Size, bool IsWrite);
} // namespace __esan
#endif // ESAN_H

View File

@ -0,0 +1,3 @@
__esan_init
__esan_aligned*
__esan_unaligned*

View File

@ -0,0 +1,102 @@
//===-- esan_interface.cpp ------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of EfficiencySanitizer, a family of performance tuners.
//
//===----------------------------------------------------------------------===//
#include "esan_interface_internal.h"
#include "esan.h"
#include "sanitizer_common/sanitizer_internal_defs.h"
using namespace __esan; // NOLINT
void __esan_init(ToolType Tool) {
initializeLibrary(Tool);
}
void __esan_aligned_load1(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, false);
}
void __esan_aligned_load2(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false);
}
void __esan_aligned_load4(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false);
}
void __esan_aligned_load8(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false);
}
void __esan_aligned_load16(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false);
}
void __esan_aligned_store1(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 1, true);
}
void __esan_aligned_store2(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true);
}
void __esan_aligned_store4(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true);
}
void __esan_aligned_store8(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true);
}
void __esan_aligned_store16(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true);
}
void __esan_unaligned_load2(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, false);
}
void __esan_unaligned_load4(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, false);
}
void __esan_unaligned_load8(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, false);
}
void __esan_unaligned_load16(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, false);
}
void __esan_unaligned_store2(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 2, true);
}
void __esan_unaligned_store4(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 4, true);
}
void __esan_unaligned_store8(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 8, true);
}
void __esan_unaligned_store16(void *Addr) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, 16, true);
}
void __esan_unaligned_loadN(void *Addr, uptr Size) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, false);
}
void __esan_unaligned_storeN(void *Addr, uptr Size) {
processRangeAccess(GET_CALLER_PC(), (uptr)Addr, Size, true);
}

View File

@ -0,0 +1,72 @@
//===-- esan_interface_internal.h -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of EfficiencySanitizer, a family of performance tuners.
//
// Calls to the functions declared in this header will be inserted by
// the instrumentation module.
//===----------------------------------------------------------------------===//
#ifndef ESAN_INTERFACE_INTERNAL_H
#define ESAN_INTERFACE_INTERNAL_H
#include <sanitizer_common/sanitizer_internal_defs.h>
// This header should NOT include any other headers.
// All functions in this header are extern "C" and start with __esan_.
extern "C" {
// This should be kept consistent with LLVM's EfficiencySanitizerOptions.
// The value is passed as a 32-bit integer by the compiler.
typedef enum Type : u32 {
ESAN_None = 0,
ESAN_CacheFrag,
} ToolType;
// This function should be called at the very beginning of the process,
// before any instrumented code is executed and before any call to malloc.
SANITIZER_INTERFACE_ATTRIBUTE void __esan_init(ToolType Tool);
// The instrumentation module will insert a call to one of these routines prior
// to each load and store instruction for which we do not have "fastpath"
// inlined instrumentation. These calls constitute the "slowpath" for our
// tools. We have separate routines for each type of memory access to enable
// targeted optimization.
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load1(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load2(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load4(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load8(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_load16(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store1(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store2(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store4(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store8(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_aligned_store16(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load2(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load4(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load8(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_load16(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store2(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store4(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store8(void *Addr);
SANITIZER_INTERFACE_ATTRIBUTE void __esan_unaligned_store16(void *Addr);
// These cover unusually-sized accesses.
SANITIZER_INTERFACE_ATTRIBUTE
void __esan_unaligned_loadN(void *Addr, uptr Size);
SANITIZER_INTERFACE_ATTRIBUTE
void __esan_unaligned_storeN(void *Addr, uptr Size);
} // extern "C"
#endif // ESAN_INTERFACE_INTERNAL_H

View File

@ -70,6 +70,9 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
if(COMPILER_RT_HAS_SAFESTACK)
add_subdirectory(safestack)
endif()
if(COMPILER_RT_HAS_ESAN)
add_subdirectory(esan)
endif()
endif()
if(COMPILER_RT_STANDALONE_BUILD)

View File

@ -0,0 +1,32 @@
set(ESAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS})
if(NOT COMPILER_RT_STANDALONE_BUILD)
list(APPEND ESAN_TEST_DEPS esan)
endif()
set(ESAN_TESTSUITES)
set(ESAN_TEST_ARCH ${ESAN_SUPPORTED_ARCH})
set(ESAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
foreach(arch ${ESAN_TEST_ARCH})
set(ESAN_TEST_TARGET_ARCH ${arch})
string(TOLOWER "-${arch}" ESAN_TEST_CONFIG_SUFFIX)
get_target_flags_for_arch(${arch} ESAN_TEST_TARGET_CFLAGS)
string(REPLACE ";" " " ESAN_TEST_TARGET_CFLAGS "${ESAN_TEST_TARGET_CFLAGS}")
string(TOUPPER ${arch} ARCH_UPPER_CASE)
set(CONFIG_NAME ${ARCH_UPPER_CASE}Config)
configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg)
list(APPEND ESAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
endforeach()
# TODO(bruening): add Unit/ tests as well
add_lit_testsuite(check-esan "Running EfficiencySanitizer tests"
${ESAN_TESTSUITES}
DEPENDS ${ESAN_TEST_DEPS})
set_target_properties(check-esan PROPERTIES FOLDER "Esan tests")

View File

@ -0,0 +1,11 @@
// RUN: %clang_esan_frag -O0 %s -o %t 2>&1
// RUN: %env_esan_opts=verbosity=1 %run %t 2>&1 | FileCheck %s
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
// CHECK: in esan::initializeLibrary
// CHECK-NEXT: in esan::finalizeLibrary
// CHECK-NEXT: {{.*}}EfficiencySanitizer is not finished: nothing yet to report
return 0;
}

View File

@ -0,0 +1,32 @@
# -*- Python -*-
import os
# Setup config name.
config.name = 'EfficiencySanitizer' + config.name_suffix
# Setup source root.
config.test_source_root = os.path.dirname(__file__)
# Setup default compiler flags used with -fsanitize=efficiency option.
base_cflags = ([config.target_cflags] + config.debug_info_flags)
base_cxxflags = config.cxx_mode_flags + base_cflags
frag_cflags = (["-fsanitize=efficiency-cache-frag"] + base_cflags)
def build_invocation(compile_flags):
return " " + " ".join([config.clang] + compile_flags) + " "
config.substitutions.append( ("%clang_esan_frag ",
build_invocation(frag_cflags)) )
default_esan_opts = ''
config.substitutions.append(('%env_esan_opts=',
'env ESAN_OPTIONS=' + default_esan_opts))
# Default test suffixes.
config.suffixes = ['.c', '.cpp']
# EfficiencySanitizer tests are currently supported on Linux x86-64 only.
if config.host_os not in ['Linux'] or config.target_arch != 'x86_64':
config.unsupported = True

View File

@ -0,0 +1,14 @@
## Autogenerated by LLVM/Clang configuration.
# Do not edit!
# Tool-specific config options.
config.name_suffix = "@ESAN_TEST_CONFIG_SUFFIX@"
config.esan_lit_source_dir = "@ESAN_LIT_SOURCE_DIR@"
config.target_cflags = "@ESAN_TEST_TARGET_CFLAGS@"
config.target_arch = "@ESAN_TEST_TARGET_ARCH@"
# Load common config for all compiler-rt lit tests.
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")
# Load tool-specific config that would do the real work.
lit_config.load_config(config, "@ESAN_LIT_SOURCE_DIR@/lit.cfg")