From c014965edf13a3745e17c175408fa1ce9082f74c Mon Sep 17 00:00:00 2001 From: Benjamin Orthen Date: Mon, 23 May 2022 17:37:18 +0200 Subject: [PATCH] [SBCETS] Add SoftBound+CETS compiler pass and runtime library --- compiler-rt/cmake/config-ix.cmake | 13 +- compiler-rt/lib/softboundcets/CMakeLists.txt | 157 + .../softboundcets/softboundcets-wrappers.cpp | 5134 ++++++++++++++ .../lib/softboundcets/softboundcets.cpp | 1497 ++++ compiler-rt/lib/softboundcets/softboundcets.h | 517 ++ compiler-rt/test/softboundcets/CMakeLists.txt | 0 llvm/lib/Transforms/CMakeLists.txt | 1 + .../InstCombine/InstructionCombining.cpp | 6 +- .../Transforms/SoftBoundCETS/CMakeLists.txt | 29 + .../SoftBoundCETS/FixByValAttributes.cpp | 301 + .../SoftBoundCETS/FixByValAttributes.h | 83 + .../SoftBoundCETS/SoftBoundCETS.cpp | 6199 +++++++++++++++++ .../Transforms/SoftBoundCETS/SoftBoundCETS.h | 539 ++ llvm/lib/Transforms/SoftBoundCETS/Utils.cpp | 128 + llvm/lib/Transforms/SoftBoundCETS/Utils.h | 22 + 15 files changed, 14624 insertions(+), 2 deletions(-) create mode 100644 compiler-rt/lib/softboundcets/CMakeLists.txt create mode 100644 compiler-rt/lib/softboundcets/softboundcets-wrappers.cpp create mode 100644 compiler-rt/lib/softboundcets/softboundcets.cpp create mode 100644 compiler-rt/lib/softboundcets/softboundcets.h create mode 100644 compiler-rt/test/softboundcets/CMakeLists.txt create mode 100644 llvm/lib/Transforms/SoftBoundCETS/CMakeLists.txt create mode 100644 llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.cpp create mode 100644 llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.h create mode 100644 llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.cpp create mode 100644 llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.h create mode 100644 llvm/lib/Transforms/SoftBoundCETS/Utils.cpp create mode 100644 llvm/lib/Transforms/SoftBoundCETS/Utils.h diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index f81b8384cbd5..f8fa266cad58 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -299,6 +299,7 @@ set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} ${RISCV64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9}) set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9}) +set(ALL_SOFTBOUNDCETS_SUPPORTED_ARCH ${X86} ${X86_64}) set(ALL_CRT_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV32} ${RISCV64} ${VE}) set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64}) @@ -539,6 +540,9 @@ if(APPLE) list_intersect(ASAN_SUPPORTED_ARCH ALL_ASAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) + list_intersect(SOFTBOUNDCETS_SUPPORTED_ARCH + ALL_SOFTBOUNDCETS_SUPPORTED_ARCH + SANITIZER_COMMON_SUPPORTED_ARCH) list_intersect(DFSAN_SUPPORTED_ARCH ALL_DFSAN_SUPPORTED_ARCH SANITIZER_COMMON_SUPPORTED_ARCH) @@ -600,6 +604,7 @@ else() filter_available_targets(UBSAN_COMMON_SUPPORTED_ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH}) filter_available_targets(ASAN_SUPPORTED_ARCH ${ALL_ASAN_SUPPORTED_ARCH}) + filter_available_targets(SOFTBOUNDCETS_SUPPORTED_ARCH ${ALL_SOFTBOUNDCETS_SUPPORTED_ARCH}) filter_available_targets(FUZZER_SUPPORTED_ARCH ${ALL_FUZZER_SUPPORTED_ARCH}) filter_available_targets(DFSAN_SUPPORTED_ARCH ${ALL_DFSAN_SUPPORTED_ARCH}) filter_available_targets(LSAN_SUPPORTED_ARCH ${ALL_LSAN_SUPPORTED_ARCH}) @@ -640,7 +645,7 @@ if(COMPILER_RT_SUPPORTED_ARCH) endif() message(STATUS "Compiler-RT supported architectures: ${COMPILER_RT_SUPPORTED_ARCH}") -set(ALL_SANITIZERS asan;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;gwp_asan) +set(ALL_SANITIZERS asan;softboundcets;dfsan;msan;hwasan;tsan;safestack;cfi;scudo;ubsan_minimal;gwp_asan) set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING "sanitizers to build if supported on the target (all;${ALL_SANITIZERS})") list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") @@ -666,6 +671,12 @@ else() set(COMPILER_RT_HAS_ASAN FALSE) endif() +if (COMPILER_RT_HAS_SANITIZER_COMMON AND SOFTBOUNDCETS_SUPPORTED_ARCH) + set(COMPILER_RT_HAS_SOFTBOUNDCETS TRUE) +else() + set(COMPILER_RT_HAS_SOFTBOUNDCETS FALSE) +endif() + if (OS_NAME MATCHES "Linux|FreeBSD|Windows|NetBSD|SunOS") set(COMPILER_RT_ASAN_HAS_STATIC_RUNTIME TRUE) else() diff --git a/compiler-rt/lib/softboundcets/CMakeLists.txt b/compiler-rt/lib/softboundcets/CMakeLists.txt new file mode 100644 index 000000000000..5286e6994efc --- /dev/null +++ b/compiler-rt/lib/softboundcets/CMakeLists.txt @@ -0,0 +1,157 @@ +set(SOFTBOUNDCETS_SOURCES + softboundcets.cpp + softboundcets-wrappers.cpp +) + +set(SOFTBOUNDCETS_HEADERS + softboundcets.h +) + +set(SOFTBOUNDCETS_COMMON_CFLAGS ${SANITIZER_COMMON_CFLAGS} -D__SOFTBOUNDCETS_SPATIAL_TEMPORAL=1 -O2) + +set(SOFTBOUNDCETS_STANDARD_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -DLOG_LEVEL=3 -g) +set(SOFTBOUNDCETS_DYNAMIC_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -D__SOFTBOUNDCETS_DYNAMIC_RT=1 -DLOG_LEVEL=3 -O0 -g) +set(SOFTBOUNDCETS_INLINING_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -flto -g0) +set(SOFTBOUNDCETS_BENCHMARK_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -D__SOFTBOUNDCETS_BENCHMARKING=1 -g -O3) +set(SOFTBOUNDCETS_BENCHMARK_INLINING_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -D__SOFTBOUNDCETS_BENCHMARKING=1 -flto -O3 -g0) +set(SOFTBOUNDCETS_BENCHMARK_INLINING_INVALID_LOCKS_CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} -D__SOFTBOUNDCETS_BENCHMARKING=1 -D__SOFTBOUNDCETS_PREVENT_SEGFAULTS_ON_INVALID_LOCKS=1 -flto -O3 -g0) + +append_rtti_flag(OFF SANITIZER_COMMON_CFLAGS) + +set(SOFTBOUNDCETS_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}) +set(SOFTBOUNDCETS_DYNAMIC_LIBS ${SANITIZER_CXX_ABI_LIBRARIES} ${SANITIZER_COMMON_LINK_LIBS}) + +append_list_if(COMPILER_RT_HAS_LIBDL dl SOFTBOUNDCETS_DYNAMIC_LIBS) +append_list_if(COMPILER_RT_HAS_LIBRT rt SOFTBOUNDCETS_DYNAMIC_LIBS) +append_list_if(COMPILER_RT_HAS_LIBM m SOFTBOUNDCETS_DYNAMIC_LIBS) +append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread SOFTBOUNDCETS_DYNAMIC_LIBS) +append_list_if(COMPILER_RT_HAS_LIBLOG log SOFTBOUNDCETS_DYNAMIC_LIBS) + + + +add_compiler_rt_component(softboundcets) + +add_compiler_rt_object_libraries(RTsoftboundcets_dynamic + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_DYNAMIC_CFLAGS}) + + +add_compiler_rt_object_libraries(RTsoftboundcets + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_STANDARD_CFLAGS} +) + +add_compiler_rt_object_libraries(RTsoftboundcets_benchmark + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_CFLAGS} +) + +add_compiler_rt_object_libraries(RTsoftboundcets_inlining + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_INLINING_CFLAGS} +) + +add_compiler_rt_object_libraries(RTsoftboundcets_benchmark_inlining + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_INLINING_CFLAGS} +) + +add_compiler_rt_object_libraries(RTsoftboundcets_benchmark_inlining_invalid_locks + ARCHS ${SOFTBOUNDCETS_SUPPORTED_ARCH} + SOURCES ${SOFTBOUNDCETS_SOURCES} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_INLINING_INVALID_LOCKS_CFLAGS} +) + + + +include_directories(..) + +foreach(arch ${SOFTBOUNDCETS_SUPPORTED_ARCH}) + add_compiler_rt_runtime(clang_rt.softboundcets + STATIC + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_COMMON_CFLAGS} + PARENT_TARGET softboundcets) + + add_compiler_rt_runtime(clang_rt.softboundcets_benchmark + STATIC + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets_benchmark + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_CFLAGS} + PARENT_TARGET softboundcets) + + add_compiler_rt_runtime(clang_rt.softboundcets_inlining + STATIC + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets_inlining + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_INLINING_CFLAGS} + PARENT_TARGET softboundcets) + + add_compiler_rt_runtime(clang_rt.softboundcets_benchmark_inlining + STATIC + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets_benchmark_inlining + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_INLINING_CFLAGS} + PARENT_TARGET softboundcets) + + add_compiler_rt_runtime(clang_rt.softboundcets_benchmark_inlining_invalid_locks + STATIC + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets_benchmark_inlining_invalid_locks + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_BENCHMARK_INLINING_INVALID_LOCKS_CFLAGS} + PARENT_TARGET softboundcets) + + add_compiler_rt_runtime(clang_rt.softboundcets + SHARED + ARCHS ${arch} + OBJECT_LIBS RTsoftboundcets_dynamic + RTInterception + RTSanitizerCommon + RTSanitizerCommonLibc + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + CFLAGS ${SOFTBOUNDCETS_DYNAMIC_CFLAGS} + LINK_FLAGS ${SOFTBOUNDCETS_DYNAMIC_LINK_FLAGS} + LINK_LIBS ${SOFTBOUNDCETS_DYNAMIC_LIBS} + ADDITIONAL_HEADERS ${SOFTBOUNDCETS_HEADERS} + PARENT_TARGET softboundcets) + +endforeach() diff --git a/compiler-rt/lib/softboundcets/softboundcets-wrappers.cpp b/compiler-rt/lib/softboundcets/softboundcets-wrappers.cpp new file mode 100644 index 000000000000..a0ffbdb6bd45 --- /dev/null +++ b/compiler-rt/lib/softboundcets/softboundcets-wrappers.cpp @@ -0,0 +1,5134 @@ +//=== softboundcets-wrappers.c- SoftBound wrappers for libraries --*- C -*===// +// Copyright (c) 2011 Santosh Nagarakatte, Milo M. K. Martin. All rights +// reserved. + +// Developed by: Santosh Nagarakatte, Milo M.K. Martin, +// Jianzhou Zhao, Steve Zdancewic +// Department of Computer and Information Sciences, +// University of Pennsylvania +// http://www.cis.upenn.edu/acg/softbound/ + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin, +// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, nor +// the names of its contributors may be used to endorse or promote +// products derived from this Software without specific prior +// written permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. +//===---------------------------------------------------------------------===// + +// #include "safecode/Config/config.h" + +#define _GNU_SOURCE + +#include + +#if defined(__linux__) +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef HAVE_ICONV_H +#include +#endif + +#include +#include +#include + +#include +#include + +#include "sanitizer_common/sanitizer_vector.h" + +#include "softboundcets.h" + +typedef void (*sighandler_t)(int); +typedef void (*void_func_ptr)(void); + +extern size_t *__softboundcets_global_lock; + +/* extern void __softboundcets_process_memory_total(); */ + +__RT_VISIBILITY void +__softboundcets_read_shadow_stack_metadata_store(char **endptr, int arg_num) { + +#if __SOFTBOUNDCETS_SPATIAL + void *nptr_base = __softboundcets_load_base_shadow_stack(arg_num); + void *nptr_bound = __softboundcets_load_bound_shadow_stack(arg_num); + + __softboundcets_metadata_store(endptr, nptr_base, nptr_bound); + +#elif __SOFTBOUNDCETS_TEMPORAL + + size_t nptr_key = __softboundcets_load_key_shadow_stack(arg_num); + size_t *nptr_lock = __softboundcets_load_lock_shadow_stack(arg_num); + + __softboundcets_metadata_store(endptr, nptr_key, nptr_lock); + +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *nptr_base = __softboundcets_load_base_shadow_stack(arg_num); + void *nptr_bound = __softboundcets_load_bound_shadow_stack(arg_num); + size_t nptr_key = __softboundcets_load_key_shadow_stack(arg_num); + size_t *nptr_lock = __softboundcets_load_lock_shadow_stack(arg_num); + __softboundcets_metadata_store(endptr, nptr_base, nptr_bound, nptr_key, + nptr_lock); + +#endif +} + +__RT_VISIBILITY void +__softboundcets_propagate_metadata_shadow_stack_from(int from_argnum, + int to_argnum) { + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + void *base = __softboundcets_load_base_shadow_stack(from_argnum); + void *bound = __softboundcets_load_bound_shadow_stack(from_argnum); + __softboundcets_store_base_shadow_stack(base, to_argnum); + __softboundcets_store_bound_shadow_stack(bound, to_argnum); + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + size_t key = __softboundcets_load_key_shadow_stack(from_argnum); + size_t *lock = __softboundcets_load_lock_shadow_stack(from_argnum); + __softboundcets_store_key_shadow_stack(key, to_argnum); + __softboundcets_store_lock_shadow_stack(lock, to_argnum); + +#endif +} + +__RT_VISIBILITY void __softboundcets_store_null_return_metadata(void) { + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + __softboundcets_store_base_shadow_stack(NULL, 0); + __softboundcets_store_bound_shadow_stack(NULL, 0); + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + __softboundcets_store_key_shadow_stack(0, 0); + __softboundcets_store_lock_shadow_stack(NULL, 0); + +#endif +} + +__RT_VISIBILITY void __softboundcets_store_return_metadata(void *base, + void *bound, + sbcets_key_t key, + sbcets_lock_t lock) { + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + __softboundcets_store_base_shadow_stack(base, 0); + __softboundcets_store_bound_shadow_stack(bound, 0); + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + __softboundcets_store_key_shadow_stack(key, 0); + __softboundcets_store_lock_shadow_stack(lock, 0); + +#endif +} + +/* + * Helper macros for parameter checking (depending on what checks are enabled) + */ + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Loads the base and bound from the shadow stack for the argument at the given position and store +// them at _base and _bounds. +#define LOAD_PTR_BOUNDS(pos, ptr) \ + [[ maybe_unused ]] sbcets_base_t ptr##_base = __softboundcets_load_base_shadow_stack(pos); \ + [[ maybe_unused ]] sbcets_bound_t ptr##_bound = __softboundcets_load_bound_shadow_stack(pos); + +// Since bounds checking every pointer may lead to a significant loss in performance, those checks +// need to be enabled using a macro. +#if __SOFTBOUNDCETS_CHECK_LOADS + +// Check if a pointer dereference of a given size is in bounds and generate an appropriate error +// message if not. +// This requires both base and bounds to be defined (see LOAD_PTR_BOUNDS). +#define CHECK_PTR_BOUNDS_LOAD_ONLY(ptr, size) \ + __softboundcets_spatial_load_dereference_check(ptr##_base, ptr##_bound, (void*)ptr, size); + +// Check if a string function can properly dereference the pointer when it is interpreted as a +// string. +// This requires the base and bound pointers to be defined (See LOAD_PTR_BOUNDS). +#define CHECK_STRING_BOUNDS_LOAD_ONLY(ptr) \ + if (!memchr(ptr, '\0', (char*)ptr##_bound - (char*)ptr)) { \ + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", \ + ptr##_base, ptr##_bound, ptr); \ + __softboundcets_abort(); \ + } + +#else // __SOFTBOUNDCETS_CHECK_LOADS + +// NOOP since loads are not checked. +// Define __SOFTBOUNDCETS_CHECK_LOADS to enable bounds checks on reads. +#define CHECK_PTR_BOUNDS_LOAD_ONLY(ptr, size) + +// NOOP since loads are not checked. +// Define __SOFTBOUNDCETS_CHECK_LOADS to enable bounds checks on reads. +#define CHECK_STRING_BOUNDS_LOAD_ONLY(ptr) + +#endif // __SOFTBOUNDCETS_CHECK_LOADS + +// Check if a pointer dereference is in bounds and raise an appropriate error if not. +// This macro requires base and bound for the pointer to be in scope (see LOAD_PTR_BOUNDS) +#define CHECK_PTR_BOUNDS_STORE_ONLY(ptr, size) \ + __softboundcets_spatial_store_dereference_check(ptr##_base, ptr##_bound, ptr, size); + +// Check if a string function can access this pointer correctly, that is that it contains a null +// terminator within bounds. +// This macro requires the appropriate base and bound to be in scope (See LOAD_PTR_BOUNDS) +#define CHECK_STRING_BOUNDS_STORE_ONLY(ptr) \ + if (!memchr(ptr, '\0', (char*)ptr##_bound - (char*)ptr)) { \ + __softboundcets_error_printf("In string store dereference check: base=%zx, bound=%zx, ptr=%zx", \ + ptr##_base, ptr##_bound, ptr); \ + __softboundcets_abort(); \ + } + +#else // __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Since bounds checking is disabled, this macro introduces dummy variables for a parameter's base +// and bounds pointers. + +// To enable bounds checking, define __SOFTBOUND_SPATIAL=1 of __SOFTBOUND_SPATIAL_TEMPORAL=1. +#define LOAD_PTR_BOUNDS(pos, ptr) \ + [[ maybe_unused ]] void *ptr##_base = nullptr; \ + [[ maybe_unused ]] void *ptr##_bound = nullptr; + +// NOOP since bounds checking and load checking are both disabled. +// To enable bounds checking, define __SOFTBOUND_SPATIAL=1 or __SOFTBOUND_SPATIAL_TEMPORAL=1. +// Define __SOFTBOUNDCETS_CHECK_LOADS to enable load checking. +#define CHECK_PTR_BOUNDS_LOAD_ONLY(ptr, size) + +// NOOP since bounds checking and load checking are both disabled. +// To enable bounds checking, define __SOFTBOUND_SPATIAL=1 or __SOFTBOUND_SPATIAL_TEMPORAL=1. +// Define __SOFTBOUNDCETS_CHECK_LOADS to enable load checking. +#define CHECK_STRING_BOUNDS_LOAD_ONLY(ptr) + +// NOOP since bounds checking is disabled. +// To enable bounds checking, define __SOFTBOUND_SPATIAL=1 or __SOFTBOUND_SPATIAL_TEMPORAL=1. +#define CHECK_PTR_BOUNDS_STORE_ONLY(ptr, size) + +// NOOP since bounds checking is disabled. +// To enable bounds checking, define __SOFTBOUND_SPATIAL=1 or __SOFTBOUND_SPATIAL_TEMPORAL=1. +#define CHECK_STRING_BOUNDS_STORE_ONLY(ptr) + +#endif // __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Introduce base and bounds for the given argument and check for a read access. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_LOAD_ONLY. +#define CHECK_PTR_BOUNDS_LOAD(pos, ptr, size) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + CHECK_PTR_BOUNDS_LOAD_ONLY(ptr, size); + +// Introduce base and bound for the given argument. +// If the pointer is not null, also check if it can be dereferenced. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_LOAD_ONLY. +#define CHECK_PTR_BOUNDS_LOAD_NULLABLE(pos, ptr, size) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_PTR_BOUNDS_LOAD_ONLY(ptr, size); \ + } + +// Introduce base and bound for the given string argument and check if it can be safely passed to +// a string function. +// This combines the LOAD_PTR_BOUNDS and CHECK_STRING_BOUNDS_LOAD_ONLY. +#define CHECK_STRING_BOUNDS_LOAD(pos, ptr) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + CHECK_STRING_BOUNDS_LOAD_ONLY(ptr); + +// Introduce base and bound for the given string argument and check if it can be safely passed to +// a string function, if it is not null. +// This combines the LOAD_PTR_BOUNDS and CHECK_STRING_BOUNDS_LOAD_ONLY. +#define CHECK_STRING_BOUNDS_LOAD_NULLABLE(pos, ptr) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_STRING_BOUNDS_LOAD_ONLY(ptr); \ + } + +// Introduce the base and bound for a given argument and check if it can be dereferenced. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_STORE_ONLY macros. +#define CHECK_PTR_BOUNDS_STORE(pos, ptr, size) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + CHECK_PTR_BOUNDS_STORE_ONLY(ptr, size); + +// Introduce the base and bound for a given argument. +// It the argument is not null, also check if it can be dereferenced. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_STORE_ONLY macros. +#define CHECK_PTR_BOUNDS_STORE_NULLABLE(pos, ptr, size) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_PTR_BOUNDS_STORE_ONLY(ptr, size); \ + } + +// Introduce the base and bound for a given argument and check if it can be dereferenced. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_STORE_ONLY macros. +#define CHECK_STRING_BOUNDS_STORE(pos, ptr) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + CHECK_STRING_BOUNDS_STORE_ONLY(ptr); + +// Introduce the base and bound for a given argument. +// It the argument is not null, also check if it can be dereferenced. +// This combines the LOAD_PTR_BOUNDS and CHECK_PTR_BOUNDS_STORE_ONLY macros. +#define CHECK_STRING_BOUNDS_STORE_NULLABLE(pos, ptr, size) \ + LOAD_PTR_BOUNDS(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_STRING_BOUNDS_STORE_ONLY(ptr, size); \ + } + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Loads the lock and key for the given parameter from the shadow stack into _lock and +// _key. +#define LOAD_PTR_LOCK(pos, ptr) \ + [[ maybe_unused ]] sbcets_lock_t ptr##_lock = __softboundcets_load_lock_shadow_stack(pos); \ + [[ maybe_unused ]] sbcets_key_t ptr##_key = __softboundcets_load_key_shadow_stack(pos); + +#if __SOFTBOUNDCETS_CHECK_LOADS + +// Check if a pointer is still alive when it is dereferenced. +// The lock and key for the pointer must be defined (see LOAD_PTR_LOCK). +#define CHECK_PTR_ALIVE_LOAD_ONLY(ptr) \ + __softboundcets_temporal_load_dereference_check(ptr##_lock, ptr##_key); + +#else // __SOFTBOUNDCETS_CHECK_LOADS + +// NOOP, since load checking is disabled. +// define __SOFTBOUNDCETS_CHECK_LOADS=1 to enable. +#define CHECK_PTR_ALIVE_LOAD_ONLY(ptr) + +#endif // __SOFTBOUNDCETS_CHECK_LOADS + +// Check if a pointer is still alive when dereferenced for writing. +// This macro requires that the lock and key values for the pointer are in scope +// (see LOAD_PTR_LOCK). +#define CHECK_PTR_ALIVE_STORE_ONLY(ptr) \ + __softboundcets_temporal_store_dereference_check(ptr##_lock, ptr##_key); + +#else // __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Define dummy values for the pointers lock and key as temporal checking is disabled. +// To enable, define __SOFTBOUNDCETS_TEMPORAL=1 or __SOFTBOUNDCETS_SPATIAL_TEMPORAL=1 +#define LOAD_PTR_LOCK(pos, ptr) \ + [[ maybe_unused ]] sbcets_lock_t ptr##_lock = nullptr; \ + [[ maybe_unused ]] sbcets_key_t ptr##_key = 0; + +// NOOP as temporal checking is disabled. +// To enable, define __SOFTBOUNDCETS_CHECK_LOADS=1 and __SOFTBOUNDCETS_TEMPORAL=1 or +// __SOFTBOUNDCETS_SPATIAL_TEMPORAL=1 +#define CHECK_PTR_ALIVE_LOAD_ONLY(ptr) + +// NOOP as temporal checking is disabled. +// To enable, define __SOFTBOUNDCETS_TEMPORAL=1 or __SOFTBOUNDCETS_SPATIAL_TEMPORAL=1 +#define CHECK_PTR_ALIVE_STORE_ONLY(ptr) + +#endif // __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +// Introduce the lock and key for an argument and check that it is alive. +// This macro combines the LOAD_PTR_LOCK and CHECK_PTR_ACTIVE_LOAD_ONLY macros. +#define CHECK_PTR_ALIVE_LOAD(pos, ptr) \ + LOAD_PTR_LOCK(pos, ptr); \ + CHECK_PTR_ALIVE_LOAD_ONLY(ptr); + +// Introduce the lock and key for an argument and check that it is alive if it is not null. +// This macro combines the LOAD_PTR_LOCK and CHECK_PTR_ACTIVE_LOAD_ONLY macros. +#define CHECK_PTR_ALIVE_LOAD_NULLABLE(pos, ptr) \ + LOAD_PTR_LOCK(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_PTR_ALIVE_LOAD_ONLY(ptr); \ + } + +// Introduce the lock and key for an argument and check that it is alive. +// This macro combines the LOAD_PTR_LOCK and CHECK_PTR_ACTIVE_STORe_ONLY macros. +#define CHECK_PTR_ALIVE_STORE(pos, ptr) \ + LOAD_PTR_LOCK(pos, ptr); \ + CHECK_PTR_ALIVE_STORE_ONLY(ptr); + +// Introduce the lock and key for an argument and check that it is alive if it is not null. +// This macro combines the LOAD_PTR_LOCK and CHECK_PTR_ACTIVE_STORE_ONLY macros. +#define CHECK_PTR_ALIVE_STORE_NULLABLE(pos, ptr) \ + LOAD_PTR_LOCK(pos, ptr); \ + if (ptr != nullptr) { \ + CHECK_PTR_ALIVE_STORE_ONLY(ptr); \ + } + +// Check that a pointer read is both alive and in bounds. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_PTR_BOUNDS_LOAD and CHECK_PTR_ALIVE_LOAD macros. +#define CHECK_PTR_LOAD(pos, ptr, size) \ + CHECK_PTR_BOUNDS_LOAD(pos, ptr, size); \ + CHECK_PTR_ALIVE_LOAD(pos, ptr); + +// Check that a pointer read is both alive and in bounds if it is not null. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_PTR_BOUNDS_LOAD_NULLABLE and CHECK_PTR_ALIVE_LOAD_NULLABLE macros. +#define CHECK_PTR_LOAD_NULLABLE(pos, ptr, size) \ + CHECK_PTR_BOUNDS_LOAD_NULLABLE(pos, ptr, size); \ + CHECK_PTR_ALIVE_LOAD_NULLABLE(pos, ptr); + +// Check that a pointer write is both alive and in bounds. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_PTR_BOUNDS_STORE and CHECK_PTR_ALIVE_STORE macros. +#define CHECK_PTR_STORE(pos, ptr, size) \ + CHECK_PTR_BOUNDS_STORE(pos, ptr, size); \ + CHECK_PTR_ALIVE_STORE(pos, ptr); + +// Check that a pointer read is both alive and in bounds if it is not null. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_PTR_BOUNDS_STORE_NULLABLE and CHECK_PTR_ALIVE_STORE_NULLABLE macros. +#define CHECK_PTR_STORE_NULLABLE(pos, ptr, size) \ + CHECK_PTR_BOUNDS_STORE_NULLABLE(pos, ptr, size); \ + CHECK_PTR_ALIVE_STORE_NULLABLE(pos, ptr); + +// Check that passing an argument to a function expecting a null terminated string is safe. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_STRING_BOUNDS_LOAD and CHECK_PTR_ALIVE_LOAD macros. +#define CHECK_STRING_LOAD(pos, ptr) \ + CHECK_STRING_BOUNDS_LOAD(pos, ptr); \ + CHECK_PTR_ALIVE_LOAD(pos, ptr); + +// Check that passing an argument to a function expecting a null terminated string is safe, if it +// is not null. +// It will still introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_STRING_BOUNDS_LOAD_NULLABLE and CHECK_PTR_ALIVE_LOAD_NULLABLE macros. +#define CHECK_STRING_LOAD_NULLABLE(pos, ptr) \ + CHECK_STRING_BOUNDS_LOAD_NULLABLE(pos, ptr); \ + CHECK_PTR_ALIVE_LOAD_NULLABLE(pos, ptr); + +// Check that passing an argument to a function expecting a null terminated string is safe. +// It will also introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_STRING_BOUNDS_STORE and CHECK_PTR_ALIVE_STORE macros. +#define CHECK_STRING_STORE(pos, ptr) \ + CHECK_STRING_BOUNDS_STORE(pos, ptr); \ + CHECK_PTR_ALIVE_STORE(pos, ptr); + +// Check that passing an argument to a function expecting a null terminated string is safe, if it +// is not null. +// It will still introduce variables for its base, bound, key and lock in the local scope. +// This combines the CHECK_STRING_BOUNDS_STORE_NULLABLE and CHECK_PTR_ALIVE_STORE_NULLABLE macros. +#define CHECK_STRING_STORE_NULLABLE(pos, ptr) \ + CHECK_STRING_BOUNDS_STORE_NULLABLE(pos, ptr); \ + CHECK_PTR_ALIVE_STORE_NULLABLE(pos, ptr); + +/* wrappers for library calls (incomplete) */ +//////////////////////system wrappers ////////////////////// + +__RT_VISIBILITY int softboundcets_setenv(const char *name, const char *value, + int overwrite) { + + return setenv(name, value, overwrite); +} + +__RT_VISIBILITY +int softboundcets_unsetenv(const char *name) { return unsetenv(name); } + +__RT_VISIBILITY int softboundcets_system(char *ptr) { return system(ptr); } + +__RT_VISIBILITY int softboundcets_setreuid(uid_t ruid, uid_t euid) { + + /* tested */ + return setreuid(ruid, euid); +} + +__RT_VISIBILITY int softboundcets_mkstemp(char *templ) { + + /* tested */ + return mkstemp(templ); +} + +__RT_VISIBILITY int softboundcets_getrlimit(int resource, struct rlimit *rlim) { + + /* tested */ + return getrlimit(resource, rlim); +} + +__RT_VISIBILITY int softboundcets_setrlimit(int resource, + const struct rlimit *rlim) { + + /* tested */ + return setrlimit(resource, rlim); +} + +__RT_VISIBILITY size_t softboundcets_fread_unlocked(void *ptr, size_t size, + size_t n, FILE *stream) { + + return fread_unlocked(ptr, size, n, stream); +} + +#if 0 +__RT_VISIBILITY int +softboundcets_fputs_unlocked(const char *s, FILE *stream){ + return fputs_unlocked(s, stream); +} +#endif + +__RT_VISIBILITY size_t softboundcets_fread(void *ptr, size_t size, size_t nmemb, + FILE *stream) { + /* tested */ + return fread(ptr, size, nmemb, stream); +} + +__RT_VISIBILITY int softboundcets_mkdir(const char *pathname, mode_t mode) { + + /* tested */ + return mkdir(pathname, mode); +} + +__RT_VISIBILITY int softboundcets_chroot(const char *path) { + /* tested */ + return chroot(path); +} + +__RT_VISIBILITY int softboundcets_rmdir(const char *pathname) { + + /* tested */ + return rmdir(pathname); +} + +__RT_VISIBILITY int softboundcets_stat(const char *path, struct stat *buf) { + /* tested */ + return stat(path, buf); +} + +__RT_VISIBILITY int softboundcets_fputc(int c, FILE *stream) { + + /* tested */ + return fputc(c, stream); +} + +__RT_VISIBILITY int softboundcets_fileno(FILE *stream) { + + return fileno(stream); +} + +__RT_VISIBILITY int softboundcets_fgetc(FILE *stream) { return fgetc(stream); } + +__RT_VISIBILITY int softboundcets_ungetc(int c, FILE *stream) { + + return ungetc(c, stream); +} + +__RT_VISIBILITY int softboundcets_strncmp(const char *s1, const char *s2, + size_t n) { + return strncmp(s1, s2, n); +} + +__RT_VISIBILITY long long softboundcets_fwrite(char *ptr, size_t size, + size_t nmemb, FILE *stream) { + return fwrite(ptr, size, nmemb, stream); +} + +__RT_VISIBILITY double softboundcets_atof(char *ptr) { return atof(ptr); } + +__RT_VISIBILITY int softboundcets_feof(FILE *stream) { return feof(stream); } + +__RT_VISIBILITY int softboundcets_remove(const char *pathname) { + + return remove(pathname); +} + +////////////////File Library Wrappers here ////////////////////// + +__RT_VISIBILITY FILE *softboundcets_tmpfile(void) { + + FILE *ret_ptr = tmpfile(); + void *ret_ptr_bound = (char *)ret_ptr + sizeof(FILE); + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_ferror(FILE *stream) { + + return ferror(stream); +} + +__RT_VISIBILITY long softboundcets_ftell(FILE *stream) { return ftell(stream); } + +__RT_VISIBILITY int softboundcets_fstat(int filedes, struct stat *buff) { + + return fstat(filedes, buff); +} + +// __RT_VISIBILITY int softboundcets___lxstat(int __ver, const char *__filename, +// struct stat *__stat_buf) { + +// return __lxstat(__ver, __filename, __stat_buf); +// } + +__RT_VISIBILITY size_t softboundcets_mbrtowc(wchar_t *pwc, const char *s, + size_t n, mbstate_t *ps) { + return mbrtowc(pwc, s, n, ps); +} + +__RT_VISIBILITY int softboundcets_mbsinit(const mbstate_t *ps) { + return mbsinit(ps); +} + +// __RT_VISIBILITY int softboundcets___fxstat(int ver, int file_des, +// struct stat *stat_struct) { +// return __fxstat(ver, file_des, stat_struct); +// } + +// __RT_VISIBILITY int softboundcets___fxstatat(int ver, int file_des, +// const char *filename, +// struct stat *stat_struct, +// int flag) { +// return __fxstatat(ver, file_des, filename, stat_struct, flag); +// } + +__RT_VISIBILITY int softboundcets_fflush(FILE *stream) { + + return fflush(stream); +} + +__RT_VISIBILITY int softboundcets_fputs(const char *s, FILE *stream) { + + return fputs(s, stream); +} + +__RT_VISIBILITY DIR *softboundcets_fdopendir(int fd) { + + void *ret_ptr = (void *)fdopendir(fd); + void *ret_ptr_bound = (char *)ret_ptr + 1024 * 1024; + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + return (DIR *)ret_ptr; +} + +__RT_VISIBILITY int softboundcets_fseeko(FILE *stream, off_t offset, + int whence) { + + return fseeko(stream, offset, whence); +} + +__RT_VISIBILITY char *softboundcets_mkdtemp(char *templ) { + + char *ret_ptr = mkdtemp(templ); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_linkat(int olddirfd, const char *oldpath, + int newdirfd, const char *newpath, + int flags) { + + return linkat(olddirfd, oldpath, newdirfd, newpath, flags); +} + +__RT_VISIBILITY int softboundcets_utimes(const char *filename, + const struct timeval times[2]) { + + return utimes(filename, times); +} + +#if 0 +__RT_VISIBILITY int softboundcets_futimesat(int dirfd, const char *pathname, + const struct timeval times[2]){ + + return futimesat(dirfd, pathname, times); +} +#endif + +__RT_VISIBILITY int softboundcets_futimens(int fd, + const struct timespec times[2]) { + + return futimens(fd, times); +} + +__RT_VISIBILITY int softboundcets_utimensat(int dirfd, const char *pathname, + const struct timespec times[2], + int flags) { + + return utimensat(dirfd, pathname, times, flags); +} + +__RT_VISIBILITY int softboundcets_iswprint(wint_t wc) { return iswprint(wc); } + +__RT_VISIBILITY int softboundcets_getpagesize(void) { return getpagesize(); } + +__RT_VISIBILITY int softboundcets_dirfd(DIR *dirp) { return dirfd(dirp); } + +__RT_VISIBILITY struct lconv *softboundcets_localeconv(void) { + struct lconv *temp = localeconv(); + + __softboundcets_store_return_metadata(temp, temp + 1024, 1, + __softboundcets_global_lock); + + return temp; +} + +__RT_VISIBILITY struct tm *softboundcets_gmtime(const time_t *timep) { + + struct tm *temp = gmtime(timep); + + __softboundcets_store_return_metadata(temp, temp + 1024, 1, + __softboundcets_global_lock); + + return temp; +} + +__RT_VISIBILITY void * +softboundcets_bsearch(const void *key, const void *base, size_t nmemb, + size_t size, int (*compar)(const void *, const void *)) { + + void *ret_ptr = bsearch(key, base, nmemb, size, compar); + + __softboundcets_propagate_metadata_shadow_stack_from(2, 0); + return ret_ptr; +} + +__RT_VISIBILITY +struct group *softboundcets_getgrnam(const char *name) { + struct group *ret_ptr = getgrnam(name); + __softboundcets_store_return_metadata(ret_ptr, (char *)ret_ptr + 1024 * 1024, + 1, __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY +int softboundcets_rpmatch(const char *response) { return rpmatch(response); } + +__RT_VISIBILITY +int softboundcets_regcomp(regex_t *preg, const char *regex, int cflags) { + return regcomp(preg, regex, cflags); +} + +__RT_VISIBILITY +size_t softboundcets_regerror(int errcode, const regex_t *preg, char *errbuf, + size_t errbuf_size) { + + return regerror(errcode, preg, errbuf, errbuf_size); +} + +__RT_VISIBILITY +int softboundcets_regexec(const regex_t *preg, const char *string, + size_t nmatch, regmatch_t pmatch[], int eflags) { + return regexec(preg, string, nmatch, pmatch, eflags); +} + +#ifdef HAVE_ICONV_H +__RT_VISIBILITY +size_t softboundcets_iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) { + + return iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft); +} + +__RT_VISIBILITY +iconv_t softboundcets_iconv_open(const char *tocode, const char *fromcode) { + + return iconv_open(tocode, fromcode); +} +#endif + +__RT_VISIBILITY +struct passwd *softboundcets_getpwnam(const char *name) { + struct passwd *ret_ptr = getpwnam(name); + __softboundcets_store_return_metadata(ret_ptr, (char *)ret_ptr + 1024 * 1024, + 1, __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY struct passwd *softboundcets_getpwuid(uid_t uid) { + struct passwd *ret_ptr = getpwuid(uid); + + __softboundcets_store_return_metadata(ret_ptr, (char *)ret_ptr + 1024 * 1024, + 1, __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY struct group *softboundcets_getgrgid(gid_t gid) { + + struct group *ret_ptr = getgrgid(gid); + __softboundcets_store_return_metadata(ret_ptr, (char *)ret_ptr + 1024 * 1024, + 1, __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY FILE *softboundcets_fopen(const char *path, const char *mode) { + + FILE *ret_ptr = fopen(path, mode); + void *ret_ptr_bound = (char *)ret_ptr + sizeof(FILE); + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY FILE *softboundcets_fdopen(int fildes, const char *mode) { + + void *ret_ptr = (void *)fdopen(fildes, mode); + void *ret_ptr_bound = (char *)ret_ptr + sizeof(FILE); + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + return (FILE *)ret_ptr; +} + +__RT_VISIBILITY int softboundcets_fseek(FILE *stream, long offset, int whence) { + + return fseek(stream, offset, whence); +} + +__RT_VISIBILITY int softboundcets_ftruncate(int fd, off_t length) { + return ftruncate(fd, length); +} + +__RT_VISIBILITY FILE *softboundcets_popen(const char *command, + const char *type) { + + FILE *ret_ptr = popen(command, type); + void *ret_ptr_bound = (char *)ret_ptr + sizeof(FILE); + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_fclose(FILE *fp) { return fclose(fp); } + +__RT_VISIBILITY int softboundcets_pclose(FILE *stream) { + + return pclose(stream); +} + +__RT_VISIBILITY void softboundcets_rewind(FILE *stream) { rewind(stream); } + +__RT_VISIBILITY struct dirent *softboundcets_readdir(DIR *dir) { + + struct dirent *ret_ptr = readdir(dir); + void *ret_ptr_bound = (char *)ret_ptr + sizeof(struct dirent); + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_creat(const char *pathname, mode_t mode) { + + return creat(pathname, mode); +} + +__RT_VISIBILITY int softboundcets_fnmatch(const char *pattern, + const char *string, int flags) { + + return fnmatch(pattern, string, flags); +} + +__RT_VISIBILITY DIR *softboundcets_opendir(const char *name) { + + DIR *ret_ptr = opendir(name); + + /* FIX Required, don't know the sizeof(DIR) */ + void *ret_ptr_bound = (char *)ret_ptr + 1024 * 1024; + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr_bound, 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_closedir(DIR *dir) { return closedir(dir); } + +__RT_VISIBILITY int softboundcets_rename(const char *old_path, + const char *new_path) { + + return rename(old_path, new_path); +} + +////////////////////unistd.h wrappers //////////////////////////////// + +__RT_VISIBILITY char *softboundcets_getcwd(char *buf, size_t size) { + + if (buf == NULL) { + printf("This case not handled, requesting memory from system\n"); + __softboundcets_abort(); + } + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + char *base = (char *)__softboundcets_load_base_shadow_stack(1); + char *bound = (char *)__softboundcets_load_bound_shadow_stack(1); + + if (buf < base || buf + size > bound) { + __softboundcets_error_printf("[getcwd], overflow in buf in getcwd\n"); + __softboundcets_abort(); + } + +#endif + + char *ret_ptr = getcwd(buf, size); + + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return ret_ptr; +} + +__RT_VISIBILITY +int softboundcets_readlinkat(int dirfd, const char *pathname, char *buf, + size_t bufsiz) { + return readlinkat(dirfd, pathname, buf, bufsiz); +} + +__RT_VISIBILITY +int softboundcets_renameat(int olddirfd, const char *oldpath, int newdirfd, + const char *newpath) { + return renameat(olddirfd, oldpath, newdirfd, newpath); +} + +__RT_VISIBILITY +int softboundcets_unlinkat(int dirfd, const char *pathname, int flags) { + return unlinkat(dirfd, pathname, flags); +} + +__RT_VISIBILITY +int softboundcets_symlinkat(const char *oldpath, int newdirfd, + const char *newpath) { + + return symlinkat(oldpath, newdirfd, newpath); +} + +__RT_VISIBILITY +int softboundcets_mkdirat(int dirfd, const char *pathname, mode_t mode) { + + return mkdirat(dirfd, pathname, mode); +} + +__RT_VISIBILITY +int softboundcets_fchownat(int dirfd, const char *pathname, uid_t owner, + gid_t group, int flags) { + + return fchownat(dirfd, pathname, owner, group, flags); +} + +__RT_VISIBILITY +int softboundcets_chmod(const char *path, mode_t mode) { + return chmod(path, mode); +} + +__RT_VISIBILITY +int softboundcets_openat(int dirfd, const char *pathname, int flags) { + return openat(dirfd, pathname, flags); +} + +__RT_VISIBILITY +int softboundcets_fchmodat(int dirfd, const char *pathname, mode_t mode, + int flags) { + return fchmodat(dirfd, pathname, mode, flags); +} + +#if defined(__linux__) + +// __RT_VISIBILITY +// int softboundcets___xmknodat(int __ver, int __fd, const char *__path, +// __mode_t __mode, __dev_t *__dev) { +// return __xmknodat(__ver, __fd, __path, __mode, __dev); +// } + +__RT_VISIBILITY +int softboundcets_mkfifoat(int dirfd, const char *pathname, mode_t mode) { + return mkfifoat(dirfd, pathname, mode); +} + +#endif + +#if 0 +__RT_VISIBILITY +int softboundcets_openat(int dirfd, const char *pathname, int flags, mode_t mode){ + return opennat(dirfd, pathname, flags, mode); +} +#endif + +__RT_VISIBILITY int softboundcets_chown(const char *path, uid_t owner, + gid_t group) { + return chown(path, owner, group); +} + +__RT_VISIBILITY int softboundcets_chdir(const char *path) { + return chdir(path); +} + +///////////////////String related wrappers //////////////////////////// + +__RT_VISIBILITY int softboundcets_strcmp(const char *s1, const char *s2) { + + return strcmp(s1, s2); +} + +__RT_VISIBILITY int softboundcets_strcasecmp(const char *s1, const char *s2) { + + return strcasecmp(s1, s2); +} + +__RT_VISIBILITY int softboundcets_strncasecmp(const char *s1, const char *s2, + size_t n) { + return strncasecmp(s1, s2, n); +} + +__RT_VISIBILITY size_t softboundcets_strlen(const char *s) { return strlen(s); } + +__RT_VISIBILITY const char *softboundcets_strpbrk(const char *s, + const char *accept) { + + const char *ret_ptr = strpbrk(s, accept); + if (ret_ptr != NULL) { + + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + + __softboundcets_store_null_return_metadata(); + } + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_gets(char *s) { + + printf("[SBCETS] gets used and should not be used\n"); + __softboundcets_abort(); +#if 0 + printf("[Softboundcets][Warning] Should not use gets\n"); + char* ret_ptr = gets(s); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +#endif + return NULL; +} + +__RT_VISIBILITY char *softboundcets_fgets(char *s, int size, FILE *stream) { + + char *ret_ptr = fgets(s, size, stream); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return ret_ptr; +} + +__RT_VISIBILITY void softboundcets_perror(const char *s) { perror(s); } + +__RT_VISIBILITY size_t softboundcets_strspn(const char *s, const char *accept) { + + return strspn(s, accept); +} + +__RT_VISIBILITY size_t softboundcets_strcspn(const char *s, + const char *reject) { + + return strcspn(s, reject); +} + +#ifdef _GNU_SOURCE + +__RT_VISIBILITY void *softboundcets_mempcpy(void *dest, const void *src, + size_t n) { + + // IMP: need to copy the metadata + void *ret_ptr = mempcpy(dest, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +#endif + +__RT_VISIBILITY int softboundcets_memcmp(const void *s1, const void *s2, + size_t n) { + return memcmp(s1, s2, n); +} + +#ifdef _GNU_SOURCE + +__RT_VISIBILITY const void *softboundcets_memrchr(const void *s, int c, + size_t n) { + const void *ret_ptr = memrchr(s, c, n); + if (ret_ptr != NULL) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return ret_ptr; +} +#endif + +__RT_VISIBILITY void softboundcets_rewinddir(DIR *dirp) { rewinddir(dirp); } + +__RT_VISIBILITY const void *softboundcets_memchr(const void *s, int c, + size_t n) { + const void *ret_ptr = memchr(s, c, n); + if (ret_ptr != NULL) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_rindex(char *s, int c) { + + char *ret_ptr = rindex(s, c); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY ssize_t softboundcets___getdelim(char **lineptr, size_t *n, + int delim, FILE *stream) { + + int metadata_prop = 1; + if (*lineptr == NULL) { + metadata_prop = 0; + } + + ssize_t ret_val = getdelim(lineptr, n, delim, stream); + + /* TODO fix storing of metadata for lineptr*/ + if (metadata_prop) { + __softboundcets_read_shadow_stack_metadata_store(lineptr, 0); + } else { + // no need to store return metadata as no pointer is returned + __softboundcets_store_return_metadata(*lineptr, + (*lineptr) + strlen(*lineptr), 1, + __softboundcets_global_lock); + } + + return ret_val; +} + +__RT_VISIBILITY unsigned long int +softboundcets_strtoul(const char *nptr, char **endptr, int base) { + + unsigned long temp = strtoul(nptr, endptr, base); + if (endptr != NULL) { + __softboundcets_read_shadow_stack_metadata_store(endptr, 0); + } + + return temp; +} + +__RT_VISIBILITY double softboundcets_strtod(const char *nptr, char **endptr) { + + double temp = strtod(nptr, endptr); + + if (endptr != NULL) { + __softboundcets_read_shadow_stack_metadata_store(endptr, 0); + } + return temp; +} + +__RT_VISIBILITY long softboundcets_strtol(const char *nptr, char **endptr, + int base) { + + long temp = strtol(nptr, endptr, base); + if (endptr != NULL) { + // __softboundcets_printf("*endptr=%p\n", *endptr); + __softboundcets_read_shadow_stack_metadata_store(endptr, 0); + } + return temp; +} + +#ifdef _GNU_SOURCE + +__RT_VISIBILITY const char *softboundcets_strchrnul(const char *s, int c) { + + const char *ret_ptr = strchrnul(s, c); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} +#endif + +__RT_VISIBILITY const char *softboundcets_strchr(const char *s, int c) { + + const char *ret_ptr = strchr(s, c); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY const char *softboundcets_strrchr(const char *s, int c) { + + const char *ret_ptr = strrchr(s, c); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_stpcpy(char *dest, char *src) { + + char *ret_ptr = stpcpy(dest, src); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_strcpy(char *dest, char *src) { + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + char *dest_base = (char *)__softboundcets_load_base_shadow_stack(1); + char *dest_bound = (char *)__softboundcets_load_bound_shadow_stack(1); + + char *src_base = (char *)__softboundcets_load_base_shadow_stack(2); + char *src_bound = (char *)__softboundcets_load_bound_shadow_stack(2); + + /* There will be an out-of-bound read before we trigger an error as + we currently use strlen. Can either (dest + size) or (src + size) + overflow? + */ +#ifndef __NOSIM_CHECKS + size_t size = strlen(src); + if (dest < dest_base || (dest > dest_bound - size - 1) || + (size > (size_t)dest_bound)) { + printf("[strcpy] overflow in strcpy with dest\n"); + __softboundcets_abort(); + } + if (src < src_base || (src > src_bound - size - 1) || + (size > (size_t)src_bound)) { + printf("[strcpy] overflow in strcpy with src\n"); + __softboundcets_abort(); + } +#endif +#endif + + char *ret_ptr = strcpy(dest, src); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +/////////////////* TODO */////////////////////////// + +__RT_VISIBILITY int softboundcets_atoi(const char *ptr) { + + if (ptr == NULL) { + __softboundcets_abort(); + } + return atoi(ptr); +} + +__RT_VISIBILITY int softboundcets_puts(char *ptr) { return puts(ptr); } + +__RT_VISIBILITY char *softboundcets_strtok(char *str, const char *delim) { + + char *ret_ptr = strtok(str, delim); + __softboundcets_store_return_metadata((void *)0, (void *)(281474976710656), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY void __softboundcets_strdup_handler(void *ret_ptr) { + sbcets_key_t ptr_key; + sbcets_lock_t ptr_lock; + + if (ret_ptr == NULL) { + __softboundcets_store_null_return_metadata(); + } else { + // printf("strndup malloced pointer %p\n", ret_ptr); + __softboundcets_memory_allocation(ret_ptr, &ptr_lock, &ptr_key); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen((char *)ret_ptr) + 1), + ptr_key, ptr_lock); + } +} + +// strdup, allocates memory from the system using malloc, thus can be freed +__RT_VISIBILITY char *softboundcets_strndup(const char *s, size_t n) { + + /* IMP: strndup just copies the string s */ + char *ret_ptr = strndup(s, n); + __softboundcets_strdup_handler(ret_ptr); + return ret_ptr; +} + +// strdup, allocates memory from the system using malloc, thus can be freed +__RT_VISIBILITY char *softboundcets_strdup(const char *s) { + + /* IMP: strdup just copies the string s */ + char *ret_ptr = strdup(s); + + __softboundcets_strdup_handler(ret_ptr); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets___strdup(const char *s) { + + char *ret_ptr = strdup(s); + __softboundcets_strdup_handler(ret_ptr); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_strcat(char *dest, char *src) { + CHECK_STRING_STORE(1, dest); + CHECK_STRING_LOAD(2, src); + + // + 1 for the null terminator + // Note: strlen(src) may overrun if we don't define __SOFTBOUNDCETS_CHECK_LOADS + if (dest + strlen(dest) + strlen(src) + 1 > dest_bound) { + printf("overflow with strcat, dest = %p, strlen(dest)=%d, " + "strlen(src)=%d, dest_bound=%p \n", + dest, strlen(dest), strlen(src), dest_bound); + __softboundcets_abort(); + } + + char *ret_ptr = strcat(dest, src); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_strncat(char *dest, char *src, + size_t n) { + CHECK_STRING_STORE(1, dest); + + size_t src_len = strlen(src); + size_t min_n_src_len = (n < src_len) ? n : src_len; + + CHECK_PTR_LOAD(2, src, min_n_src_len); + + + if (dest + strlen(dest) + min_n_src_len + 1 > dest_bound) { + printf("overflow with strncat, dest = %p, strlen(dest)=%d, " + "n=%d, dest_bound=%p \n", + dest, strlen(dest), n, dest_bound); + __softboundcets_abort(); + } + + char *ret_ptr = strncat(dest, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_strncpy(char *dest, char *src, size_t n) { + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + char *dest_base = (char *)__softboundcets_load_base_shadow_stack(1); + char *dest_bound = (char *)__softboundcets_load_bound_shadow_stack(1); + + char *src_base = (char *)__softboundcets_load_base_shadow_stack(2); + char *src_bound = (char *)__softboundcets_load_bound_shadow_stack(2); + + /* Can either (dest + n) or (src + n) overflow? */ + if (dest < dest_base || (dest + n > dest_bound)) { + printf("[strncpy] overflow in strncpy with dest\n"); + __softboundcets_abort(); + } + if (src < src_base || (src > src_bound - n)) { + __softboundcets_abort(); + } +#endif + + char *ret_ptr = strncpy(dest, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return ret_ptr; +} + +__RT_VISIBILITY const char *softboundcets_strstr(const char *haystack, + const char *needle) { + + const char *ret_ptr = strstr(haystack, needle); + if (ret_ptr != NULL) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return ret_ptr; +} + +__RT_VISIBILITY sighandler_t softboundcets_signal(int signum, + sighandler_t handler) { + + sighandler_t ptr = signal(signum, handler); + __softboundcets_store_return_metadata((void *)ptr, (void *)ptr, 1, + __softboundcets_global_lock); + return ptr; +} + +__RT_VISIBILITY long softboundcets_atol(const char *nptr) { return atol(nptr); } + +__RT_VISIBILITY void *softboundcets_realloc(void *ptr, size_t size) { + + __softboundcets_debug_printf("performing relloc, which can cause ptr=%p\n", + ptr); + void *ret_ptr = realloc(ptr, size); + __softboundcets_allocation_secondary_trie_allocate(ret_ptr); + size_t ptr_key = 1; + size_t *ptr_lock = __softboundcets_global_lock; + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + // if the original pointer is null, we must allocate a new lock + if (ptr == NULL) { + __softboundcets_memory_allocation(ret_ptr, &ptr_lock, &ptr_key); + } else { + ptr_key = __softboundcets_load_key_shadow_stack(1); + ptr_lock = __softboundcets_load_lock_shadow_stack(1); + } +#endif + + __softboundcets_store_return_metadata(ret_ptr, (char *)(ret_ptr) + size, + ptr_key, ptr_lock); + if (ret_ptr != ptr) { + __softboundcets_check_remove_from_free_map(ptr_key, ptr); + __softboundcets_add_to_free_map(ptr_key, ret_ptr); + __softboundcets_copy_metadata(ret_ptr, ptr, size); + } + + return ret_ptr; +} + +__RT_VISIBILITY void *softboundcets_calloc(size_t nmemb, size_t size) { + + sbcets_key_t ptr_key = 1; + sbcets_lock_t ptr_lock = NULL; + + void *ret_ptr = calloc(nmemb, size); + if (ret_ptr != NULL) { + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_memory_allocation(ret_ptr, &ptr_lock, &ptr_key); +#endif + + __softboundcets_store_return_metadata( + ret_ptr, ((char *)(ret_ptr) + (nmemb * size)), ptr_key, ptr_lock); + + if (__SOFTBOUNDCETS_FREE_MAP) { +#if 0 + __softboundcets_debug_printf("calloc ptr=%p, ptr_key=%zx\n", + ret_ptr, ptr_key); +#endif + // __softboundcets_add_to_free_map(ptr_key, ret_ptr); + } + } else { + __softboundcets_store_null_return_metadata(); + } + return ret_ptr; +} + +__RT_VISIBILITY void *softboundcets_mmap(void *addr, size_t length, int prot, + int flags, int fd, off_t offset) { + + sbcets_key_t ptr_key = 1; + sbcets_lock_t ptr_lock = __softboundcets_global_lock; + char *ret_ptr = (char *)mmap(addr, length, prot, flags, fd, offset); + if (ret_ptr == (void *)-1) { + __softboundcets_store_null_return_metadata(); + } else { + + char *ret_bound = ret_ptr + length; + __softboundcets_store_return_metadata(ret_ptr, ret_bound, ptr_key, + ptr_lock); + } + return ret_ptr; +} + +__RT_VISIBILITY void *softboundcets_malloc(size_t size) { + + sbcets_key_t ptr_key = 1; + sbcets_lock_t ptr_lock = NULL; + + char *ret_ptr = (char *)malloc(size); + if (ret_ptr == NULL) { + __softboundcets_store_null_return_metadata(); + } else { + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_memory_allocation(ret_ptr, &ptr_lock, &ptr_key); +#endif + + char *ret_bound = ret_ptr + size; + __softboundcets_store_return_metadata(ret_ptr, ret_bound, ptr_key, + ptr_lock); + + if (__SOFTBOUNDCETS_FREE_MAP) { +#if 1 + __softboundcets_debug_printf("malloc ptr=%p, ptr_key=%zx\n", ret_ptr, + ptr_key); +#endif + /* __softboundcets_add_to_free_map(ptr_key, ret_ptr); */ + } + } + return ret_ptr; +} + +__RT_VISIBILITY clock_t softboundcets_times(struct tms *buf) { + return times(buf); +} + +__RT_VISIBILITY size_t softboundcets_strftime(char *s, size_t max, + const char *format, + const struct tm *tm) { + + return strftime(s, max, format, tm); +} + +__RT_VISIBILITY time_t softboundcets_mktime(struct tm *tm) { + return mktime(tm); +} + +__RT_VISIBILITY long softboundcets_pathconf(char *path, int name) { + return pathconf(path, name); +} + +__RT_VISIBILITY struct tm *softboundcets_localtime(const time_t *timep) { + + struct tm *ret_ptr = localtime(timep); + __softboundcets_store_return_metadata(ret_ptr, + (char *)ret_ptr + sizeof(struct tm), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY time_t softboundcets_time(time_t *t) { return time(t); } + +__RT_VISIBILITY void softboundcets_free(void *ptr) { + /* more checks required to check if it is a malloced address */ + // freeing a null pointer is allowed by the libc + if (ptr != NULL) { +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *base = __softboundcets_load_base_shadow_stack(0); + if (ptr != base) { + __softboundcets_log_message( + LOG_LEVEL_ERROR, + "[mem_dealloc] invalid deallocation!\npointer = %p, base = %p\n,", + ptr, base); + __softboundcets_abort(); + } +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + size_t *ptr_lock = __softboundcets_load_lock_shadow_stack(0); + size_t ptr_key = __softboundcets_load_key_shadow_stack(0); + + __softboundcets_log_message(LOG_LEVEL_INFO, + "[free] ptr = %p, lock = %p, key = %zx\n", ptr, + ptr_lock, ptr_key); + __softboundcets_memory_deallocation(ptr_lock, ptr_key); + + if (__SOFTBOUNDCETS_FREE_MAP) { + __softboundcets_check_remove_from_free_map(ptr_key, ptr); + } +#endif + } + + free(ptr); +} + +/* ////////////////////Time Related Library Wrappers///////////////////////// */ + +__RT_VISIBILITY char *softboundcets_ctime(const time_t *timep) { + + char *ret_ptr = ctime(timep); + + if (ret_ptr == NULL) { + __softboundcets_store_null_return_metadata(); + } else { + __softboundcets_store_return_metadata( + ret_ptr, ret_ptr + strlen(ret_ptr) + 1, 1, __softboundcets_global_lock); + } + return ret_ptr; +} + +__RT_VISIBILITY void softboundcets_setbuf(FILE *stream, char *buf) { + setbuf(stream, buf); +} + +__RT_VISIBILITY char *softboundcets_getenv(const char *name) { + + char *ret_ptr = getenv(name); + + if (ret_ptr != NULL) { + __softboundcets_store_return_metadata( + ret_ptr, ret_ptr + strlen(ret_ptr) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_atexit(void_func_ptr function) { + return atexit(function); +} + +#ifdef _GNU_SOURCE +__RT_VISIBILITY char *softboundcets_strerror_r(int errnum, char *buf, + size_t buf_len) { + + char *ret_ptr = strerror_r(errnum, buf, buf_len); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr) + 1), 1, + __softboundcets_global_lock); + return ret_ptr; +} +#endif + +__RT_VISIBILITY char *softboundcets_strerror(int errnum) { + + char *ret_ptr = strerror(errnum); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr) + 1), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY int softboundcets_unlink(const char *pathname) { + return unlink(pathname); +} + +__RT_VISIBILITY int softboundcets_open(const char *pathname, int flags) { + return open(pathname, flags); +} + +__RT_VISIBILITY ssize_t softboundcets_read(int fd, void *buf, size_t count) { + + return read(fd, buf, count); +} + +__RT_VISIBILITY ssize_t softboundcets_write(int fd, void *buf, size_t count) { + return write(fd, buf, count); +} + +__RT_VISIBILITY int softboundcets_gettimeofday(struct timeval *tv, void *tz) { + return gettimeofday(tv, tz); +} + +__RT_VISIBILITY int softboundcets_select(int nfds, fd_set *readfds, + fd_set *writefds, fd_set *exceptfds, + struct timeval *timeout) { + return select(nfds, readfds, writefds, exceptfds, timeout); +} + +#if defined(__linux__) + +__RT_VISIBILITY char *softboundcets_setlocale(int category, + const char *locale) { + + char *ret_ptr = setlocale(category, locale); + // when setlocale is called, the ctype array is potentially rewritten and we + // need to renew the stored metadata + softboundcets_init_ctype(); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_textdomain(const char *domainname) { + + char *ret_ptr = textdomain(domainname); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_bindtextdomain(const char *domainname, + const char *dirname) { + + char *ret_ptr = bindtextdomain(domainname, dirname); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_gettext(const char *msgid) { + + char *ret_ptr = gettext(msgid); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_dcngettext(const char *domainname, + const char *msgid, + const char *msgid_plural, + unsigned long int n, + int category) { + + char *ret_ptr = dcngettext(domainname, msgid, msgid_plural, n, category); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +/* IMP: struct hostent may have pointers in the structure being returned, + we need to store the metadata for all those pointers */ +__RT_VISIBILITY +struct hostent *softboundcets_gethostbyname(const char *name) { + + struct hostent *ret_ptr = gethostbyname(name); + + void *ret_bound = ret_ptr + sizeof(struct hostent); + __softboundcets_store_return_metadata(ret_ptr, ret_bound, 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY char *softboundcets_dcgettext(const char *domainname, + const char *msgid, int category) { + + char *ret_ptr = dcgettext(domainname, msgid, category); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + strlen(ret_ptr)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +#endif + +#if defined(__linux__) +__RT_VISIBILITY int *softboundcets___errno_location(void) { + int *ret_ptr = __errno_location(); + // printf("ERRNO: ptr is %lx", ptrs->ptr); + __softboundcets_store_return_metadata( + ret_ptr, (void *)((char *)ret_ptr + sizeof(int *)), 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY unsigned short const **softboundcets___ctype_b_loc(void) { + + unsigned short const **ret_ptr = __ctype_b_loc(); + + __softboundcets_store_return_metadata((void *)ret_ptr, + (void *)((char *)ret_ptr + 8), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY int const **softboundcets___ctype_toupper_loc(void) { + + int const **ret_ptr = __ctype_toupper_loc(); + + __softboundcets_store_return_metadata((void *)ret_ptr, + (void *)((char *)ret_ptr + 8), 1, + __softboundcets_global_lock); + return ret_ptr; +} + +__RT_VISIBILITY int const **softboundcets___ctype_tolower_loc(void) { + + int const **ret_ptr = __ctype_tolower_loc(); + __softboundcets_store_return_metadata((void *)ret_ptr, + (void *)((char *)ret_ptr + 8), 1, + __softboundcets_global_lock); + return ret_ptr; +} +#endif + +/* This is a custom implementation of qsort */ + +static int +compare_elements_helper(void *base, size_t element_size, int idx1, int idx2, + int (*comparer)(const void *, const void *)) { + + char *base_bytes = (char *)base; + return comparer(&base_bytes[idx1 * element_size], + &base_bytes[idx2 * element_size]); +} + +#define element_less_than(i, j) \ + (compare_elements_helper(base, element_size, (i), (j), comparer) < 0) + +static void exchange_elements_helper(void *base, size_t element_size, int idx1, + int idx2) { + + char *base_bytes = (char *)base; + size_t i; + + for (i = 0; i < element_size; i++) { + char temp = base_bytes[idx1 * element_size + i]; + base_bytes[idx1 * element_size + i] = base_bytes[idx2 * element_size + i]; + base_bytes[idx2 * element_size + i] = temp; + } + + for (i = 0; i < element_size; i += 8) { + void *base_idx1; + void *bound_idx1; + + void *base_idx2; + void *bound_idx2; + + size_t key_idx1 = 1; + size_t key_idx2 = 1; + + sbcets_lock_t lock_idx1 = NULL; + sbcets_lock_t lock_idx2 = NULL; + + char *addr_idx1 = &base_bytes[idx1 * element_size + i]; + char *addr_idx2 = &base_bytes[idx2 * element_size + i]; + + // printf("addr_idx1= %p, addr_idx2=%p\n", addr_idx1, addr_idx2); + __softboundcets_metadata_t *metadata1 = __softboundcets_shadowspace_metadata_ptr(addr_idx1); + __softboundcets_metadata_t *metadata2 = __softboundcets_shadowspace_metadata_ptr(addr_idx2); + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + base_idx1 = metadata1->base; + bound_idx1 = metadata1->bound; + base_idx2 = metadata2->base; + bound_idx2 = metadata2->bound; + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + lock_idx1 = metadata1->lock; + key_idx1 = metadata1->key; + lock_idx2 = metadata2->lock; + key_idx2 = metadata2->key; + +#endif + __softboundcets_metadata_store(addr_idx1, base_idx2, bound_idx2, key_idx2, + lock_idx2); + __softboundcets_metadata_store(addr_idx2, base_idx1, bound_idx1, key_idx1, + lock_idx1); + + } +} + +#define exchange_elements(i, j) \ + (exchange_elements_helper(base, element_size, (i), (j))) + +#define MIN_QSORT_LIST_SIZE 32 + +__WEAK__ +void my_qsort(void *base, size_t num_elements, size_t element_size, + int (*comparer)(const void *, const void *)) { + + size_t i; + + for (i = 0; i < num_elements; i++) { + int j; + for (j = i - 1; j >= 0; j--) { + if (element_less_than(j, j + 1)) + break; + exchange_elements(j, j + 1); + } + } + /* may be implement qsort here */ +} + +__RT_VISIBILITY void softboundcets_qsort(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, + const void *)) { + + my_qsort(base, nmemb, size, compar); +} + +#if defined(__linux__) + +__RT_VISIBILITY +void softboundcets__obstack_newchunk(struct obstack *obj, int b) { + + _obstack_newchunk(obj, b); +} + +__RT_VISIBILITY +int softboundcets__obstack_begin(struct obstack *obj, int a, int b, + void *(foo)(long), void(bar)(void *)) { + return _obstack_begin(obj, a, b, foo, bar); +} + +__RT_VISIBILITY +void softboundcets_obstack_free(struct obstack *obj, void *object) { + obstack_free(obj, object); +} + +__RT_VISIBILITY +char *softboundcets_nl_langinfo(nl_item item) { + + char *ret_ptr = nl_langinfo(item); + + __softboundcets_store_return_metadata(ret_ptr, ret_ptr + 1024 * 1024, 1, + __softboundcets_global_lock); + + return ret_ptr; +} + +__RT_VISIBILITY +int softboundcets_clock_gettime(clockid_t clk_id, struct timespec *tp) { + return clock_gettime(clk_id, tp); +} + +#endif + +#if 0 + +int softboundcets__obstack_memory_used(struct obstack *h){ + return _obstack_memory_used(h); +} + +#endif + +// TODO: Move to the correct file section (wchar.h) + +__RT_VISIBILITY +int softboundcets_wprintf(wchar_t *fmt, ...) { + CHECK_PTR_ALIVE_LOAD(0, fmt); + LOAD_PTR_BOUNDS(0, fmt); + +#if __SOFTBOUNDCETS_CHECK_LOADS + if (!wmemchr(fmt, L'\0', (char*)fmt_bound - (char*)fmt)) { + __softboundcets_error_printf("In wstring load dereference check: base=%zx, bound=%zx, ptr=%zx", + fmt_base, fmt_bound, fmt); + __softboundcets_abort(); + } +#endif + + va_list va; + va_start(va, fmt); + int result= vwprintf(fmt, va); + va_end(va); + + return result; +} + +// Functions from string.h + +__RT_VISIBILITY +void *softboundcets___mempcpy(void *dest, void *src, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + __softboundcets_copy_metadata(dest, src, n & ~7); + + return __mempcpy(dest, src, n); +} + +__RT_VISIBILITY +char *softboundcets___stpcpy(char *dest, char *src) { + CHECK_STRING_STORE(1, dest); + CHECK_STRING_LOAD(2, src); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return __stpcpy(dest, src); +} + +__RT_VISIBILITY +char *softboundcets___stpncpy(char *dest, char *src, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return __stpncpy(dest, src, n); +} + +__RT_VISIBILITY +char *softboundcets_basename(char *filename) { + CHECK_STRING_LOAD_NULLABLE(1, filename); + + char *result = basename(filename); + + if (filename != nullptr) { + // The return value is part of the (modified) filename parameter => copy the metadata over. + // We need to check this since the null pointer may not have all the metadata we want. + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + // strlen here is safe as the static result strings are always null terminated + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } + + return result; +} + +__RT_VISIBILITY +void softboundcets_explicit_bzero(void *buf, size_t n) { + CHECK_PTR_STORE(0, buf, n); + + explicit_bzero(buf, n); +} + +// NOTE: We are treating this function as a string copy function, so we are not copying metadata +__RT_VISIBILITY +void *softboundcets_memccpy(void *dest, void *src, int c, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + + void *result = memccpy(dest, src, c, n); + + if (result != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +void *softboundcets_memfrob(void *buf, size_t n) { + CHECK_PTR_LOAD(1, buf, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return memfrob(buf, n); +} + +__RT_VISIBILITY +void *softboundcets_memmem(void *haystack, size_t haystack_len, void *needle, size_t needle_len) { + CHECK_PTR_LOAD(1, haystack, haystack_len); + CHECK_PTR_LOAD(2, needle, needle_len); + + void *result = memmem(haystack, haystack_len, needle, needle_len); + + if (result != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +void *softboundcets_memmove(void *dest, void *src, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + __softboundcets_copy_metadata(dest, src, n & ~7); + return memmove(dest, src, n); +} + +__RT_VISIBILITY +void *softboundcets_memset(void *buf, int c, size_t n) { + CHECK_PTR_STORE(1, buf, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return memset(buf, c, n); +} + +__RT_VISIBILITY +void *softboundcets_rawmemchr(void *buf, int c) { + // Note that the point of this function is that it is memchr without the bounds check. + // This wrapper essentially makes the function useless, so we just use regular memchr. + LOAD_PTR_BOUNDS(1, buf); + CHECK_PTR_ALIVE_LOAD(1, buf); + + void *result = memchr(buf, c, (char*)buf_bound - (char*)buf); + if (result != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_error_printf("Out of bounds read in rawmemchr, base=%zx, bound=%zx, ptr=%zx, c=%u", + buf_base, buf_bound, buf, c); + __softboundcets_abort(); + } + return result; +} + +/* +The following functions should exist, but are not defined in the header file. + +__RT_VISIBILITY +char *softboundcets_sigabbrev_np(int sig) { + char *result = sigabbrev_np(sig); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +char *softboundcets_sigdescr_np(int sig) { + char *result = sigdescr_np(sig); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} +*/ + +__RT_VISIBILITY +char *softboundcets_stpncpy(char *dest, char *src, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return stpncpy(dest, src, n); +} + +__RT_VISIBILITY +char *softboundcets_strcasestr(char *haystack, char *needle) { + CHECK_STRING_LOAD(1, haystack); + CHECK_STRING_LOAD(2, needle); + + char *result = strcasestr(haystack, needle); + + if (result != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_strcoll(char *s1, char *s2) { + CHECK_STRING_BOUNDS_LOAD(0, s1); + CHECK_STRING_BOUNDS_LOAD(1, s2); + return strcoll(s1, s2); +} + +__RT_VISIBILITY +int softboundcets_strcoll_l(char *s1, char *s2, locale_t locale) { + CHECK_STRING_BOUNDS_LOAD(0, s1); + CHECK_STRING_BOUNDS_LOAD(1, s2); + return strcoll_l(s1, s2, locale); +} + +__RT_VISIBILITY +char *softboundcets_strerror_l(int errnum, locale_t locale) { + char *result = strerror_l(errnum, locale); + // This is save as strerror_l returns a valid string + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + return result; +} + +/* +__RT_VISIBILITY +char *softboundcets_strerrordesc_np(int errnum) { + char *result = strerrordesc_np(errnum); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +char *softboundcets_strerrorname_np(int errnum) { + char *result = strerrorname_np(errnum); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} +*/ + +__RT_VISIBILITY +char *softboundcets_strfry(char *str) { + CHECK_STRING_LOAD(1, str); + char *result = strfry(str); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return result; +} + +/* +__RT_VISIBILITY +size_t softboundcets_strlcat(char *dest, char *src, size_t n) { + CHECK_PTR_STORE(1, dest, n); + CHECK_PTR_LOAD(2, src, n); + return strlcat(dest, src, n); +} +*/ + +__RT_VISIBILITY +size_t softboundcets_strnlen(char *str, size_t n) { + CHECK_PTR_LOAD(0, str, n); + return strnlen(str, n); +} + +__RT_VISIBILITY +char *softboundcets_strsep(char **segment, char *delim) { + CHECK_PTR_STORE(1, segment, sizeof(char*)); + CHECK_STRING_LOAD(2, delim); + + if(*segment != nullptr) { + // Also check if the string pointed to by segment is valid + sbcets_base_t str_base = __softboundcets_metadata_load_base(*segment); + sbcets_bound_t str_bound = __softboundcets_metadata_load_bound(*segment); + sbcets_key_t str_key = __softboundcets_metadata_load_key(*segment); + sbcets_lock_t str_lock = __softboundcets_metadata_load_lock(*segment); + __softboundcets_temporal_store_dereference_check(str_lock, str_key); + // We need *segment to be a valid string -> It must contain a null terminator + if (!memchr(*segment, '\n', (char*)str_bound - *segment)) { + __softboundcets_error_printf("In string store dereference check: base=%zx, bound=%zx, ptr=%zx", + str_base, str_bound, *segment); + __softboundcets_abort(); + } + + __softboundcets_store_return_metadata(str_base, str_bound, str_key, str_lock); + return strsep(segment, delim); + } else { + __softboundcets_store_null_return_metadata(); + return nullptr; + } +} + +__RT_VISIBILITY +char *softboundcets_strsignal(int sig) { + char *result = strsignal(sig); + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + return result; +} + +__RT_VISIBILITY +char *softboundcets_strtok_r(char *s, char *delim, char **save_ptr) { + // This function has two "modes" + // If s is not null, it reads from s and modifies *save_ptr with the return pointing into s + // else it reads from and modifies *save_ptr with the retur pointing there as well + // Note that this behaviour may change with future releases of glibc + CHECK_STRING_LOAD_NULLABLE(1, s); + CHECK_STRING_LOAD(2, delim); + CHECK_PTR_LOAD(3, save_ptr, sizeof(char*)); + + if(s != nullptr) { + // We already checked all pointers + char *result = strtok_r(s, delim, save_ptr); + + // Store the metadata for *save_ptr + __softboundcets_metadata_store(*save_ptr, s_base, s_bound, s_key, s_lock); + if (result) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; + } else { + // We need to check the metadata of *save_ptr manually + sbcets_base_t saved_ptr_base = __softboundcets_metadata_load_base(*save_ptr); + sbcets_bound_t saved_ptr_bound = __softboundcets_metadata_load_bound(*save_ptr); + sbcets_lock_t saved_ptr_lock = __softboundcets_metadata_load_lock(*save_ptr); + sbcets_key_t saved_ptr_key = __softboundcets_metadata_load_key(*save_ptr); + + __softboundcets_temporal_store_dereference_check(saved_ptr_lock, saved_ptr_key); + if (!memchr(*save_ptr, '\n', (char*)saved_ptr_bound - *save_ptr)) { + __softboundcets_error_printf("In string store dereference check: base=%zx, bound=%zx, ptr=%zx", + saved_ptr_base, saved_ptr_bound, *save_ptr); + __softboundcets_abort(); + } + + char *result = strtok_r(s, delim, save_ptr); + if (result) { + // The returned pointer is in *save_ptr's bounds + __softboundcets_store_return_metadata(saved_ptr_base, saved_ptr_bound, saved_ptr_key, saved_ptr_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; + } +} + +__RT_VISIBILITY +int softboundcets_strverscmp(char *s1, char *s2) { + CHECK_STRING_LOAD(0, s1); + CHECK_STRING_LOAD(1, s2); + return strverscmp(s1, s2); +} + +__RT_VISIBILITY +size_t softboundcets_strxfrm(char *dest, char *src, size_t n) { + CHECK_PTR_STORE(0, dest, n); + CHECK_PTR_LOAD(1, src, n); + return strxfrm(dest, src, n); +} + +__RT_VISIBILITY +size_t softboundcets_strxfrm_l(char *dest, const char *src, size_t n, locale_t locale) { + CHECK_PTR_STORE(0, dest, n); + CHECK_PTR_LOAD(1, src, n); + return strxfrm_l(dest, src, n, locale); +} + +// Wrappers for strings.h + +__RT_VISIBILITY +int softboundcets_bcmp(void *s1, void *s2, size_t n) { + CHECK_PTR_LOAD(0, s1, n); + CHECK_PTR_LOAD(1, s2, n); + return bcmp(s1, s2, n); +} + +__RT_VISIBILITY +void softboundcets_bcopy(void *src, void *dest, size_t n) { + CHECK_PTR_LOAD(0, src, n); + CHECK_PTR_STORE(1, dest, n); + __softboundcets_copy_metadata(dest, src, n & ~7); + bcopy(src, dest, n); +} + +__RT_VISIBILITY +void softboundcets_bzero(void *s, size_t n) { + CHECK_PTR_STORE(1, s, n); + bzero(s, n); +} + +__RT_VISIBILITY +char *softboundcets_index(char *s, int c) { + CHECK_STRING_LOAD(1, s); + char *result = index(s, c); + if (result) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_strcasecmp_l(char *s1, char *s2, locale_t l) { + CHECK_STRING_LOAD(0, s1); + CHECK_STRING_LOAD(1, s2); + return strcasecmp_l(s1, s2, l); +} + +__RT_VISIBILITY +int softboundcets_strncasecmp_l(char *s1, char *s2, size_t n, locale_t l) { + CHECK_PTR_LOAD(0, s1, n); + CHECK_PTR_LOAD(1, s2, n); + return strncasecmp_l(s1, s2, n, l); +} + +// Wrappers for stdio.h + +__RT_VISIBILITY +int softboundcets___asprintf(char **ptr, char *fmt, ...) { + CHECK_PTR_STORE(0, ptr, sizeof(char*)); + CHECK_STRING_LOAD(1, fmt); + // Cannot use varargs directly, so use vasprintf + // Since __asprintf and asprintf point to the same implementation, this should be fine + va_list varargs; + va_start(varargs, fmt); + int result = vasprintf(ptr, fmt, varargs); + va_end(varargs); + + if (result != -1) { + sbcets_key_t key = 0; + sbcets_lock_t lock = nullptr; + __softboundcets_memory_allocation(*ptr, &lock, &key); + // We have sucessfully allocated, result contains the size of the buffer - 1 for \0 + __softboundcets_metadata_store(*ptr, *ptr, *ptr + result + 1, key, lock); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets___overflow(FILE *file, int i) { + // Note: While we should probably never dereference FILE* directly, some libc macros may, so set + // the bounds accordingly. + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return __overflow(file, i); +} + +__RT_VISIBILITY +int softboundcets___uflow(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return __uflow(file); +} + +__RT_VISIBILITY +int softboundcets_asprintf(char **ptr, char *fmt, ...) { + CHECK_PTR_STORE(0, ptr, sizeof(char*)); + CHECK_STRING_LOAD(1, fmt); + + va_list varargs; + va_start(varargs, fmt); + int result = vasprintf(ptr, fmt, varargs); + va_end(varargs); + + if (result != -1) { + sbcets_key_t key = 0; + sbcets_lock_t lock = nullptr; + __softboundcets_memory_allocation(*ptr, &lock, &key); + // We have sucessfully allocated, result contains the size of the buffer - 1 for \0 + __softboundcets_metadata_store(*ptr, *ptr, *ptr + result + 1, key, lock); + } + + return result; +} + +__RT_VISIBILITY +void softboundcets_clearerr(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + clearerr(file); +} + +__RT_VISIBILITY +void softboundcets_clearerr_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + clearerr(file); +} + +__RT_VISIBILITY +char *softboundcets_ctermid(char *s) { + // L_ctermid is the maximum size retuned by the function. + CHECK_PTR_LOAD_NULLABLE(1, s, L_ctermid); + + char *result = ctermid(s); + + if (s != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_return_metadata(result, result + L_ctermid, 1, __softboundcets_global_lock); + } + return result; +} + +__RT_VISIBILITY +char *softboundcets_cuserid(char *s) { + CHECK_PTR_STORE_NULLABLE(1, s, L_cuserid); + + char *result = cuserid(s); + + if (s != nullptr) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_return_metadata(result, result + L_cuserid, 1, __softboundcets_global_lock); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_dprintf(int fd, char *fmt, ...) { + CHECK_STRING_LOAD(0, fmt); + + va_list varargs; + va_start(varargs, fmt); + int result = vdprintf(fd, fmt, varargs); + va_end(varargs); + + return result; +} + +__RT_VISIBILITY +int softboundcets_feof_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return feof_unlocked(file); +} + +__RT_VISIBILITY +int softboundcets_ferror_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return ferror_unlocked(file); +} + +__RT_VISIBILITY +int softboundcets_fflush_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return fflush_unlocked(file); +} + +__RT_VISIBILITY +int softboundcets_fgetc_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)) + return fgetc_unlocked(file); +} + +__RT_VISIBILITY +int softboundcets_fgetpos(FILE *file, fpos_t *pos) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_STORE(1, pos, sizeof(fpos_t)); + return fgetpos(file, pos); +} + +__RT_VISIBILITY +int softboundcets_fgetpos64(FILE *file, fpos64_t *pos) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_STORE(1, pos, sizeof(fpos64_t)); + return fgetpos64(file, pos); +} + +__RT_VISIBILITY +char *softboundcets_fgets_unlockd(char *s, int n, FILE *file) { + CHECK_PTR_STORE(1, s, n); + CHECK_PTR_LOAD(2, file, sizeof(FILE)); + char *result = fgets_unlocked(s, n, file); + if (result) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_fileno_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return fileno_unlocked(file); +} + +__RT_VISIBILITY +void softboundcets_flockfile(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + flockfile(file); +} + +__RT_VISIBILITY +FILE *softboundcets_fmemopen(void *buf, size_t n, char *mode) { + CHECK_PTR_STORE_NULLABLE(1, buf, n); + CHECK_STRING_LOAD(2, mode); + + FILE *result = fmemopen(buf, n, mode); + if (result) { + sbcets_lock_t lock; + sbcets_key_t key; + __softboundcets_memory_allocation(result, &lock, &key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), key, lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +FILE *softboundcets_fopen64(char *filename, char *mode) { + CHECK_STRING_LOAD(1, filename); + CHECK_STRING_LOAD(2, mode); + + FILE *result = fopen64(filename, mode); + if (result) { + sbcets_lock_t lock; + sbcets_key_t key; + __softboundcets_memory_allocation(result, &lock, &key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), key, lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +// TODO: The inner pointers of cookie_io_functions_t are probably not checked, so add macros for +// situations like this as well? +__RT_VISIBILITY +FILE *softboundcets_fopencookie(void *magic_cookie, char *modes, cookie_io_functions_t io_functions) { + // TODO: What to do with dynamically sized data types? I suppose they are checked in the callback + // functions that this value is eventually passed to since the io_functions are user-defined. + CHECK_PTR_LOAD(1, magic_cookie, 0); + CHECK_STRING_LOAD(2, modes); + + FILE *result = fopencookie(magic_cookie, modes, io_functions); + if (result) { + sbcets_lock_t lock; + sbcets_key_t key; + __softboundcets_memory_allocation(result, &lock, &key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), key, lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_fprintf(FILE *file, char *fmt, ...) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_STRING_LOAD(1, fmt); + + va_list va; + va_start(va, fmt); + int result = vfprintf(file, fmt, va); + va_end(va); + + return result; +} + +__RT_VISIBILITY +int softboundcets_fputc_unlocked(int c, FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + + return fputc_unlocked(c, file); +} + +__RT_VISIBILITY +int softboundcets_fputs_unlocked(char *s, FILE *file) { + CHECK_STRING_LOAD(0, s); + CHECK_PTR_LOAD(1, file, sizeof(FILE)); + + return fputs_unlocked(s, file); +} + +__RT_VISIBILITY +FILE *softboundcets_freopen(char *filename, char *mode, FILE *file) { + CHECK_STRING_LOAD_NULLABLE(1, filename); + CHECK_STRING_LOAD(2, mode); + // TODO: The freopen function accepts null, but returns an error if it does. + // Is that something we want to avoid? + CHECK_PTR_LOAD_NULLABLE(3, file, sizeof(FILE)); + + // TODO: I'm somewhat confused as to how this function works. + // It seems like the old file is always closed in the glibc implementation, but the man page + // seems to disagree + // Just treat this as a deallocation and new allocation for now, if the function succeeds + FILE *result = freopen(filename, mode, file); + if (result) { + sbcets_lock_t lock; + sbcets_key_t key; + __softboundcets_memory_allocation(result, &lock, &key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), key, lock); + __softboundcets_memory_deallocation(file_lock, file_key); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +FILE *softboundcets_freopen64(char *filename, char *mode, FILE *file) { + CHECK_STRING_LOAD_NULLABLE(1, filename); + CHECK_STRING_LOAD(2, mode); + CHECK_PTR_LOAD_NULLABLE(3, file, sizeof(FILE)); + + FILE *result = freopen64(filename, mode, file); + if (result) { + sbcets_lock_t lock; + sbcets_key_t key; + __softboundcets_memory_allocation(result, &lock, &key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), key, lock); + __softboundcets_memory_deallocation(file_lock, file_key); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_fscanf(FILE *file, char *format, ...) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_STRING_LOAD(1, format); + + va_list va; + va_start(va, format); + int result = vfscanf(file, format, va); + va_end(va); + + return result; +} + +__RT_VISIBILITY +int softboundcets_fseeko64(FILE *file, off64_t off, int whence) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return fseeko64(file, off, whence); +} + +__RT_VISIBILITY +int softboundcets_fsetpos(FILE *file, fpos_t *pos) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_LOAD(1, pos, sizeof(fpos_t)); + return fsetpos(file, pos); +} + +__RT_VISIBILITY +int softboundcets_fsetpos64(FILE *file, fpos64_t *pos) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_LOAD(1, pos, sizeof(fpos_t)); + return fsetpos64(file, pos); +} + +__RT_VISIBILITY +off_t softboundcets_ftello(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return ftello(file); +} + +__RT_VISIBILITY +off64_t softboundcets_ftello64(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return ftello64(file); +} + +__RT_VISIBILITY +int softboundcets_ftrylockfile(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return ftrylockfile(file); +} + +__RT_VISIBILITY +void softboundcets_funlockfile(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + funlockfile(file); +} + +__RT_VISIBILITY +size_t softboundcets_fwrite_unlocked(void *ptr, size_t size, size_t n, FILE *file) { + // Note: This could technically overflow + CHECK_PTR_LOAD(0, ptr, size * n); + CHECK_PTR_LOAD(1, file, sizeof(FILE)); + return fwrite_unlocked(ptr, size, n, file); +} + +__RT_VISIBILITY +int softboundcets_getc(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return getc(file); +} + +__RT_VISIBILITY +int softboundcets_getc_unlocked(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return getc_unlocked(file); +} + + +__RT_VISIBILITY +ssize_t softboundcets_getdelim(char **lineptr, size_t *n, int delim, FILE *file) { + CHECK_PTR_STORE(0, lineptr, sizeof(char*)); + CHECK_PTR_STORE(1, n, sizeof(size_t)); + CHECK_PTR_LOAD(2, file, sizeof(FILE)); + + // Note: *lineptr can be null or malloc'd, but should not be static of from some other allocator + // that does not work with metadata. + // Since I don't think that softboundcets can express this, this is not checked. + if (*lineptr) { + sbcets_bound_t line_bound = __softboundcets_metadata_load_bound(*lineptr); + size_t buffer_size = (char*)line_bound - *lineptr; + if (buffer_size < *n) { + __softboundcets_error_printf("in getdelim, the buffer size is assumed to be too large: "\ + "actual size: %d, stated size: %d", buffer_size, *n); + } + } + char *prev_line = *lineptr; + + ssize_t result = getdelim(lineptr, n, delim, file); + + // Check if we (re-)allocated and reset metadata if so + // TODO: Is this correct? Is there any other way to detect reallocation at the same location? + if (prev_line != *lineptr) { + + sbcets_lock_t line_lock = __softboundcets_metadata_load_lock(prev_line); + sbcets_key_t line_key = __softboundcets_metadata_load_key(prev_line); + __softboundcets_memory_deallocation(line_lock, line_key); + + sbcets_lock_t new_line_lock = nullptr; + sbcets_key_t new_line_key = 0; + __softboundcets_memory_allocation(*lineptr, &new_line_lock, &new_line_key); + __softboundcets_metadata_store(*lineptr, *lineptr, *lineptr + *n, new_line_key, new_line_lock); + } + + return result; +} + +__RT_VISIBILITY +ssize_t softboundcets_getline(char **lineptr, size_t *n, FILE *file) { + CHECK_PTR_STORE(0, lineptr, sizeof(char*)); + CHECK_PTR_STORE(1, n, sizeof(size_t)); + CHECK_PTR_LOAD(2, file, sizeof(FILE)); + + // Note: *lineptr can be null or malloc'd, but should not be static of from some other allocator + // that does not work with metadata. + // Since I don't think that softboundcets can express this, this is not checked. + if (*lineptr) { + sbcets_bound_t line_bound = __softboundcets_metadata_load_bound(*lineptr); + size_t buffer_size = (char*)line_bound - *lineptr; + if (buffer_size < *n) { + __softboundcets_error_printf("in getdelim, the buffer size is assumed to be too large: "\ + "actual size: %d, stated size: %d", buffer_size, *n); + __softboundcets_abort(); + } + } + char *prev_line = *lineptr; + + ssize_t result = getline(lineptr, n, file); + + // Check if we (re-)allocated and reset metadata if so + if (prev_line != *lineptr) { + + sbcets_lock_t line_lock = __softboundcets_metadata_load_lock(prev_line); + sbcets_key_t line_key = __softboundcets_metadata_load_key(prev_line); + __softboundcets_memory_deallocation(line_lock, line_key); + + sbcets_lock_t new_line_lock = nullptr; + sbcets_key_t new_line_key = 0; + __softboundcets_memory_allocation(*lineptr, &new_line_lock, &new_line_key); + __softboundcets_metadata_store(*lineptr, *lineptr, *lineptr + *n, new_line_key, new_line_lock); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_getw(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return getw(file); +} + +__RT_VISIBILITY +int softboundcets_obstack_printf(struct obstack *obstack, char *fmt, ...) { + CHECK_PTR_LOAD(0, obstack, sizeof(struct obstack)); + CHECK_STRING_LOAD(1, fmt); + + va_list va; + va_start(va, fmt); + int result = obstack_vprintf(obstack, fmt, va); + va_end(va); + return result; +} + +__RT_VISIBILITY +int softboundcets_obstack_vprintf(struct obstack *obstack, char *fmt, va_list va) { + CHECK_PTR_LOAD(0, obstack, sizeof(struct obstack)); + CHECK_STRING_LOAD(1, fmt); + + return obstack_vprintf(obstack, fmt, va); +} + +__RT_VISIBILITY +FILE *softboundcets_open_memstream(char **buf, size_t *size) { + CHECK_PTR_STORE(1, buf, sizeof(char*)); + CHECK_PTR_STORE(2, size, sizeof(size_t)); + + // Note: The pointer pointed to by *buf grows dynamically and is this reallocated when writing. + FILE *result = open_memstream(buf, size); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + // *buf can be assigned regardless of the functions success + if (*buf) { + sbcets_lock_t lock = nullptr; + sbcets_key_t key = 0; + __softboundcets_memory_allocation(*buf, &lock, &key); + // WORKAROUND: Since the pointer can be reallocated by the libc without us knowing, just make + // this pointer valid erverywhere. + // I don't think this function is used often enough to merit runtime patching the relevant + // realloc function. + __softboundcets_metadata_store(*buf, 0, (void*)281474976710656, key, lock); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_printf(char *fmt, ...) { + CHECK_STRING_LOAD(0, fmt); + + va_list args; + va_start(args, + fmt); // Initialize va_list with the start of the variable arguments + size_t ptr_arg_pos = 1; + + int i = 0; + while (fmt[i] != '\0') { + if (fmt[i] == '%' && fmt[i + 1] != '%' && fmt[i + 1] != '\0') { + i++; + // ignore all flags/width/precision/modifiers + while(isdigit(fmt[i]) || fmt[i] == '.' || fmt[i] == '#' || fmt[i] == '+' || + fmt[i] == '-' || fmt[i] == ' ' || fmt[i] == 'h' || fmt[i] == 'l' || + fmt[i] == 'j' || fmt[i] == 'z' || fmt[i] == 't' || fmt[i] == 'L') + i++; + switch (fmt[i]) { + // Integer specifiers + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': { + int intValue = va_arg(args, int); + break; + } + // Floating-point specifiers + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': { + double floatValue = + va_arg(args, double); // Note: 'float' is promoted to 'double' when + // passed through '...' + break; + } + case 's': { + char *stringValue = va_arg(args, char *); + CHECK_STRING_LOAD(ptr_arg_pos, stringValue); + ptr_arg_pos++; + break; + } + case 'c': { + // Note: 'char' is promoted to 'int' when passed through '...' + int charValue = va_arg(args, int); + break; + } + case 'p': { + void *val = va_arg(args, void *); + break; + } + // Add more cases as needed + default: + printf("Unknown fmt specifier: %%%c\n", fmt[i]); + break; + } + } else if (fmt[i] == '%' && fmt[i + 1] == '%') { + // Skip "%%" since it's used to print a literal '%' + i++; + } + i++; + } + + va_end(args); + va_list va; + va_start(va, fmt); + int result = vprintf(fmt, va); + va_end(va); + return result; +} + +__RT_VISIBILITY +int softboundcets_putc(int c, FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return putc(c, file); +} + +__RT_VISIBILITY +int softboundcets_putc_unlocked(int c, FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return putc_unlocked(c, file); +} + +__RT_VISIBILITY +int softboundcets_putw(int w, FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + return putw(w, file); +} + +__RT_VISIBILITY +int softboundcets_renameat2(int oldfd, char *oldpath, int newfd, char *newpath, unsigned int flags) { + CHECK_STRING_LOAD(0, oldpath); + CHECK_STRING_LOAD(1, newpath); + + return renameat2(oldfd, oldpath, newfd, newpath, flags); +} + +__RT_VISIBILITY +int softboundcets_scanf(char *fmt, ...) { + CHECK_STRING_LOAD(0, fmt); + + va_list va; + va_start(va, fmt); + int result = vscanf(fmt, va); + va_end(va); + return result; +} + +__RT_VISIBILITY +void softboundcets_setbuffer(FILE *file, char *buf, size_t size) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_STORE_NULLABLE(1, buf, size); + + // Note: the buffer needs to be alive as long as it is bound to file, but we cannot express this + setbuffer(file, buf, size); +} + +__RT_VISIBILITY +void softboundcets_setlinebuf(FILE *file) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + setlinebuf(file); +} + +__RT_VISIBILITY +int softboundcets_setvbuf(FILE *file, char *buf, int mode, size_t size) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_PTR_STORE_NULLABLE(1, buf, size); + + // Note: the buffer needs to be alive as long as it is bound to file, but we cannot express this + return setvbuf(file, buf, mode, size); +} + +__RT_VISIBILITY +int softboundcets_snprintf(char *buf, size_t n, char *fmt, ...) { + CHECK_PTR_STORE(0, buf, n); + CHECK_STRING_LOAD(1, fmt); + + va_list va; + va_start(va, fmt); + int result = vsnprintf(buf, n, fmt, va); + va_end(va); + + return result; +} + +__RT_VISIBILITY +int softboundcets_sprintf(char *buf, char *fmt, ...) { + CHECK_PTR_STORE_NULLABLE(0, buf, 0); + CHECK_STRING_LOAD(1, fmt); + + // Note: Since we cannot check for the buffer size before calling sprintf, we check after to + // limit the potential damage + va_list va; + va_start(va, fmt); + int result = vsprintf(buf, fmt, va); + va_end(va); + + if (buf) { + size_t bufsize = (size_t)buf_bound - (size_t)buf_base; + // The trailing null character is not included, so if the result is the same as the buffer size, + // we have overshot by one. + if ((size_t)result >= bufsize) { + __softboundcets_error_printf("sprintf result did not fit in the provided buffer: "\ + "buffer size = %llu, required size = %d", bufsize, result + 1); + __softboundcets_abort(); + } + } + return result; +} + +__RT_VISIBILITY +int softboundcets_sscanf(char *buf, char *fmt, ...) { + CHECK_STRING_LOAD(0, buf); + CHECK_STRING_LOAD(1, fmt); + + va_list va; + va_start(va, fmt); + int result = vsscanf(buf, fmt, va); + va_end(va); + + return result; +} + +__RT_VISIBILITY +char *softboundcets_tempnam(char *dir, char *pfx) { + CHECK_STRING_LOAD_NULLABLE(1, dir); + CHECK_STRING_LOAD_NULLABLE(2, pfx); + + char *result = tempnam(dir, pfx); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +FILE *softboundcets_tmpfile64() { + FILE *result = tmpfile64(); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, result + sizeof(FILE), result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +char *softboundcets_tmpnam(char *s) { + CHECK_PTR_STORE_NULLABLE(1, s, L_tmpnam); + + char *result = tmpnam(s); + + if (result) { + if (s) { + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + // Per the man page, this returns a static buffer + __softboundcets_store_return_metadata(result, result + L_tmpnam, 1, __softboundcets_global_lock); + } + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +char *softboundcets_tmpnam_r(char *s) { + CHECK_PTR_STORE_NULLABLE(1, s, L_tmpnam); + + char *result = tmpnam_r(s); + + if (result) { + // This just returns s + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_vasprintf(char **ptr, char *fmt, va_list va) { + CHECK_PTR_STORE(0, ptr, sizeof(char*)); + CHECK_STRING_LOAD(1, fmt); + + int result = vasprintf(ptr, fmt, va); + + if (result != -1) { + sbcets_key_t key = 0; + sbcets_lock_t lock = nullptr; + __softboundcets_memory_allocation(*ptr, &lock, &key); + // We have sucessfully allocated, result contains the size of the buffer - 1 for \0 + __softboundcets_metadata_store(*ptr, *ptr, *ptr + result + 1, key, lock); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_vdprintf(int fd, char *fmt, va_list va) { + CHECK_STRING_LOAD(0, fmt); + + return vdprintf(fd, fmt, va); +} + +__RT_VISIBILITY +int softboundcets_vfprintf(FILE *file, char *fmt, va_list va) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_STRING_LOAD(1, fmt); + + return vfprintf(file, fmt, va); +} + +__RT_VISIBILITY +int softboundcets_vfscanf(FILE *file, char *format, va_list va) { + CHECK_PTR_LOAD(0, file, sizeof(FILE)); + CHECK_STRING_LOAD(1, format); + + return vfscanf(file, format, va); +} + +__RT_VISIBILITY +int softboundcets_vprintf(char *fmt, va_list va) { + CHECK_STRING_LOAD(0, fmt); + + return vprintf(fmt, va); +} + +__RT_VISIBILITY +int softboundcets_vscanf(char *fmt, va_list va) { + CHECK_STRING_LOAD(0, fmt); + + return vscanf(fmt, va); +} + + +__RT_VISIBILITY +int softboundcets_vsnprintf(char *buf, size_t n, char *fmt, va_list va) { + CHECK_PTR_STORE(0, buf, n); + CHECK_STRING_LOAD(1, fmt); + + return vsnprintf(buf, n, fmt, va); +} + +__RT_VISIBILITY +int softboundcets_vsprintf(char *buf, char *fmt, va_list va) { + CHECK_PTR_STORE_NULLABLE(0, buf, 0); + CHECK_STRING_LOAD(1, fmt); + + // Note: Since we cannot check for the buffer size before calling sprintf, we check after to + // limit the potential damage + int result = vsprintf(buf, fmt, va); + + if (buf) { + size_t bufsize = (size_t)buf_bound - (size_t)buf_base; + // The trailing null character is not included, so if the result is the same as the buffer size, + // we have overshot by one. + if ((size_t)result >= bufsize) { + __softboundcets_error_printf("vsprintf result did not fit in the provided buffer: "\ + "buffer size = %llu, required size = %d", bufsize, result + 1); + __softboundcets_abort(); + } + } + return result; +} + +__RT_VISIBILITY +int softboundcets_vsscanf(char *buf, char *fmt, va_list va) { + CHECK_STRING_LOAD(0, buf); + CHECK_STRING_LOAD(1, fmt); + + return vsscanf(buf, fmt, va); +} + +__RT_VISIBILITY +wchar_t *softboundcets_wcscpy(wchar_t *dest, const wchar_t *src) { + CHECK_STRING_STORE(1, dest); + CHECK_STRING_LOAD(2, src); + + size_t size = wcslen(src)*sizeof(wchar_t); + if (dest < dest_base || ((char*)dest > (char*)dest_bound - size - 1) || + (size > (size_t)dest_bound)) { + printf("[wcscpy] overflow in wcscpy with dest\n"); + __softboundcets_abort(); + } + if (src < src_base || ((char*)src > (char*)src_bound - size - 1) || + (size > (size_t)src_bound)) { + printf("[wcscpy] overflow in wcscpy with src\n"); + __softboundcets_abort(); + } + + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return wcscpy(dest, src); +} + +// Wrappers for unistd.h + +__RT_VISIBILITY +int softboundcets_access(char *name, int type) { + CHECK_STRING_LOAD(0, name); + return access(name, type); +} + +__RT_VISIBILITY +int softboundcets_brk(void *addr) { + // Note: brk only uses the address of the pointer, this is not expected to be valid + return brk(addr); +} + +__RT_VISIBILITY +size_t softboundcets_confstr(int name, char *buf, size_t len) { + CHECK_PTR_STORE_NULLABLE(0, buf, len); + return confstr(name, buf, len); +} + +__RT_VISIBILITY +ssize_t softboundcets_copy_file_range(int infd, loff_t *inoff, int outfd, loff_t *outoff, size_t length, unsigned int flags) { + CHECK_PTR_LOAD_NULLABLE(0, inoff, sizeof(loff_t)); + CHECK_PTR_STORE_NULLABLE(1, outoff, sizeof(loff_t)); + return copy_file_range(infd, inoff, outfd, outoff, length, flags); +} + +/*__RT_VISIBILITY +char *softboundcets_crypt(char *key, char *salt) { + CHECK_STRING_LOAD(1, key); + CHECK_STRING_LOAD(2, salt); + + char *result = crypt(key, salt); + // crypt should always return a buffer, but an invalid value on error + __softboundcets_store_return_metadata(result, result + CRYPT_OUTPUT_SIZE, 1, __softboundcets_global_lock); + return result; +}*/ + +__RT_VISIBILITY +int softboundcets_eaccess(char *name, int type) { + CHECK_STRING_LOAD(0, name); + return eaccess(name, type); +} + +__RT_VISIBILITY +int softboundcets_euidaccess(char *name, int type) { + CHECK_STRING_LOAD(0, name); + return euidaccess(name, type); +} + +__RT_VISIBILITY +int softboundcets_execl(char *path, char *arg0, ...) { + CHECK_STRING_LOAD(0, path); + CHECK_STRING_LOAD(1, arg0); + + __sanitizer::Vector args; + args.PushBack(arg0); + char *arg; + va_list va; + va_start(va, arg0); + + // Note: This loop may overrun if the va_list is not terminated + arg = va_arg(va, char *); + while (arg != nullptr) { + sbcets_base_t arg_base = __softboundcets_metadata_load_base(arg); + sbcets_bound_t arg_bound = __softboundcets_metadata_load_bound(arg); + sbcets_lock_t arg_lock = __softboundcets_metadata_load_lock(arg); + sbcets_key_t arg_key = __softboundcets_metadata_load_key(arg); + + __softboundcets_temporal_load_dereference_check(arg_lock, arg_key); + if (!memchr(arg, '\0', (char*)arg_bound - arg)) { + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", + arg_base, arg_bound, arg); + __softboundcets_abort(); + } + + args.PushBack(arg); + arg = va_arg(va, char *); + } + args.PushBack(nullptr); + + int result = execv(path, &args[0]); + + va_end(va); + return result; +} + +// Check a null terminated string array +static void check_string_array(char **array, sbcets_bound_t array_bound) { + bool null_found = false; + for (char **element = array; element < array_bound; element++) { + if (element == nullptr) { + null_found = true; + break; + } + + sbcets_base_t element_base = __softboundcets_metadata_load_base(element); + sbcets_bound_t element_bound = __softboundcets_metadata_load_bound(element); + sbcets_lock_t element_lock = __softboundcets_metadata_load_lock(element); + sbcets_key_t element_key = __softboundcets_metadata_load_key(element); + + __softboundcets_temporal_load_dereference_check(element_lock, element_key); + if (!memchr(*element, '\0', (char*)element_bound - *element)) { + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", + element_base, element_bound, *element); + __softboundcets_abort(); + } + } + + if (!null_found) { + __softboundcets_error_printf("Array was not NULL-terminated"); + __softboundcets_abort(); + } +} + +__RT_VISIBILITY +int softboundcets_execle(char *path, char *arg0, .../*, nullptr, char *envp[]*/) { + CHECK_STRING_LOAD(0, path); + CHECK_STRING_LOAD(1, arg0); + + __sanitizer::Vector args; + args.PushBack(arg0); + char *arg; + va_list va; + va_start(va, arg0); + + // Note: This loop may overrun if the va_list is not terminated + arg = va_arg(va, char *); + while (arg != nullptr) { + sbcets_base_t arg_base = __softboundcets_metadata_load_base(arg); + sbcets_bound_t arg_bound = __softboundcets_metadata_load_bound(arg); + sbcets_lock_t arg_lock = __softboundcets_metadata_load_lock(arg); + sbcets_key_t arg_key = __softboundcets_metadata_load_key(arg); + + __softboundcets_temporal_load_dereference_check(arg_lock, arg_key); + if (!memchr(arg, '\0', (char*)arg_bound - arg)) { + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", + arg_base, arg_bound, arg); + __softboundcets_abort(); + } + + args.PushBack(arg); + arg = va_arg(va, char *); + } + args.PushBack(nullptr); + + char **envp = va_arg(va, char **); +#if __SOFTBOUNDCETS_CHECK_LOADS + sbcets_base_t envp_base = __softboundcets_metadata_load_base(envp); + sbcets_bound_t envp_bound = __softboundcets_metadata_load_bound(envp); + sbcets_lock_t envp_lock = __softboundcets_metadata_load_lock(envp); + sbcets_key_t envp_key = __softboundcets_metadata_load_key(envp); + + __softboundcets_temporal_load_dereference_check(envp_lock, envp_key); + check_string_array(envp, envp_bound); +#endif + + int result = execve(path, &args[0], envp); + + va_end(va); + return result; +} + +__RT_VISIBILITY +int softboundcets_execlp(char *file, char *arg0, ...) { + CHECK_STRING_LOAD(0, file); + CHECK_STRING_LOAD(1, arg0); + + __sanitizer::Vector args; + args.PushBack(arg0); + char *arg; + va_list va; + va_start(va, arg0); + + // Note: This loop may overrun if the va_list is not terminated + arg = va_arg(va, char *); + while (arg != nullptr) { + sbcets_base_t arg_base = __softboundcets_metadata_load_base(arg); + sbcets_bound_t arg_bound = __softboundcets_metadata_load_bound(arg); + sbcets_lock_t arg_lock = __softboundcets_metadata_load_lock(arg); + sbcets_key_t arg_key = __softboundcets_metadata_load_key(arg); + + __softboundcets_temporal_load_dereference_check(arg_lock, arg_key); + if (!memchr(arg, '\0', (char*)arg_bound - arg)) { + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", + arg_base, arg_bound, arg); + __softboundcets_abort(); + } + + args.PushBack(arg); + arg = va_arg(va, char *); + } + args.PushBack(nullptr); + + int result = execvp(file, &args[0]); + + va_end(va); + return result; +} + +__RT_VISIBILITY +int softboundcets_execv(char *path, char **argv) { + CHECK_STRING_LOAD(0, path); + CHECK_PTR_LOAD(1, argv, sizeof(char*)); +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(argv, argv_bound); +#endif + + return execv(path, argv); +} + +__RT_VISIBILITY +int softboundcets_execve(char *path, char **argv, char **envp) { + CHECK_STRING_LOAD(0, path); + CHECK_PTR_LOAD(1, argv, sizeof(char*)); + CHECK_PTR_LOAD(2, envp, sizeof(char*)); + +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(argv, argv_bound); + check_string_array(envp, envp_bound); +#endif + + return execve(path, argv, envp); +} + +/* TODO: This is undefined on debian? +__RT_VISIBILITY +int softboundcets_execveat(int dirfd, char *path, char **argv, char **envp, int flags) { + CHECK_STRING_LOAD(2, path); + CHECK_PTR_LOAD(3, argv, sizeof(char*)); + CHECK_PTR_LOAD(4, envp, sizeof(char*)); + +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(argv, argv_bound); + check_string_array(envp, envp_bound); +#endif + + return execveat(dirfd, path, argv, envp, flags); +} +*/ + +__RT_VISIBILITY +int softboundcets_execvp(char *file, char **args) { + CHECK_STRING_LOAD(0, file); + CHECK_PTR_LOAD(1, args, sizeof(char*)); + +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(args, args_bound); +#endif + + return execvp(file, args); +} + +__RT_VISIBILITY +int softboundcets_execvpe(char *file, char **args, char **envp) { + CHECK_STRING_LOAD(0, file); + CHECK_PTR_LOAD(1, args, sizeof(char*)); + CHECK_PTR_LOAD(2, envp, sizeof(char*)); + +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(args, args_bound); + check_string_array(envp, envp_bound); +#endif + + return execvpe(file, args, envp); +} + +__RT_VISIBILITY +int softboundcets_faccessat(int fd, char *file, int type, int flags) { + CHECK_STRING_LOAD(0, file); + return faccessat(fd, file, type, flags); +} + +__RT_VISIBILITY +int softboundcets_fexecve(int fd, char **argv, char **envp) { + CHECK_PTR_LOAD(0, argv, sizeof(char*)); + CHECK_PTR_LOAD(1, envp, sizeof(char*)); + +#if __SOFTBOUNDCETS_CHECK_LOADS + check_string_array(argv, argv_bound); + check_string_array(envp, envp_bound); +#endif + + return fexecve(fd, argv, envp); +} + +__RT_VISIBILITY +char *softboundcets_get_current_dir_name(void) { + char *result = get_current_dir_name(); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_getdomainname(char *name, size_t len) { + CHECK_PTR_STORE(0, name, len); + return getdomainname(name, len); +} + +__RT_VISIBILITY +int softboundcets_getgroups(int size, gid_t *list) { + if (size > 0) { + CHECK_PTR_STORE(0, list, size * sizeof(gid_t)); + } + + return getgroups(size, list); +} + +__RT_VISIBILITY +int softboundcets_gethostname(char *name, size_t len) { + CHECK_PTR_STORE(0, name, len); + return gethostname(name, len); +} + +__RT_VISIBILITY +char *softboundcets_getlogin(void) { + char *result = getlogin(); + + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_getlogin_r(char *name, size_t len) { + CHECK_PTR_STORE(0, name, len); + return getlogin_r(name, len); +} + +__RT_VISIBILITY +char *softboundcets_getpass(char *prompt) { + CHECK_STRING_LOAD(1, prompt); + char *result = getpass(prompt); + + if (result) { + // Note: The bound should be PASS_MAX, but in glibc, it allocates BUFSIZ instead + __softboundcets_store_return_metadata(result, result + BUFSIZ, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid) { + CHECK_PTR_STORE(0, rgid, sizeof(gid_t)); + CHECK_PTR_STORE(1, egid, sizeof(gid_t)); + CHECK_PTR_STORE(2, sgid, sizeof(gid_t)); + return getresgid(rgid, egid, sgid); +} + +__RT_VISIBILITY +int softboundcets_getresuid(uid_t *ruid, uid_t *euid, uid_t *suid) { + CHECK_PTR_STORE(0, ruid, sizeof(uid_t)); + CHECK_PTR_STORE(1, euid, sizeof(uid_t)); + CHECK_PTR_STORE(2, suid, sizeof(uid_t)); + return getresuid(ruid, euid, suid); +} + +__RT_VISIBILITY +char *softboundcets_getusershell(void) { + char *result = getusershell(); + + if (result) { + // The man page does not have a lot to say about this function, so I assume that the buffer is + // not malloc'd + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +char *softboundcets_getwd(char *buf) { + CHECK_PTR_STORE(1, buf, PATH_MAX); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return getwd(buf); +} + +__RT_VISIBILITY +int softboundcets_lchown(char *file, uid_t owner, gid_t group) { + CHECK_STRING_LOAD(0, file); + return lchown(file, owner, group); +} + +__RT_VISIBILITY +int softboundcets_link(char *from, char *to) { + CHECK_STRING_LOAD(0, from); + CHECK_STRING_LOAD(1, to); + + return link(from, to); +} + +__RT_VISIBILITY +int softboundcets_pipe(int *pipes) { + CHECK_PTR_LOAD(0, pipes, 2 * sizeof(int)); + return pipe(pipes); +} + +__RT_VISIBILITY +int softboundcets_pipe2(int *pipes, int flags) { + CHECK_PTR_LOAD(0, pipes, 2 * sizeof(int)); + return pipe2(pipes, flags); +} + +__RT_VISIBILITY +ssize_t softboundcets_pread(int fd, void *buf, size_t nbytes, off_t offset) { + CHECK_PTR_STORE(0, buf, nbytes); + return pread(fd, buf, nbytes, offset); +} + +__RT_VISIBILITY +ssize_t softboundcets_pread64(int fd, void *buf, size_t nbytes, off64_t offset) { + CHECK_PTR_STORE(0, buf, nbytes); + return pread64(fd, buf, nbytes, offset); +} + +__RT_VISIBILITY +int softboundcets_profil(unsigned short *buf, size_t bufsize, size_t offset, unsigned int scale) { + // Note: buf is stored in libc and must be valid until the function is called again, I think + CHECK_PTR_STORE(0, buf, bufsize); + return profil(buf, bufsize, offset, scale); +} + +__RT_VISIBILITY +ssize_t softboundcets_pwrite(int fd, void *buf, size_t n, off_t offset) { + CHECK_PTR_LOAD(0, buf, n); + return pwrite(fd, buf, n, offset); +} + +__RT_VISIBILITY +ssize_t softboundcets_pwrite64(int fd, void *buf, size_t n, off64_t offset) { + CHECK_PTR_LOAD(0, buf, n); + return pwrite64(fd, buf, n, offset); +} + +__RT_VISIBILITY +ssize_t softboundcets_readlink(char *path, char *buf, size_t len) { + CHECK_STRING_LOAD(0, path); + CHECK_PTR_STORE(1, buf, len); + return readlink(path, buf, len); +} + +__RT_VISIBILITY +int softboundcets_revoke(char *file) { + CHECK_STRING_LOAD(0, file); + return revoke(file); +} + +__RT_VISIBILITY +void *softboundcets_sbrk(intptr_t delta) { + void *result = sbrk(delta); + if ((intptr_t)result == (intptr_t)-1) { + // Note: Since NULL is actually a valid pointer here, -1 is the null pointer essentially + __softboundcets_store_null_return_metadata(); + } else { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, (void*)((intptr_t)result + delta), result_key, result_lock); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_setdomainname(char *name, size_t len) { + CHECK_PTR_LOAD(0, name, len); + return setdomainname(name, len); +} + +__RT_VISIBILITY +int softboundcets_sethostname(char *name, size_t len) { + CHECK_PTR_LOAD(0, name, len); + return sethostname(name, len); +} + +__RT_VISIBILITY +int softboundcets_setlogin(char *name) { + CHECK_STRING_LOAD(0, name); +} + +__RT_VISIBILITY +void softboundcets_swab(void *from, void *to, ssize_t n) { + if (n < 0) { + // Negative n is allowed, but the function does nothing in that case + // We can thus return early and not have to deal with passing a negative number to the checkers + return; + } + + CHECK_PTR_LOAD(0, from, n); + CHECK_PTR_STORE(1, to, n); + + // Note: I'll not copy over any metadata since the buffer is scrambled and any pointer will be + // invalid after. + + swab(from, to, n); +} + +__RT_VISIBILITY +int softboundcets_symlink(char *from, char *to) { + CHECK_STRING_LOAD(0, from); + CHECK_STRING_LOAD(1, to); + + return symlink(from, to); +} + +__RT_VISIBILITY +int softboundcets_truncate(char *file, off_t length) { + CHECK_STRING_LOAD(0, file); + + return truncate(file, length); +} + +__RT_VISIBILITY +int softboundcets_truncate64(char *file, off64_t length) { + CHECK_STRING_LOAD(0, file); + + return truncate64(file, length); +} + +__RT_VISIBILITY +char *softboundcets_ttyname(int fd) { + char *result = ttyname(fd); + + if (result) { + // From the man page: "The return pointer *may* point to static data", so I'll not do an + // allocation here. + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_ttyname_r(int fd, char *buf, size_t len) { + CHECK_PTR_STORE(0, buf, len); + + return ttyname_r(fd, buf, len); +} + +__RT_VISIBILITY +int softboundcets_acct(char *filename) { + CHECK_STRING_LOAD_NULLABLE(0, filename); + + return acct(filename); +} + +__RT_VISIBILITY +int softboundcets_getentropy(void *buffer, size_t len) { + CHECK_PTR_STORE(0, buffer, len); + + return getentropy(buffer, len); +} + +// Wrappers for stdlib.h + +__RT_VISIBILITY +long softboundcets_a64l(char *s) { + CHECK_STRING_LOAD(0, s); + + return a64l(s); +} + +__RT_VISIBILITY +void *softboundcets_aligned_alloc(size_t alignment, size_t size) { + void *result = aligned_alloc(alignment, size); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, (char*)result + size, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +/** +__RT_VISIBILITY +void softboundcets_arc4random_buf(void *buf, size_t size) { + CHECK_PTR_STORE(0, buf, size); + + arc4random_buf(buf, size); +} +*/ + +__RT_VISIBILITY +int softboundcets_at_quick_exit(void (*func)(void)) { + CHECK_PTR_LOAD(0, func, 0); + + at_quick_exit(func); +} + +__RT_VISIBILITY +long long softboundcets_atoll(char *nptr) { + CHECK_STRING_LOAD(0, nptr); + + return atoll(nptr); +} + +__RT_VISIBILITY +char *softboundcets_canonicalize_file_name(char *name) { + CHECK_STRING_LOAD(1, name); + + char *result = canonicalize_file_name(name); + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + + __softboundcets_memory_allocation(result, &result_lock, &result_key); + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +int softboundcets_drand48_r(struct drand48_data *buffer, double *result) { + CHECK_PTR_STORE(0, buffer, sizeof(struct drand48_data)); + CHECK_PTR_STORE(1, result, sizeof(double)); + + return drand48_r(buffer, result); +} + +__RT_VISIBILITY +char *softboundcets_ecvt(double value, int ndigits, int *decpt, int *sign) { + CHECK_PTR_STORE(1, decpt, sizeof(int)); + CHECK_PTR_STORE(1, sign, sizeof(int)); + + char *result = ecvt(value, ndigits, decpt, sign); + // I'm not sure this ever returns null, but just in case + if (result) { + // The result is allocated in a static buffer + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_ecvt_r(double value, int ndigits, int *decpt, int *sign, char *buf, size_t len) { + CHECK_PTR_STORE(0, decpt, sizeof(int)); + CHECK_PTR_STORE(1, sign, sizeof(int)); + CHECK_PTR_STORE(2, buf, len); + + return ecvt_r(value, ndigits, decpt, sign, buf, len); +} + +__RT_VISIBILITY +double softboundcets_erand48(unsigned short xsubi[3]) { + CHECK_PTR_STORE(0, xsubi, 3 * sizeof(unsigned short)); + + return erand48(xsubi); +} + +__RT_VISIBILITY +int softboundcets_erand48_r(unsigned short xsubi[3], struct drand48_data *buffer, double *result) { + CHECK_PTR_STORE(0, xsubi, sizeof(unsigned short) * 3); + CHECK_PTR_STORE(1, buffer, sizeof(struct drand48_data)); + CHECK_PTR_STORE(2, result, sizeof(double)); + + return erand48_r(xsubi, buffer, result); +} + +__RT_VISIBILITY +char *softboundcets_fcvt(double value, int ndigits, int *decpt, int *sign) { + CHECK_PTR_STORE(1, decpt, sizeof(int)); + CHECK_PTR_STORE(2, sign, sizeof(int)); + + char *result = fcvt(value, ndigits, decpt, sign); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_fcvt_r(double value, int ndigits, int *decpt, int *sign, char *buf, size_t len) { + CHECK_PTR_STORE(0, decpt, sizeof(int)); + CHECK_PTR_STORE(1, sign, sizeof(int)); + CHECK_PTR_STORE(2, buf, len); + + return fcvt_r(value, ndigits, decpt, sign, buf, len); +} + + +__RT_VISIBILITY +char *softboundcets_gcvt(double value, int ndigit, char *buf) { + CHECK_PTR_LOAD(1, buf, ndigit); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return gcvt(value, ndigit, buf); +} + +__RT_VISIBILITY +int softboundcets_getloadavg(double *loadavg, int nelem) { + CHECK_PTR_STORE(0, loadavg, nelem * sizeof(double)); + + return getloadavg(loadavg, nelem); +} + +__RT_VISIBILITY +int softboundcets_getsubopt(char **optionp, char **tokens, char **valuep) { + CHECK_PTR_STORE(0, optionp, sizeof(char*)); + CHECK_PTR_LOAD(1, tokens, sizeof(char*)); + CHECK_PTR_STORE(2, valuep, sizeof(char*)); + + // The optionp should point into a continous string -> make sure the string has a null terminator + char *option = *optionp; + sbcets_base_t option_base = __softboundcets_metadata_load_base(option); + sbcets_bound_t option_bound = __softboundcets_metadata_load_bound(option); + sbcets_lock_t option_lock = __softboundcets_metadata_load_lock(option); + sbcets_key_t option_key = __softboundcets_metadata_load_key(option); + + CHECK_PTR_ALIVE_LOAD_ONLY(option); + CHECK_STRING_BOUNDS_LOAD_ONLY(option); + + check_string_array(tokens, tokens_bound); + + int result = getsubopt(optionp, tokens, valuep); + + if (*valuep) { + // Since *valuep points into *optionp, we just copy over the metadata + __softboundcets_metadata_store(*valuep, option_base, option_bound, option_key, option_lock); + } +} + +// Since the initstate function returns the previous value, we have to keep track of its metadata +// Note: This is set up to return universally valid metadata for the first call since +// we do not know the location of the initial state's internal buffer in glibc +static size_t _initstate_size = 32; +static sbcets_base_t _initstate_base = 0; +static sbcets_bound_t _initstate_bound = (void*)281474976710656; +static sbcets_lock_t _initstate_lock = __softboundcets_global_lock; +static sbcets_key_t _initstate_key = 1; + +__RT_VISIBILITY +char *softboundcets_initstate(unsigned int seed, char *state, size_t n) { + CHECK_PTR_LOAD(1, state, n); + + char *result = initstate(seed, state, n); + + if (result) { + __softboundcets_store_return_metadata(_initstate_base, _initstate_bound, _initstate_key, + _initstate_lock); + + _initstate_size = n; + _initstate_base = state_base; + _initstate_bound = state_bound; + _initstate_lock = state_lock; + _initstate_key = state_key; + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_initstate_r(unsigned int seed, char *state, size_t n, struct random_data *buf) { + CHECK_PTR_LOAD(0, state, n); + CHECK_PTR_STORE(1, buf, sizeof(struct random_data)); + + return initstate_r(seed, state, n, buf); +} + +__RT_VISIBILITY +long softboundcets_jrand48(unsigned short xsubi[3]) { + CHECK_PTR_STORE(0, xsubi, sizeof(unsigned short) * 3); + + return jrand48(xsubi); +} + +__RT_VISIBILITY +int softboundcets_jrand48_r(unsigned short xsubi[3], struct drand48_data *buf, long *result) { + CHECK_PTR_STORE(0, xsubi, sizeof(unsigned short) * 3); + CHECK_PTR_STORE(1, buf, sizeof(struct drand48_data)); + CHECK_PTR_STORE(2, result, sizeof(long)); + + return jrand48_r(xsubi, buf, result); +} + +__RT_VISIBILITY +char *softboundcets_l64a(long n) { + char *result = l64a(n); + + // I'm not sure if this ever returns null, but better to be sure + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +void softboundcets_lcong48(unsigned short param[7]) { + CHECK_PTR_LOAD(0, param, sizeof(unsigned short) * 7); + + lcong48(param); +} + +__RT_VISIBILITY +int softboundcets_lcong48_r(unsigned short param[7], struct drand48_data *buf) { + CHECK_PTR_LOAD(0, param, sizeof(unsigned short) * 7); + CHECK_PTR_STORE(1, buf, sizeof(struct drand48_data)); + + return lcong48_r(param, buf); +} + +__RT_VISIBILITY +int softboundcets_lrand48_r(struct drand48_data *buf, long *result) { + CHECK_PTR_STORE(0, buf, sizeof(struct drand48_data)); + CHECK_PTR_STORE(1, result, sizeof(long)); + + return lrand48_r(buf, result); +} + +__RT_VISIBILITY +int softboundcets_mblen(char *s, size_t n) { + CHECK_PTR_LOAD_NULLABLE(0, s, n); + + return mblen(s, n); +} + +__RT_VISIBILITY +size_t softboundcets_mbstowcs(wchar_t *dest, char *src, size_t dest_size) { + CHECK_PTR_BOUNDS_STORE_NULLABLE(0, dest, dest_size); + // While src is a multibyte-encoded string, it still is terminated by NULL + CHECK_STRING_BOUNDS_LOAD(1, src); + + return mbstowcs(dest, src, dest_size); +} + +__RT_VISIBILITY +int softboundcets_mbtowc(wchar_t *dest, char *src, size_t n) { + CHECK_PTR_STORE_NULLABLE(0, dest, sizeof(wchar_t)); + CHECK_PTR_LOAD_NULLABLE(1, src, n); + + return mbtowc(dest, src, n); +} + +__RT_VISIBILITY +int softboundcets_mkostemp(char *path_template, int flags) { + CHECK_STRING_LOAD(0, path_template); + + return mkostemp(path_template, flags); +} + +__RT_VISIBILITY +int softboundcets_mkostemp64(char *path_template, int flags) { + CHECK_STRING_LOAD(0, path_template); + + return mkostemp64(path_template, flags); +} + +__RT_VISIBILITY +int softboundcets_mkostemps(char *path_template, int suffixlen, int flags) { + CHECK_STRING_LOAD(0, path_template); + + return mkostemps(path_template, suffixlen, flags); +} + +__RT_VISIBILITY +int softboundcets_mkostemps64(char *path_template, int suffixlen, int flags) { + CHECK_STRING_LOAD(0, path_template); + + return mkostemps64(path_template, suffixlen, flags); +} + +__RT_VISIBILITY +int softboundcets_mkstemp64(char *path_template) { + CHECK_STRING_LOAD(0, path_template); + + return mkstemp64(path_template); +} + +__RT_VISIBILITY +int softboundcets_mkstemps(char *path_template, int suffixlen) { + CHECK_STRING_LOAD(0, path_template); + + return mkstemps(path_template, suffixlen); +} + +__RT_VISIBILITY +int softboundcets_mkstemps64(char *path_template, int suffixlen) { + CHECK_STRING_LOAD(0, path_template); + + return mkstemps64(path_template, suffixlen); +} + + +__RT_VISIBILITY +char *softboundcets_mktemp(char *path_template) { + CHECK_STRING_STORE(1, path_template); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + + return mktemp(path_template); +} + +__RT_VISIBILITY +int softboundcets_mrand48_r(struct drand48_data *buf, long *result) { + CHECK_PTR_STORE(0, buf, sizeof(struct drand48_data)); + CHECK_PTR_STORE(1, result, sizeof(long)); + + return mrand48_r(buf, result); +} + +__RT_VISIBILITY +long softboundcets_nrand48(unsigned short xsubi[3]) { + CHECK_PTR_STORE(0, xsubi, sizeof(unsigned short) * 3); + + return nrand48(xsubi); +} + +__RT_VISIBILITY +int softboundcets_nrand48_r(unsigned short xsubi[3], struct drand48_data *buf, long *result) { + CHECK_PTR_STORE(0, xsubi, sizeof(unsigned short) * 3); + CHECK_PTR_STORE(1, buf, sizeof(struct drand48_data)); + CHECK_PTR_STORE(2, result, sizeof(long)); + + return nrand48_r(xsubi, buf, result); +} + +__RT_VISIBILITY +int softboundcets_on_exit(void (*func)(int status, void *arg), void *arg) { + CHECK_PTR_LOAD(0, func, 0); + // Note: Arg should be valid when the on_exit function is called. + // TODO: We could wrap the callback as well, but that would probably require us to leak memory + CHECK_PTR_LOAD(1, arg, 0); + + return on_exit(func, arg); +} + +__RT_VISIBILITY +int softboundcets_posix_memalign(void **ptr, size_t alignment, size_t size) { + CHECK_PTR_STORE(0, ptr, sizeof(void*)); + + int result = posix_memalign(ptr, alignment, size); + if (result == 0) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + + __softboundcets_memory_allocation(*ptr, &result_lock, &result_key); + __softboundcets_metadata_store(*ptr, *ptr, (char*)*ptr + size, result_key, result_lock); + } + + return result; +} + +__RT_VISIBILITY +char *softboundcets_ptsname(int fd) { + char *result = ptsname(fd); + + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_ptsname_r(int fd, char *buf, size_t n) { + CHECK_PTR_STORE(0, buf, n); + + return ptsname_r(fd, buf, n); +} + +__RT_VISIBILITY +int softboundcets_putenv(char *string) { + CHECK_STRING_LOAD(0, string); + + return putenv(string); +} + +__RT_VISIBILITY +char *softboundcets_qecvt(long double value, int ndigit, int *decpt, int *sign) { + CHECK_PTR_STORE(1, decpt, sizeof(int)); + CHECK_PTR_STORE(2, sign, sizeof(int)); + + char *result = qecvt(value, ndigit, decpt, sign); + + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_qecvt_r(long double value, int ndigit, int *decpt, int *sign, char *buf, size_t n) { + CHECK_PTR_STORE(0, decpt, sizeof(int)); + CHECK_PTR_STORE(1, sign, sizeof(int)); + CHECK_PTR_STORE(2, buf, n); + + return qecvt_r(value, ndigit, decpt, sign, buf, n); +} + +__RT_VISIBILITY +char *softboundcets_qfcvt(long double value, int ndigit, int *decpt, int *sign) { + CHECK_PTR_STORE(1, decpt, sizeof(int)); + CHECK_PTR_STORE(2, sign, sizeof(int)); + + char *result = qfcvt(value, ndigit, decpt, sign); + + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +int softboundcets_qfcvt_r(long double value, int ndigit, int *decpt, int *sign, char *buf, size_t n) { + CHECK_PTR_STORE(0, decpt, sizeof(int)); + CHECK_PTR_STORE(1, sign, sizeof(int)); + CHECK_PTR_STORE(2, buf, n); + + return qfcvt_r(value, ndigit, decpt, sign, buf, n); +} + +__RT_VISIBILITY +char *softboundcets_qgcvt(long double value, int ndigit, char *buf) { + CHECK_PTR_STORE(1, buf, ndigit); + __softboundcets_propagate_metadata_shadow_stack_from(1, 0); + return qgcvt(value, ndigit, buf); +} + +/* + * We use heapsort for qsort_r, since it provides nlogn runtime complexity while not requiring + * memory allocations on the heap or the stack. + * Glibc uses mergesort and falls back to heapsort in case the allocator fails, which would be + * better for performance but I do not want to implement two sorting algorithms. + * + * We build a max heap in-place in the array we have to sort. + * After that, we put the largest (root) element at the back and end up with an array sorted in + * ascending order. + * The heap is a binary tree where for index i, its children are at 2 * i + 1 and 2 * i + 2. + */ + +typedef struct { + sbcets_base_t array_base; + sbcets_bound_t array_bound; + sbcets_lock_t array_lock; + sbcets_key_t array_key; + + sbcets_base_t arg_base; + sbcets_bound_t arg_bound; + sbcets_lock_t arg_lock; + sbcets_key_t arg_key; +} _qsort_r_meta; + + +typedef int (*_qsort_r_cmp_fn)(void *left, void *right, void *arg); + +__attribute__((always_inline)) +int _qsort_r_call_cmp(_qsort_r_cmp_fn cmp, void *left, void *right, void *arg, + _qsort_r_meta meta) { + __softboundcets_allocate_shadow_stack_space(3); + + // Since both left and right point into the same array, we pass the arrays metadata here + __softboundcets_store_base_shadow_stack(meta.array_base, 0); + __softboundcets_store_bound_shadow_stack(meta.array_bound, 0); + __softboundcets_store_lock_shadow_stack(meta.array_lock, 0); + __softboundcets_store_key_shadow_stack(meta.array_key, 0); + + __softboundcets_store_base_shadow_stack(meta.array_base, 1); + __softboundcets_store_bound_shadow_stack(meta.array_bound, 1); + __softboundcets_store_lock_shadow_stack(meta.array_lock, 1); + __softboundcets_store_key_shadow_stack(meta.array_key, 1); + + __softboundcets_store_base_shadow_stack(meta.arg_base, 2); + __softboundcets_store_bound_shadow_stack(meta.arg_bound, 2); + __softboundcets_store_lock_shadow_stack(meta.arg_lock, 2); + __softboundcets_store_key_shadow_stack(meta.arg_key, 2); + + int result = cmp(left, right, arg); + + __softboundcets_deallocate_shadow_stack_space(); + + return result; +} + +__attribute__((always_inline)) +void _qsort_r_swap_element(char *ptr1, char *ptr2, size_t size, bool is_ptr) { + // We could probably make this more efficient by copying in word-size steps but oh well + for (size_t i = 0; i < size; i++) { + char swap = ptr1[i]; + ptr1[i] = ptr2[i]; + ptr2[i] = swap; + } + + if (is_ptr) { + for (size_t i = 0; i < size; i += sizeof(void*)) { + __softboundcets_metadata_t *ptr1_meta = __softboundcets_shadowspace_metadata_ptr_create_secondary_tries(&ptr1[i]); + __softboundcets_metadata_t *ptr2_meta = __softboundcets_shadowspace_metadata_ptr_create_secondary_tries(&ptr2[i]); + + sbcets_base_t ptr1_base = nullptr; + sbcets_bound_t ptr1_bound = nullptr; + sbcets_lock_t ptr1_lock = nullptr; + sbcets_key_t ptr1_key = 0; + + sbcets_base_t ptr2_base = nullptr; + sbcets_bound_t ptr2_bound = nullptr; + sbcets_lock_t ptr2_lock = nullptr; + sbcets_key_t ptr2_key = 0; + + // We have to feature gate these assignments since the __softboundcets_metadata_t struct is + // defined differently based on the flags +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + ptr1_base = ptr1_meta->base; + ptr1_bound = ptr1_meta->bound; + + ptr2_base = ptr2_meta->base; + ptr2_bound = ptr2_meta->bound; +#endif + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + ptr1_lock = ptr1_meta->lock; + ptr1_key = ptr1_meta->key; + + ptr2_lock = ptr2_meta->lock; + ptr2_key = ptr2_meta->key; +#endif + + __softboundcets_metadata_store(&ptr1[i], ptr1_base, ptr1_bound, ptr1_key, ptr1_lock); + __softboundcets_metadata_store(&ptr2[i], ptr2_base, ptr2_bound, ptr2_key, ptr2_lock); + } + } +} + +__attribute__((always_inline)) +void _qsort_r_sift_down(char *array, size_t nmemb, size_t size, _qsort_r_cmp_fn cmp, + void *arg, bool is_ptr, size_t parent, _qsort_r_meta meta) { + while (2 * parent + 1 < nmemb) { + // We select the left child first, since it has to exist + size_t child = 2 * parent + 1; + + // If the right child exists and is larger, consieder it instead + if (child + 1 < nmemb && _qsort_r_call_cmp(cmp, &array[child * size], + &array[(child + 1) * size], arg, meta) < 0) { + child++; + } + + // Now we compare the larger child with the parent and swap them if larger + // Since we swapped with the larger child, the heap property is valid at the current element + if (_qsort_r_call_cmp(cmp, &array[parent * size], &array[child * size], arg, meta) < 0) { + _qsort_r_swap_element(&array[parent * size], &array[child * size], size, is_ptr); + // Since we swapped the child element with a smaller value, we may have broken the heap + // property. + // To fix this, we attempt a swap at the child index again. + parent = child; + } else { + // Since we did nothing, we can stop + break; + } + } +} + +__attribute__((always_inline)) +void _qsort_r_heapify(char *array, size_t nmemb, size_t size, _qsort_r_cmp_fn cmp, + void *arg, bool is_ptr, _qsort_r_meta meta) { + // The start of the heap is the parent of the last element + for (size_t start = nmemb / 2; start > 0; start--) { + _qsort_r_sift_down(array, nmemb, size, cmp, arg, is_ptr, start - 1, meta); + } +} + +__RT_VISIBILITY +void softboundcets_qsort_r(void *base, size_t nmemb, size_t size, _qsort_r_cmp_fn cmp, void *arg) { + CHECK_PTR_STORE(0, base, nmemb * size); + CHECK_PTR_LOAD(1, cmp, 0); + CHECK_PTR_LOAD_NULLABLE(2, arg, 0); + + bool is_ptr = false; + char *array = (char*)base; + + _qsort_r_meta meta = { + .array_base = base_base, + .array_bound = base_bound, + .array_lock = base_lock, + .array_key = base_key, + + .arg_base = arg_base, + .arg_bound = arg_bound, + .arg_lock = arg_lock, + .arg_key = arg_key + }; + + // NOTE: + // We first check if the data to sort contains pointers. If so, we have to swap metadata as well. + // We assume that data is homogeneous and check the first element for pointers by looking for + // secondary tries. + // Note that this can lead to false positives/negatives when our assumption is incorrect and/or + // a non-pointer value would also be a valid pointer. + // We could also check for trie entries in every swap for correctness at the cost of performance + for (size_t i = 0; i < size; i += sizeof(void*)) { + // Reinterpret the array as a pointer-sized array and get the potential trie index + size_t primary_index = ((size_t*)array)[i] >> 25; + // Check if the primary trie contains a secondary tree for this element + // If it does, assume this is a pointer + // TODO: Is this reasonable? + __softboundcets_metadata_t *secondary_index = __softboundcets_trie_primary_table[primary_index]; + if (secondary_index != nullptr) { + is_ptr = true; + break; + } + } + + // We start by constructing a heap in the array + _qsort_r_heapify(array, nmemb, size, cmp, arg, is_ptr, meta); + + while (nmemb > 0) { + // Since we have a max-heap, the first (root) element is always the maximum + nmemb--; + _qsort_r_swap_element(&array[0], &array[nmemb * size], size, is_ptr); + + // Restore the heap property + _qsort_r_sift_down(array, nmemb, size, cmp, arg, is_ptr, 0, meta); + } +} + +__RT_VISIBILITY +int softboundcets_rand_r(unsigned int *seed) { + CHECK_PTR_STORE(0, seed, sizeof(int)); + + return rand_r(seed); +} + +__RT_VISIBILITY +int softboundcets_random_r(struct random_data *buf, int32_t *result) { + CHECK_PTR_STORE(0, buf, sizeof(struct random_data)); + CHECK_PTR_STORE(1, result, sizeof(int32_t)); + + return random_r(buf, result); +} + +__RT_VISIBILITY +char *softboundcets_realpath(char *name, char *resolved) { + CHECK_STRING_LOAD(1, name); + CHECK_PTR_STORE_NULLABLE(2, resolved, PATH_MAX); + + char *result = realpath(name, resolved); + if (result) { + // This function can optionally allocate if the resolved parameter is null + if (resolved) { + __softboundcets_propagate_metadata_shadow_stack_from(2, 0); + } else { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + // TODO: Check the allocation again + __softboundcets_store_return_metadata(result, result + PATH_MAX, result_key, result_lock); + } + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +char *softboundcets_secure_getenv(char *name) { + CHECK_STRING_LOAD(1, name); + + char *result = secure_getenv(name); + if (result) { + __softboundcets_store_return_metadata(result, result + strlen(result) + 1, 1, + __softboundcets_global_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + return result; +} + +__RT_VISIBILITY +unsigned short *softboundcets_seed48(unsigned short seed[3]) { + CHECK_PTR_LOAD(1, seed, sizeof(unsigned short) * 3); + + unsigned short *result = seed48(seed); + // seed48 stores the previous buffer in a static buffer that is returned + __softboundcets_store_return_metadata(result, result + sizeof(unsigned short) * 3, 1, + __softboundcets_global_lock); + + return result; +} + +__RT_VISIBILITY +int softboundcets_seed48_r(unsigned short seed[3], struct drand48_data *buf) { + CHECK_PTR_LOAD(0, seed, sizeof(unsigned short) * 3); + CHECK_PTR_STORE(1, buf, sizeof(struct drand48_data)); + + return seed48_r(seed, buf); +} + +__RT_VISIBILITY +char *softboundcets_setstate(char *state) { + // Note: This is linked to initstate, where the bound value can be changed + CHECK_PTR_LOAD(1, state, _initstate_size); + + char *result = setstate(state); + + __softboundcets_store_return_metadata(_initstate_base, _initstate_bound, _initstate_key, + _initstate_lock); + + _initstate_base = state_base; + _initstate_bound = state_bound; + _initstate_lock = state_lock; + _initstate_key = state_key; + + return result; +} + +__RT_VISIBILITY +int softboundcets_setstate_r(char *state, struct random_data *buf) { + // TODO: What size? + CHECK_PTR_LOAD(0, state, 0); + CHECK_PTR_STORE(1, buf, sizeof(struct random_data)); + + return setstate_r(state, buf); +} + +__RT_VISIBILITY +int softboundcets_srand48_r(long seed, struct drand48_data *buffer) { + CHECK_PTR_STORE(0, buffer, sizeof(drand48_data)); + + return srand48_r(seed, buffer); +} + +__RT_VISIBILITY +int softboundcets_srandom_r(unsigned int seed, struct random_data *buf) { + CHECK_PTR_STORE(0, buf, sizeof(struct random_data)); + + return srandom_r(seed, buf); +} + +__RT_VISIBILITY +int softboundcets_strfromd(char *dest, size_t size, char *format, double f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromd(dest, size, format, f); +} + +__RT_VISIBILITY +int softboundcets_strfromf(char *dest, size_t size, char *format, float f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf(dest, size, format, f); +} + +/* +__RT_VISIBILITY +int softboundcets_strfromf128(char *dest, size_t size, char *format, _Float128 f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf128(dest, size, format, f); +} +*/ + +__RT_VISIBILITY +int softboundcets_strfromf32(char *dest, size_t size, char *format, _Float32 f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf32(dest, size, format, f); +} + +__RT_VISIBILITY +int softboundcets_strfromf32x(char *dest, size_t size, char *format, _Float32x f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf32x(dest, size, format, f); +} + +__RT_VISIBILITY +int softboundcets_strfromf64(char *dest, size_t size, char *format, _Float64 f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf64(dest, size, format, f); +} + +__RT_VISIBILITY +int softboundcets_strfromf64x(char *dest, size_t size, char *format, _Float64x f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfromf64x(dest, size, format, f); +} + +__RT_VISIBILITY +int softboundcets_strfroml(char *dest, size_t size, char *format, long double f) { + CHECK_PTR_STORE(0, dest, size); + CHECK_STRING_LOAD(1, format); + + return strfroml(dest, size, format, f); +} + +__RT_VISIBILITY +double softboundcets_strtod_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtod_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +float softboundcets_strtof(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof(nptr, endptr); +} + +/* +__RT_VISIBILITY +_Float128 softboundcets_strtof128(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof128(nptr, endptr); +} + +__RT_VISIBILITY +_Float128 softboundcets_strtof128_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof128_l(nptr, endptr, locale); +} +*/ + +__RT_VISIBILITY +_Float32 softboundcets_strtof32(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof32(nptr, endptr); +} + +__RT_VISIBILITY +_Float32 softboundcets_strtof32_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof32_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +_Float32x softboundcets_strtof32x(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof32x(nptr, endptr); +} + +__RT_VISIBILITY +_Float32x softboundcets_strtof32x_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof32x_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +_Float64 softboundcets_strtof64(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof64(nptr, endptr); +} + +__RT_VISIBILITY +_Float64 softboundcets_strtof64_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof64_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +_Float64x softboundcets_strtof64x(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof64x(nptr, endptr); +} + +__RT_VISIBILITY +_Float64x softboundcets_strtof64x_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof64x_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +float softboundcets_strtof_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtof_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +long softboundcets_strtol_l(char *nptr, char **endptr, int base, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtol_l(nptr, endptr, base, locale); +} + +__RT_VISIBILITY +long double softboundcets_strtold(char *nptr, char **endptr) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtold(nptr, endptr); +} + +__RT_VISIBILITY +long double softboundcets_strtold_l(char *nptr, char **endptr, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtold_l(nptr, endptr, locale); +} + +__RT_VISIBILITY +long long softboundcets_strtoll(char *nptr, char **endptr, int base) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoll(nptr, endptr, base); +} + +__RT_VISIBILITY +long long softboundcets_strtoll_l(char *nptr, char **endptr, int base, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoll_l(nptr, endptr, base, locale); +} + +__RT_VISIBILITY +long long softboundcets_strtoq(char *nptr, char **endptr, int base) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoq(nptr, endptr, base); +} + +__RT_VISIBILITY +unsigned long softboundcets_strtoul_l(char *nptr, char **endptr, int base, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoul_l(nptr, endptr, base, locale); +} + +__RT_VISIBILITY +unsigned long long softboundcets_strtoull(char *nptr, char **endptr, int base) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoull(nptr, endptr, base); +} + +__RT_VISIBILITY +unsigned long long softboundcets_strtoull_l(char *nptr, char **endptr, int base, locale_t locale) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtoull_l(nptr, endptr, base, locale); +} + +__RT_VISIBILITY +unsigned long long softboundcets_strtouq(char *nptr, char **endptr, int base) { + CHECK_STRING_LOAD(0, nptr); + CHECK_PTR_STORE_NULLABLE(1, endptr, sizeof(char*)); + + if (endptr != nullptr) { + // If not null, endptr will point into the same buffer as nptr, even on error + __softboundcets_metadata_store(*endptr, nptr_base, nptr_bound, nptr_key, nptr_lock); + } + + return strtouq(nptr, endptr, base); +} + +__RT_VISIBILITY +size_t softboundcets_wcstombs(char *s, wchar_t *wstr, size_t n) { + CHECK_PTR_STORE_NULLABLE(0, s, n); + LOAD_PTR_BOUNDS(1, wstr); + +#if __SOFTBOUNDCETS_CHECK_LOADS + if (!wmemchr(wstr, L'\0', (wchar_t*)wstr_bound - (wchar_t*)wstr)) { + __softboundcets_error_printf("In string load dereference check: base=%zx, bound=%zx, ptr=%zx", + wstr_base, wstr_bound, wstr); + __softboundcets_abort(); + } +#endif + CHECK_PTR_ALIVE_LOAD(1, wstr); + + return wcstombs(s, wstr, n); +} + +__RT_VISIBILITY +int softboundcets_wctomb(char *s, wchar_t wchar) { + CHECK_PTR_LOAD_NULLABLE(0, s, MB_CUR_MAX); + + return wctomb(s, wchar); +} + +__RT_VISIBILITY +void *softboundcets_reallocarray(void *ptr, size_t nmemb, size_t size) { + LOAD_PTR_BOUNDS(1, ptr); + LOAD_PTR_LOCK(1, ptr); + // We do not check the pointer here since the allocator has its own metadata for that + + void *result = reallocarray(ptr, nmemb, size); + + if (result) { + sbcets_base_t result_base = result; + // This cannot overflow since reallocarray succeeded + sbcets_bound_t result_bound = (char*)result + nmemb * size; + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + if (ptr != result) { + // We have reallocated + if (ptr) { + // Since ptr was not null, we must deallocate it + __softboundcets_memory_deallocation(ptr_lock, ptr_key); + } + __softboundcets_memory_allocation(result, &result_lock, &result_key); + } else { + result_lock = ptr_lock; + result_key = ptr_key; + + // Since the pointer was not moved, we need to update the bounds + // TODO: Updating this on the shadow stack is correct, right? + __softboundcets_store_bound_shadow_stack(result_bound, 1); + } + + __softboundcets_store_return_metadata(result_base, result_bound, result_key, result_lock); + } else { + // If the reallocarray function returned NULL, the pointer may have been free'd if size was 0. + // We also check if the pointer has a key value > 1 (0: not allocated, 1: global lock) in case + // someone tried to deallocate a static variable. + // TODO: Is this sufficient to check for errors? + if (size == 0 && ptr_key > 1) { + __softboundcets_memory_deallocation(ptr_lock, ptr_key); + } + + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +// malloc.h + +__RT_VISIBILITY +int softboundcets_malloc_info(int options, FILE *fp) { + CHECK_PTR_LOAD(0, fp, sizeof(FILE)); + + return malloc_info(options, fp); +} + +__RT_VISIBILITY +size_t softboundcets_malloc_usable_size(void *ptr) { + CHECK_PTR_ALIVE_LOAD_NULLABLE(0, ptr); + + return malloc_usable_size(ptr); +} + +__RT_VISIBILITY +void *softboundcets_memalign(size_t alignment, size_t size) { + void *result = memalign(alignment, size); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + + __softboundcets_store_return_metadata(result, (char*)result + size, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +void *softboundcets_pvalloc(size_t size) { + void *result = pvalloc(size); + + size_t page_size = sysconf(_SC_PAGESIZE); + size_t real_size = size; + + // I'm not sure if this can ever be zero, but just in case... + if (page_size) { + // Round up to next multiple of page size + real_size = ((size + page_size - 1) / page_size) * page_size; + } + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + + __softboundcets_store_return_metadata(result, (char*)result + real_size, result_key, + result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} + +__RT_VISIBILITY +void *softboundcets_valloc(size_t size) { + void *result = valloc(size); + + if (result) { + sbcets_lock_t result_lock = nullptr; + sbcets_key_t result_key = 0; + __softboundcets_memory_allocation(result, &result_lock, &result_key); + + __softboundcets_store_return_metadata(result, (char*)result + size, result_key, result_lock); + } else { + __softboundcets_store_null_return_metadata(); + } + + return result; +} diff --git a/compiler-rt/lib/softboundcets/softboundcets.cpp b/compiler-rt/lib/softboundcets/softboundcets.cpp new file mode 100644 index 000000000000..13d096f5ee68 --- /dev/null +++ b/compiler-rt/lib/softboundcets/softboundcets.cpp @@ -0,0 +1,1497 @@ +//=== softboundcets.c - Creates the main function for SoftBound+CETS Runtime +//--*- C -*===// +// Copyright (c) 2014 Santosh Nagarakatte, Milo M. K. Martin. All rights +// reserved. + +// Developed by: Santosh Nagarakatte, +// Department of Computer Science, Rutgers University +// https://github.com/santoshn/softboundcets-34/ +// http://www.cs.rutgers.edu/~santosh.nagarakatte/ +// +// in collaboration with +// +// Milo M.K. Martin, Jianzhou Zhao, Steve Zdancewic +// Department of Computer and Information Sciences, +// University of Pennsylvania + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin, +// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, nor +// the names of its contributors may be used to endorse or promote +// products derived from this Software without specific prior +// written permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. +//===---------------------------------------------------------------------===// + +#include +#include +#include +#include +#if defined(__linux__) +#include +#endif +#include +#include +#include +#include +#if !defined(__FreeBSD__) +#include +#endif +#include "softboundcets.h" + +__softboundcets_metadata_t **__softboundcets_trie_primary_table; + +size_t *__softboundcets_free_map_table = NULL; + +size_t *__softboundcets_shadow_stack_ptr = NULL; + +size_t *__softboundcets_lock_next_location = NULL; +size_t *__softboundcets_lock_new_location = NULL; +size_t __softboundcets_key_id_counter = 2; + +/* key 0 means not used, 1 is for globals*/ +size_t __softboundcets_deref_check_count = 0; +size_t *__softboundcets_global_lock = 0; + +size_t *__softboundcets_temporal_space_begin = 0; +size_t *__softboundcets_stack_temporal_space_begin = NULL; + +void *malloc_address = NULL; + +void softboundcets_init_ctype(void) { +#if defined(__linux__) + + unsigned short const **ct_b_loc = __ctype_b_loc(); + unsigned short *ct_b_loc_base = *(unsigned short **)ct_b_loc; + + int const **ct_toupper_loc = __ctype_toupper_loc(); + int *ct_toupper_loc_base = *(int **)ct_toupper_loc; + + int const **ct_tolower_loc = __ctype_tolower_loc(); + int *ct_tolower_loc_base = *(int **)ct_tolower_loc; + + // __softboundcets_allocation_secondary_trie_allocate(ct_b_loc_base); + // __softboundcets_allocation_secondary_trie_allocate(ct_toupper_loc); + +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store(ct_b_loc, (ct_b_loc_base - 128), + (ct_b_loc_base + 256)); + __softboundcets_metadata_store(ct_toupper_loc, (ct_toupper_loc_base - 128), + (ct_toupper_loc_base + 256)); + __softboundcets_metadata_store(ct_tolower_loc, (ct_tolower_loc_base - 128), + (ct_tolower_loc_base + 256)); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store(ct_b_loc, 1, __softboundcets_global_lock); + __softboundcets_metadata_store(ct_toupper_loc, 1, + __softboundcets_global_lock); + __softboundcets_metadata_store(ct_tolower_loc, 1, + __softboundcets_global_lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store(ct_b_loc, (ct_b_loc_base - 128), + (ct_b_loc_base + 256), 1, + __softboundcets_global_lock); + __softboundcets_metadata_store(ct_toupper_loc, (ct_toupper_loc_base - 128), + (ct_toupper_loc_base + 256), 1, + __softboundcets_global_lock); + __softboundcets_metadata_store(ct_tolower_loc, (ct_tolower_loc_base - 128), + (ct_tolower_loc_base + 256), 1, + __softboundcets_global_lock); +#endif + +#endif // End of __linux__ +} + +void __softboundcets_init(void) { + if (softboundcets_initialized != 0) { + return; // already initialized, do nothing + } + + softboundcets_initialized = 1; + +#if __WORDSIZE == 32 + exit(1); +#endif + + __softboundcets_log_message(LOG_LEVEL_INFO, + "Initializing softboundcets metadata space\n"); + + static_assert(sizeof(__softboundcets_metadata_t) >= 16, ""); + + /* Allocating the temporal shadow space */ + + size_t temporal_table_length = + (__SOFTBOUNDCETS_N_TEMPORAL_ENTRIES) * sizeof(void *); + + __softboundcets_lock_new_location = + (size_t *)mmap(0, temporal_table_length, PROT_READ | PROT_WRITE, + SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + + assert(__softboundcets_lock_new_location != (void *)-1); + __softboundcets_temporal_space_begin = + (size_t *)__softboundcets_lock_new_location; + + size_t stack_temporal_table_length = + (__SOFTBOUNDCETS_N_STACK_TEMPORAL_ENTRIES) * sizeof(void *); + __softboundcets_stack_temporal_space_begin = + (size_t *)mmap(0, stack_temporal_table_length, PROT_READ | PROT_WRITE, + SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + assert(__softboundcets_stack_temporal_space_begin != (void *)-1); + + size_t global_lock_size = + (__SOFTBOUNDCETS_N_GLOBAL_LOCK_SIZE) * sizeof(void *); + __softboundcets_global_lock = + (size_t *)mmap(0, global_lock_size, PROT_READ | PROT_WRITE, + SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + assert(__softboundcets_global_lock != (void *)-1); + // __softboundcets_global_lock = __softboundcets_lock_new_location++; + *__softboundcets_global_lock = 1; + + size_t shadow_stack_size = + __SOFTBOUNDCETS_SHADOW_STACK_ENTRIES * sizeof(size_t); + __softboundcets_shadow_stack_ptr = + (size_t *)mmap(0, shadow_stack_size, PROT_READ | PROT_WRITE, + SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + assert(__softboundcets_shadow_stack_ptr != (void *)-1); + + *((size_t *)__softboundcets_shadow_stack_ptr) = 0; /* prev stack size */ + size_t *current_size_shadow_stack_ptr = __softboundcets_shadow_stack_ptr + 1; + *(current_size_shadow_stack_ptr) = 0; + + if (__SOFTBOUNDCETS_FREE_MAP) { + size_t length_free_map = + (__SOFTBOUNDCETS_N_FREE_MAP_ENTRIES) * sizeof(size_t); + __softboundcets_free_map_table = + (size_t *)mmap(0, length_free_map, PROT_READ | PROT_WRITE, + SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + assert(__softboundcets_free_map_table != (void *)-1); + } + + size_t length_trie = (__SOFTBOUNDCETS_TRIE_PRIMARY_TABLE_ENTRIES) * + sizeof(__softboundcets_metadata_t *); + + __softboundcets_trie_primary_table = (__softboundcets_metadata_t **)mmap( + 0, length_trie, PROT_READ | PROT_WRITE, SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + assert(__softboundcets_trie_primary_table != (void *)-1); + + int *temp = (int *)malloc(1); + __softboundcets_allocation_secondary_trie_allocate_range(0, (size_t)temp); + + // printf("before init_ctype\n"); + softboundcets_init_ctype(); +} + +void __softboundcets_printf(const char *str, ...) { + va_list args; + + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); +} + +void __softboundcets_log_message(int level, const char *str, ...) { + va_list args; + if (level >= LOG_LEVEL) { + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); + } +} + +void __softboundcets_debug_printf(const char *str, ...) { +#if (LOG_LEVEL_DEBUG >= LOG_LEVEL) + va_list args; + + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); +#endif +} + +void __softboundcets_error_printf(const char *str, ...) { +#if (LOG_LEVEL_ERROR >= LOG_LEVEL) + va_list args; + + va_start(args, str); + vfprintf(stderr, str, args); + va_end(args); +#endif +} + +__attribute__((always_inline)) +#if __SOFTBOUNDCETS_CONTINUE_ON_ABORT +void __softboundcets_abort(void) { + volatile int dummy; + dummy = 1; +} +#else +__attribute__((__noreturn__)) void +__softboundcets_abort(void) { + fprintf(stderr, + "\nSoftboundcets: Memory safety violation detected\n\nBacktrace:\n"); + + // Based on code from the backtrace man page + size_t size; + void *array[100]; + +#if !defined(__FreeBSD__) + size = backtrace(array, 100); + backtrace_symbols_fd(array, size, fileno(stderr)); +#endif + + fprintf(stderr, "\n\n"); + + abort(); +} +#endif + +__attribute__((__weak__)) extern "C" int +__softboundcets_real_main(int argc, char *argv[], char *envp[]); + +int __softboundcets_wrap_main(int argc, char *argv[], char *envp[]) { + + int *temp = (int *)malloc(1); + malloc_address = temp; + __softboundcets_allocation_secondary_trie_allocate_range(0, (size_t)temp); + __softboundcets_allocation_secondary_trie_allocate_range(argv, + (size_t)argc * 8); + + // Create lock and key for argv. + size_t argv_key; + size_t *argv_lock; + __softboundcets_stack_memory_allocation(&argv_lock, &argv_key); + +#if defined(__linux__) + mallopt(M_MMAP_MAX, 0); +#endif + + // =========================================================================================== + // Create metadata for the environ pointer + + size_t environ_key; + size_t *environ_lock; + __softboundcets_stack_memory_allocation(&environ_lock, &environ_key); + + size_t environ_size = 0; + + // Create metadata for the environment environiables stored in environ. + char **env1; + for (env1 = environ; *env1; ++env1) { + environ_size++; +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store(env1, *env1, *env1 + strlen(*env1) + 1); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store(env1, environ_key, environ_lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store(env1, *env1, *env1 + strlen(*env1) + 1, + environ_key, environ_lock); +#endif + } +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store(&environ, environ, environ + environ_size + 1); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store(&environ, environ_key, environ_lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store(&environ, environ, environ + environ_size + 1, + environ_key, environ_lock); +#endif + + // =========================================================================================== + // Create metadata for the arguments stored in argv. + for (int i = 0; i < argc; ++i) { +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store(&argv[i], argv[i], + argv[i] + strlen(argv[i]) + 1); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store(&argv[i], argv_key, argv_lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store( + &argv[i], argv[i], argv[i] + strlen(argv[i]) + 1, argv_key, argv_lock); +#endif + } + + // =========================================================================================== + // Create metadata for the environment variables stored in envp. + size_t envp_key; + size_t *envp_lock; + __softboundcets_stack_memory_allocation(&envp_lock, &envp_key); + + char **env2; + for (env2 = envp; *env2; ++env2) { +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store(env2, *env2, *env2 + strlen(*env2) + 1); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store(env2, envp_key, envp_lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store(env2, *env2, *env2 + strlen(*env2) + 1, + envp_key, envp_lock); +#endif + } + + // ============================================================================================= + + // Allocate shadow stack space for argv's and envp's metadata. + __softboundcets_allocate_shadow_stack_space(2); + + // NULL pointer stored as argv's last element. +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + char *argv_bound = (char *)(&argv[argc] + 1); + __softboundcets_store_base_shadow_stack(&argv[0], 0); + __softboundcets_store_bound_shadow_stack(argv_bound, 0); +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_store_key_shadow_stack(argv_key, 0); + __softboundcets_store_lock_shadow_stack(argv_lock, 0); +#endif + + // Store envp's metadata onto the shadow stack. We have to account for the + // NULL pointer stored as envp's last element. +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + char *envp_bound = (char *)(env2 + 1); + __softboundcets_store_base_shadow_stack(envp, 1); + __softboundcets_store_bound_shadow_stack(envp_bound, 1); +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_store_key_shadow_stack(envp_key, 1); + __softboundcets_store_lock_shadow_stack(envp_lock, 1); +#endif + + // =========================================================================================== + // call main of instrumented program + + int return_value = __softboundcets_real_main(argc, argv, envp); + + // Deallocate shadow stack space. + __softboundcets_deallocate_shadow_stack_space(); + + // Delete argv's lock and key. + __softboundcets_stack_memory_deallocation(argv_key); + + return return_value; +} + +void __softboundcets_allocate_shadow_stack_space(size_t num_pointer_args) { + + size_t *prev_stack_size_ptr = __softboundcets_shadow_stack_ptr + 1; + size_t prev_stack_size = *((size_t *)prev_stack_size_ptr); + + __softboundcets_shadow_stack_ptr = + __softboundcets_shadow_stack_ptr + prev_stack_size + 2; + + *((size_t *)__softboundcets_shadow_stack_ptr) = prev_stack_size; + size_t *current_stack_size_ptr = __softboundcets_shadow_stack_ptr + 1; + + ssize_t size = num_pointer_args * __SOFTBOUNDCETS_METADATA_NUM_FIELDS; + *((size_t *)current_stack_size_ptr) = size; +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadow_stack_metadata_ptr(size_t arg_no) { + assert(arg_no >= 0); + size_t count = 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS; + size_t *metadata_ptr = (__softboundcets_shadow_stack_ptr + count); + return (__softboundcets_metadata_t *)metadata_ptr; +} + +__RT_VISIBILITY void *__softboundcets_load_base_shadow_stack(size_t arg_no) { + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __BASE_INDEX; + size_t *base_ptr = (__softboundcets_shadow_stack_ptr + count); + void *base = *((void **)base_ptr); + return base; +} + +__RT_VISIBILITY void *__softboundcets_load_bound_shadow_stack(size_t arg_no) { + + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __BOUND_INDEX; + size_t *bound_ptr = (__softboundcets_shadow_stack_ptr + count); + + void *bound = *((void **)bound_ptr); + return bound; +} + +__RT_VISIBILITY size_t __softboundcets_load_key_shadow_stack(size_t arg_no) { + + assert(arg_no >= 0); + size_t count = 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __KEY_INDEX; + size_t *key_ptr = (__softboundcets_shadow_stack_ptr + count); + size_t key = *key_ptr; + return key; +} + +__RT_VISIBILITY size_t *__softboundcets_load_lock_shadow_stack(size_t arg_no) { + + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __LOCK_INDEX; + size_t **lock_ptr = (size_t **)(__softboundcets_shadow_stack_ptr + count); + return *lock_ptr; +} + +__RT_VISIBILITY void __softboundcets_store_base_shadow_stack(void *base, + size_t arg_no) { + + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __BASE_INDEX; + void **base_ptr = (void **)(__softboundcets_shadow_stack_ptr + count); + + *(base_ptr) = base; +} + +__RT_VISIBILITY void __softboundcets_store_bound_shadow_stack(void *bound, + size_t arg_no) { + + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __BOUND_INDEX; + void **bound_ptr = (void **)(__softboundcets_shadow_stack_ptr + count); + + *(bound_ptr) = bound; +} + +__RT_VISIBILITY void __softboundcets_store_key_shadow_stack(size_t key, + size_t arg_no) { + assert(arg_no >= 0); + size_t count = 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __KEY_INDEX; + size_t *key_ptr = (__softboundcets_shadow_stack_ptr + count); + + *(key_ptr) = key; +} + +__RT_VISIBILITY void __softboundcets_store_lock_shadow_stack(size_t *lock, + size_t arg_no) { + assert(arg_no >= 0); + size_t count = + 2 + arg_no * __SOFTBOUNDCETS_METADATA_NUM_FIELDS + __LOCK_INDEX; + size_t **lock_ptr = (size_t **)(__softboundcets_shadow_stack_ptr + count); + + *(lock_ptr) = lock; +} + +__RT_VISIBILITY void __softboundcets_store_metadata_shadow_stack( +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *base, void *bound, +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + size_t key, size_t *lock, +#endif + size_t arg_no) { + + __softboundcets_metadata_t *metadata = + __softboundcets_shadow_stack_metadata_ptr(arg_no); + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + metadata->base = base; + metadata->bound = bound; +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + metadata->key = key; + metadata->lock = lock; +#endif +} + +__RT_VISIBILITY void __softboundcets_deallocate_shadow_stack_space(void) { + + size_t *reserved_space_ptr = __softboundcets_shadow_stack_ptr; + + size_t read_value = *((size_t *)reserved_space_ptr); + + assert( + (read_value >= 0 && read_value <= __SOFTBOUNDCETS_SHADOW_STACK_ENTRIES)); + + __softboundcets_shadow_stack_ptr = + __softboundcets_shadow_stack_ptr - read_value - 2; +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_trie_allocate(void) { + + __softboundcets_metadata_t *secondary_entry; + size_t length = (__SOFTBOUNDCETS_TRIE_SECONDARY_TABLE_ENTRIES) * + sizeof(__softboundcets_metadata_t); + secondary_entry = (__softboundcets_metadata_t *)mmap( + 0, length, PROT_READ | PROT_WRITE, SOFTBOUNDCETS_MMAP_FLAGS, -1, 0); + // assert(secondary_entry != (void*)-1); + // printf("snd trie table %p %lx\n", secondary_entry, length); + return secondary_entry; +} + +__RT_VISIBILITY void __softboundcets_dummy(void) { printf("calling abort"); } +__RT_VISIBILITY void __softboundcets_introspect_metadata(void *ptr, void *base, + void *bound, + int arg_no) { + + printf("[introspect_metadata]ptr=%p, base=%p, bound=%p, arg_no=%d\n", ptr, + base, bound, arg_no); +} + +__RT_VISIBILITY +void __softboundcets_copy_metadata(void *dest, void *from, size_t size) { + + // printf("dest=%p, from=%p, size=%zx\n", dest, from, size); + + size_t dest_ptr = (size_t)dest; + size_t dest_ptr_end = dest_ptr + size; + + size_t from_ptr = (size_t)from; + size_t from_ptr_end = from_ptr + size; + + if (from_ptr % 8 != 0) { + // printf("dest=%p, from=%p, size=%zx\n", dest, from, size); + return; + // from_ptr = from_ptr %8; + // dest_ptr = dest_ptr %8; + } + + // printf("dest=%p, from=%p, size=%zx\n", dest, from, size); + __softboundcets_metadata_t *trie_secondary_table_dest_begin; + __softboundcets_metadata_t *trie_secondary_table_from_begin; + + size_t dest_primary_index_begin = (dest_ptr >> 25); + size_t dest_primary_index_end = (dest_ptr_end >> 25); + + size_t from_primary_index_begin = (from_ptr >> 25); + size_t from_primary_index_end = (from_ptr_end >> 25); + + if ((from_primary_index_begin != from_primary_index_end) || + (dest_primary_index_begin != dest_primary_index_end)) { + + size_t from_sizet = from_ptr; + size_t dest_sizet = dest_ptr; + + size_t trie_size = size; + size_t index = 0; + + for (index = 0; index < trie_size; index = index + 8) { + + size_t temp_from_pindex = (from_sizet + index) >> 25; + size_t temp_to_pindex = (dest_sizet + index) >> 25; + + size_t dest_secondary_index = (((dest_sizet + index) >> 3) & 0x3fffff); + size_t from_secondary_index = (((from_sizet + index) >> 3) & 0x3fffff); + + __softboundcets_metadata_t *temp_from_strie = + __softboundcets_trie_primary_table[temp_from_pindex]; + + if (temp_from_strie == NULL) { + temp_from_strie = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[temp_from_pindex] = temp_from_strie; + } + __softboundcets_metadata_t *temp_to_strie = + __softboundcets_trie_primary_table[temp_to_pindex]; + + if (temp_to_strie == NULL) { + temp_to_strie = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[temp_to_pindex] = temp_to_strie; + } + + void *dest_entry_ptr = &temp_to_strie[dest_secondary_index]; + void *from_entry_ptr = &temp_from_strie[from_secondary_index]; + +#if __SOFTBOUNDCETS_SPATIAL + memcpy(dest_entry_ptr, from_entry_ptr, 16); +#elif __SOFTBOUNDCETS_TEMPORAL + memcpy(dest_entry_ptr, from_entry_ptr, 16); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + memcpy(dest_entry_ptr, from_entry_ptr, 32); +#endif + } + return; + } + + trie_secondary_table_dest_begin = + __softboundcets_trie_primary_table[dest_primary_index_begin]; + trie_secondary_table_from_begin = + __softboundcets_trie_primary_table[from_primary_index_begin]; + + if (trie_secondary_table_from_begin == NULL) + return; + + if (trie_secondary_table_dest_begin == NULL) { + trie_secondary_table_dest_begin = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[dest_primary_index_begin] = + trie_secondary_table_dest_begin; + } + + size_t dest_secondary_index = ((dest_ptr >> 3) & 0x3fffff); + size_t from_secondary_index = ((from_ptr >> 3) & 0x3fffff); + + assert(dest_secondary_index < __SOFTBOUNDCETS_TRIE_SECONDARY_TABLE_ENTRIES); + assert(from_secondary_index < __SOFTBOUNDCETS_TRIE_SECONDARY_TABLE_ENTRIES); + + void *dest_entry_ptr = &trie_secondary_table_dest_begin[dest_secondary_index]; + void *from_entry_ptr = &trie_secondary_table_from_begin[from_secondary_index]; + +#if __SOFTBOUNDCETS_SPATIAL + + memcpy(dest_entry_ptr, from_entry_ptr, 16 * (size >> 3)); +#elif __SOFTBOUNDCETS_TEMPORAL + + memcpy(dest_entry_ptr, from_entry_ptr, 16 * (size >> 3)); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + memcpy(dest_entry_ptr, from_entry_ptr, 32 * (size >> 3)); +#endif + return; +} + +__RT_VISIBILITY void +__softboundcets_shrink_bounds(void *new_base, void *new_bound, void *old_base, + void *old_bound, void **base_alloca, + void **bound_alloca) { + + *(base_alloca) = new_base < old_base ? old_base : new_base; + *(bound_alloca) = new_bound > old_bound ? old_bound : new_bound; +} + +__RT_VISIBILITY void __softboundcets_spatial_call_dereference_check(void *base, + void *bound, + void *ptr) { + +#ifndef __NOSIM_CHECKS + if ((base != bound) && (ptr != base)) { + __softboundcets_error_printf( + "In Call Dereference Check, base=%p, bound=%p, ptr=%p\n", base, bound, + ptr); + __softboundcets_abort(); + } +#endif +} + +__RT_VISIBILITY void +__softboundcets_spatial_load_dereference_check(void *base, void *bound, + void *ptr, size_t size_of_type) { + + __softboundcets_log_message(LOG_LEVEL_INFO, + "[spatial_load_deref_check] base=%p, " + "bound=%p, ptr=%p, size_of_type=%zx\n", + base, bound, ptr, size_of_type); + if ((ptr < base) || ((void *)((char *)ptr + size_of_type) > bound)) { + + __softboundcets_error_printf( + "[spatial_load_deref_check] base=%zx, bound=%zx, ptr=%zx\n", base, + bound, ptr); + __softboundcets_abort(); + } +} + +__RT_VISIBILITY void __softboundcets_spatial_store_dereference_check( + void *base, void *bound, void *ptr, size_t size_of_type) { + + __softboundcets_log_message(LOG_LEVEL_INFO, + "[spatial_store_deref_check] base=%p, bound=%p, " + "ptr=%p, size_of_type=%zx\n", + base, bound, ptr, size_of_type); + if ((ptr < base) || ((void *)((char *)ptr + size_of_type) > bound)) { + __softboundcets_error_printf( + "[spatial_store_deref_check] base=%p, bound=%p, " + "ptr=%p, size_of_type=%zx, ptr+size=%p\n", + base, bound, ptr, size_of_type, (char *)ptr + size_of_type); + + __softboundcets_abort(); + } +} + +/* Memcopy check, different variants based on spatial, temporal and + spatial+temporal modes +*/ + +__RT_VISIBILITY void __softboundcets_memcopy_check( + void *dest, void *src, size_t size + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + void *dest_base, void *dest_bound, void *src_base, void *src_bound +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + size_t dest_key, void *dest_lock, size_t src_key, void *src_lock +#endif +) { + + if (size >= LONG_MAX) + __softboundcets_abort(); + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + if (dest < dest_base || (char *)dest > ((char *)dest_bound - size) || + (size > (size_t)dest_bound)) + __softboundcets_abort(); + + if (src < src_base || (char *)src > ((char *)src_bound - size) || + (size > (size_t)dest_bound)) + __softboundcets_abort(); +#endif + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + if (IS_INVALID_LOCK((size_t)dest_lock)) { + __softboundcets_error_printf("[memcpy] Invalid dest temporal lock: %p\n", + dest_lock); + __softboundcets_abort(); + return; + } + + if (IS_INVALID_LOCK((size_t)src_lock)) { + __softboundcets_error_printf("[memcpy] Invalid src temporal lock: %p\n", + src_lock); + __softboundcets_abort(); + return; + } + + if (dest_key != *((size_t *)(dest_lock))) { + __softboundcets_abort(); + } + + if (src_key != *((size_t *)(src_lock))) { + __softboundcets_abort(); + } +#endif +} +/* #elif __SOFTBOUNDCETS_TEMPORAL */ + +/* __RT_VISIBILITY void __softboundcets_memcopy_check(void *dest, void *src, */ +/* size_t size, size_t + * dest_key, */ +/* void *dest_lock, */ +/* size_t src_key, */ +/* void *src_lock) { */ + +/* if (size >= LONG_MAX) */ +/* __softboundcets_abort(); */ + +/* if (dest_key != *((size_t *)(dest_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ + +/* if (src_key != *((size_t *)(src_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ +/* } */ + +/* #elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL */ + +/* __RT_VISIBILITY void */ +/* __softboundcets_memcopy_check(void *dest, void *src, size_t size, */ +/* void *dest_base, void *dest_bound, void + * *src_base, */ +/* void *src_bound, size_t dest_key, void + * *dest_lock, */ +/* size_t src_key, void *src_lock) { */ + +/* #ifndef __NOSIM_CHECKS */ + +/* /\* printf("dest=%zx, src=%zx, size=%zx, ulong_max=%zx\n", *\/ */ +/* /\* dest, src, size, ULONG_MAX); *\/ */ +/* if (size >= LONG_MAX) */ +/* __softboundcets_abort(); */ + +/* if (dest < dest_base || (char *)dest > ((char *)dest_bound - size) || */ +/* (size > (size_t)dest_bound)) */ +/* __softboundcets_abort(); */ + +/* if (src < src_base || (char *)src > ((char *)src_bound - size) || */ +/* (size > (size_t)dest_bound)) */ +/* __softboundcets_abort(); */ + +/* if (dest_key != *((size_t *)(dest_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ + +/* if (src_key != *((size_t *)(src_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ + +/* #endif */ +/* } */ +/* #else */ + +/* __RT_VISIBILITY void */ +/* __softboundcets_memcopy_check(void *dest, void *src, size_t size, */ +/* void *dest_base, void *dest_bound, void + * *src_base, */ +/* void *src_bound, size_t dest_key, void + * *dest_lock, */ +/* size_t src_key, void *src_lock) { */ + +/* printf("not handled\n"); */ +/* __softboundcets_abort(); */ +/* } */ +/* #endif */ + +/* Memset check, different variants based on spatial, temporal and + spatial+temporal modes */ + +__RT_VISIBILITY void __softboundcets_memset_check(void *dest, size_t size +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + sbcets_base_t dest_base, + sbcets_bound_t dest_bound +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + sbcets_key_t dest_key, + sbcets_lock_t dest_lock +#endif +) { + + if (size >= LONG_MAX) + __softboundcets_abort(); + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + if (dest < dest_base || (char *)dest > ((char *)dest_bound - size) || + (size > (size_t)dest_bound)) + __softboundcets_abort(); + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + if (IS_INVALID_LOCK((size_t)dest_lock)) { + __softboundcets_error_printf("[memset] Invalid dest temporal lock: %p\n", + dest_lock); + __softboundcets_abort(); + return; + } + + if (dest_key != *((size_t *)(dest_lock))) { + __softboundcets_abort(); + } +#endif +} +/* #elif __SOFTBOUNDCETS_TEMPORAL */ + +/* __RT_VISIBILITY void __softboundcets_memset_check(void *dest, size_t size, */ +/* size_t dest_key, */ +/* void *dest_lock) { */ + +/* if (size >= LONG_MAX) */ +/* __softboundcets_abort(); */ + +/* if (dest_key != *((size_t *)(dest_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ +/* } */ + +/* #elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL */ + +/* __RT_VISIBILITY void __softboundcets_memset_check(void *dest, size_t size, */ +/* void *dest_base, */ +/* void *dest_bound, */ +/* size_t dest_key, */ +/* void *dest_lock) { */ + +/* #ifndef __NOSIM_CHECKS */ + +/* if (size >= LONG_MAX) */ +/* __softboundcets_abort(); */ + +/* if (dest < dest_base || (char *)dest > ((char *)dest_bound - size) || */ +/* (size > (size_t)dest_bound)) */ +/* __softboundcets_abort(); */ + +/* if (dest_key != *((size_t *)(dest_lock))) { */ +/* __softboundcets_abort(); */ +/* } */ + +/* #endif */ +/* } */ +/* #else */ + +/* __RT_VISIBILITY void __softboundcets_memset_check(void *dest, size_t size, */ +/* void *dest_base, */ +/* void *dest_bound, */ +/* size_t dest_key, */ +/* void *dest_lock) { */ + +/* printf("not handled\n"); */ +/* __softboundcets_abort(); */ +/* } */ +/* #endif */ + +/* Metadata store parameterized by the mode of checking */ + +#if __SOFTBOUNDCETS_SPATIAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + void *base, void *bound) { + +#elif __SOFTBOUNDCETS_TEMPORAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + size_t key, size_t *lock) { + +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + void *base, void *bound, + size_t key, size_t *lock) { +#endif + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_log_message( + LOG_LEVEL_DEBUG, + "[metadata_store] addr_of_ptr = %p, ptr = %p, base=%p, bound = %p\n", + addr_of_ptr, *(void **)addr_of_ptr, base, bound); +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_log_message( + LOG_LEVEL_DEBUG, + "[metadata_store] addr_of_ptr = %p, ptr = %p, key=%zx, lock = %p\n", + addr_of_ptr, *(void **)addr_of_ptr, key, lock); +#endif + + size_t ptr = (size_t)addr_of_ptr; + size_t primary_index; + __softboundcets_metadata_t *trie_secondary_table; + + primary_index = (ptr >> 25); + trie_secondary_table = __softboundcets_trie_primary_table[primary_index]; + + if (UNLIKELY(trie_secondary_table == NULL)) { + trie_secondary_table = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[primary_index] = trie_secondary_table; + } + assert(trie_secondary_table != NULL); + + size_t secondary_index = ((ptr >> 3) & 0x3fffff); + __softboundcets_metadata_t *entry_ptr = + &trie_secondary_table[secondary_index]; + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + entry_ptr->base = base; + entry_ptr->bound = bound; + +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + entry_ptr->key = key; + entry_ptr->lock = lock; + +#endif + + return; +} + +__RT_VISIBILITY +void __softboundcets_log_pointer_metadata( + void *address, __softboundcets_metadata_t *metadata) { +#if __SOFTBOUNDCETS_SPATIAL_TEMPORAL || __SOFTBOUNDCETS_SPATIAL + __softboundcets_log_message( + LOG_LEVEL_DEBUG, + "[metadata_load_base/bound] addr_of_ptr = %p, base = %p, bound = %p\n", + address, metadata->base, metadata->bound); +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_log_message( + LOG_LEVEL_DEBUG, + "[metadata_load_key/lock] addr_of_ptr = %p, key = %p, lock = %p\n", + address, metadata->key, metadata->lock); +#endif +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_metadata_ptr(void *address) { + + size_t ptr = (size_t)address; + __softboundcets_metadata_t *trie_secondary_table; + size_t primary_index = (ptr >> 25); + trie_secondary_table = __softboundcets_trie_primary_table[primary_index]; + + size_t secondary_index = ((ptr >> 3) & 0x3fffff); + __softboundcets_metadata_t *entry_ptr = + &trie_secondary_table[secondary_index]; + + __softboundcets_log_pointer_metadata(address, entry_ptr); + + return entry_ptr; +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_metadata_ptr_create_secondary_tries(void *address) { + + size_t ptr = (size_t)address; + __softboundcets_metadata_t *trie_secondary_table; + size_t primary_index = (ptr >> 25); + trie_secondary_table = __softboundcets_trie_primary_table[primary_index]; + + /* unnecessary control flow causes performance overhead */ + /* this can cause segfaults with uninitialized pointer reads from memory */ + if (UNLIKELY(trie_secondary_table == NULL)) { + trie_secondary_table = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[primary_index] = trie_secondary_table; + } + + size_t secondary_index = ((ptr >> 3) & 0x3fffff); + __softboundcets_metadata_t *entry_ptr = + &trie_secondary_table[secondary_index]; + + __softboundcets_log_pointer_metadata(address, entry_ptr); + + return entry_ptr; +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_vector_metadata_ptr(void *addr_of_ptr, int index) { + size_t val = index * 8; + size_t addr = (size_t)addr_of_ptr; + addr = addr + val; + sbcets_base_t addr_cast = (sbcets_base_t)addr; + + return __softboundcets_shadowspace_metadata_ptr(addr_cast); +} + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_masked_vector_metadata_ptr(void *addr_of_ptr, + int index, bool mask) { + if (mask) + return __softboundcets_shadowspace_vector_metadata_ptr(addr_of_ptr, index); + return &dummy_invalid_metadata; +} + +#if __SOFTBOUNDCETS_SPATIAL_TEMPORAL || __SOFTBOUNDCETS_SPATIAL +__RT_VISIBILITY sbcets_base_t +__softboundcets_metadata_load_base(void *address) { + __softboundcets_metadata_t *entry_ptr = + __softboundcets_shadowspace_metadata_ptr(address); + sbcets_base_t base = entry_ptr->base; + __softboundcets_log_message( + LOG_LEVEL_DEBUG, "[metadata_load_base] addr_of_ptr = %p, base = %p\n", + address, base); + return base; +} + +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_load_bound(void *address) { + __softboundcets_metadata_t *entry_ptr = + __softboundcets_shadowspace_metadata_ptr(address); + sbcets_bound_t bound = entry_ptr->bound; + __softboundcets_log_message( + LOG_LEVEL_DEBUG, "[metadata_load_bound] addr_of_ptr = %p, bound = %p\n", + address, bound); + return bound; +} +__RT_VISIBILITY sbcets_base_t +__softboundcets_metadata_load_vector_base(void *ptr, int index) { + size_t val = index * 8; + size_t addr = (size_t)ptr; + addr = addr + val; + sbcets_base_t addr_cast = (sbcets_base_t)addr; + + return __softboundcets_metadata_load_base(addr_cast); +} + +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_load_vector_bound(void *ptr, int index) { + size_t val = index * 8; + size_t addr = (size_t)ptr; + addr = addr + val; + sbcets_base_t addr_cast = (sbcets_base_t)addr; + + return __softboundcets_metadata_load_bound(addr_cast); +} + +__RT_VISIBILITY sbcets_base_t __softboundcets_metadata_masked_load_vector_base( + void *ptr, int index, bool mask) { + if (mask) + return __softboundcets_metadata_load_vector_base(ptr, index); + return 0; +} +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_masked_load_vector_bound(void *ptr, int index, + bool mask) { + if (mask) + return __softboundcets_metadata_load_vector_bound(ptr, index); + return 0; +} + +#endif + +#if __SOFTBOUNDCETS_SPATIAL_TEMPORAL || __SOFTBOUNDCETS_TEMPORAL +__RT_VISIBILITY size_t __softboundcets_metadata_load_key(void *address) { + __softboundcets_metadata_t *entry_ptr = + __softboundcets_shadowspace_metadata_ptr(address); + sbcets_key_t key = entry_ptr->key; + __softboundcets_log_message( + LOG_LEVEL_DEBUG, "[metadata_load_key] addr_of_ptr = %p, key = %p\n", + address, key); + return key; +} + +__RT_VISIBILITY size_t *__softboundcets_metadata_load_lock(void *address) { + + __softboundcets_metadata_t *entry_ptr = + __softboundcets_shadowspace_metadata_ptr(address); + sbcets_lock_t lock = entry_ptr->lock; + __softboundcets_log_message( + LOG_LEVEL_DEBUG, "[metadata_load_lock] addr_of_ptr = %p, lock = %p\n", + address, lock); + return lock; +} + +__RT_VISIBILITY sbcets_lock_t +__softboundcets_metadata_load_vector_lock(void *ptr, int index) { + size_t val = index * 8; + size_t addr = (size_t)ptr; + addr = addr + val; + sbcets_base_t addr_cast = (sbcets_base_t)addr; + + return __softboundcets_metadata_load_lock(addr_cast); +} +__RT_VISIBILITY sbcets_key_t +__softboundcets_metadata_load_vector_key(void *ptr, int index) { + size_t val = index * 8; + size_t addr = (size_t)ptr; + addr = addr + val; + sbcets_base_t addr_cast = (sbcets_base_t)addr; + + return __softboundcets_metadata_load_key(addr_cast); +} + +__RT_VISIBILITY sbcets_key_t __softboundcets_metadata_masked_load_vector_key( + void *ptr, int index, bool mask) { + if (mask) + return __softboundcets_metadata_load_vector_key(ptr, index); + return 0; +} + +__RT_VISIBILITY sbcets_lock_t __softboundcets_metadata_masked_load_vector_lock( + void *ptr, int index, bool mask) { + if (mask) + return __softboundcets_metadata_load_vector_lock(ptr, index); + return 0; +} +#endif + +__RT_VISIBILITY void +__softboundcets_temporal_load_dereference_check(size_t *pointer_lock, + size_t key) { + + /* URGENT: I should think about removing this condition check */ + if (IS_INVALID_LOCK((size_t)pointer_lock)) { + __softboundcets_error_printf( + "[temporal_load_deref_check] Invalid temporal lock: %p\n", + pointer_lock); + __softboundcets_abort(); + return; + } + + size_t temp = *pointer_lock; + + if (temp != key) { + __softboundcets_error_printf( + "[temporal_load_deref_check] Key mismatch key = %zx, *lock=%zx\n", key, + temp); + __softboundcets_abort(); + } +} + +__RT_VISIBILITY void +__softboundcets_temporal_store_dereference_check(size_t *pointer_lock, + size_t key) { + + if (IS_INVALID_LOCK((size_t)pointer_lock)) { + __softboundcets_error_printf( + "[temporal_store_deref_check] Invalid temporal lock: %p\n", + pointer_lock); + __softboundcets_abort(); + return; + } + + size_t temp = *pointer_lock; + + if (temp != key) { + + __softboundcets_error_printf( + "[temporal_store_deref_check] Key mismatch, key = %zx, *lock=%zx\n", + key, temp); + __softboundcets_abort(); + } +} + +__RT_VISIBILITY void __softboundcets_stack_memory_deallocation(size_t ptr_key) { + +#ifndef __SOFTBOUNDCETS_CONSTANT_STACK_KEY_LOCK + + __softboundcets_stack_temporal_space_begin--; + *(__softboundcets_stack_temporal_space_begin) = 0; + +#endif + + return; +} + +__RT_VISIBILITY void __softboundcets_memory_deallocation(size_t *ptr_lock, + size_t ptr_key) { + + if (IS_INVALID_LOCK((size_t)ptr_lock)) { + __softboundcets_error_printf("[mem_dealloc] Invalid temporal lock: %p\n", + ptr_lock); + __softboundcets_abort(); + return; + } + size_t lock_content = *ptr_lock; + + if (lock_content != ptr_key || lock_content == 1) { + __softboundcets_log_message( + LOG_LEVEL_ERROR, + "[mem_dealloc] invalid deallocation!\npointer_lock = %p, key = %zx, " + "*pointer_lock=%zx\n", + ptr_lock, ptr_key, *ptr_lock); + __softboundcets_abort(); + return; + } + + __softboundcets_log_message( + LOG_LEVEL_INFO, "[mem_dealloc] pointer_lock = %p, *pointer_lock=%zx\n", + ptr_lock, *ptr_lock); + + *ptr_lock = 0; + *((void **)ptr_lock) = __softboundcets_lock_next_location; + __softboundcets_lock_next_location = ptr_lock; +} + +__RT_VISIBILITY void *__softboundcets_allocate_lock_location(void) { + + void *temp = NULL; + if (__softboundcets_lock_next_location == NULL) { + __softboundcets_debug_printf("[lock_allocate] new_lock_location=%p\n", + __softboundcets_lock_new_location); + + if (__softboundcets_lock_new_location > + __softboundcets_temporal_space_begin + + __SOFTBOUNDCETS_N_TEMPORAL_ENTRIES) { + __softboundcets_error_printf( + "[lock_allocate] out of temporal free entries \n"); + __softboundcets_abort(); + } + + return __softboundcets_lock_new_location++; + } + + temp = __softboundcets_lock_next_location; + __softboundcets_lock_next_location = + *((size_t **)__softboundcets_lock_next_location); + __softboundcets_debug_printf( + "[lock_allocate] allocated lock_location=%p (-> %p)\n", temp, + __softboundcets_lock_next_location); + + return temp; +} + +__RT_VISIBILITY void +__softboundcets_allocation_secondary_trie_allocate_range(void *initial_ptr, + size_t size) { + + if (!__SOFTBOUNDCETS_PREALLOCATE_TRIE) + return; + + void *addr_of_ptr = initial_ptr; + size_t start_addr_of_ptr = (size_t)addr_of_ptr; + size_t start_primary_index = start_addr_of_ptr >> 25; + + size_t end_addr_of_ptr = (size_t)((char *)initial_ptr + size); + size_t end_primary_index = end_addr_of_ptr >> 25; + + for (; start_primary_index <= end_primary_index; start_primary_index++) { + + __softboundcets_metadata_t *trie_secondary_table = + __softboundcets_trie_primary_table[start_primary_index]; + if (trie_secondary_table == NULL) { + trie_secondary_table = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[start_primary_index] = + trie_secondary_table; + } + } +} + +__RT_VISIBILITY void +__softboundcets_allocation_secondary_trie_allocate(void *addr_of_ptr) { + + /* URGENT: THIS FUNCTION REQUIRES REWRITE */ + + if (!__SOFTBOUNDCETS_PREALLOCATE_TRIE) + return; + + size_t ptr = (size_t)addr_of_ptr; + size_t primary_index = (ptr >> 25); + // size_t secondary_index = ((ptr >> 3) & 0x3fffff); + + __softboundcets_metadata_t *trie_secondary_table = + __softboundcets_trie_primary_table[primary_index]; + + if (trie_secondary_table == NULL) { + trie_secondary_table = __softboundcets_trie_allocate(); + __softboundcets_trie_primary_table[primary_index] = trie_secondary_table; + } + + __softboundcets_metadata_t *trie_secondary_table_second_entry = + __softboundcets_trie_primary_table[primary_index + 1]; + + if (trie_secondary_table_second_entry == NULL) { + __softboundcets_trie_primary_table[primary_index + 1] = + __softboundcets_trie_allocate(); + } + + if (primary_index != 0 && + (__softboundcets_trie_primary_table[primary_index - 1] == NULL)) { + __softboundcets_trie_primary_table[primary_index - 1] = + __softboundcets_trie_allocate(); + } + + return; +} + +__RT_VISIBILITY void __softboundcets_stack_memory_allocation(size_t **ptr_lock, + size_t *ptr_key) { + +#if __SOFTBOUNDCETS_CONSTANT_STACK_KEY_LOCK + *ptr_key = 1; + *ptr_lock = __softboundcets_global_lock; +#else + size_t temp_id = __softboundcets_key_id_counter++; + *ptr_lock = (size_t *)__softboundcets_stack_temporal_space_begin++; + *ptr_key = temp_id; + **ptr_lock = temp_id; +#endif +} + +__RT_VISIBILITY void __softboundcets_memory_allocation(void *ptr, + size_t **ptr_lock, + size_t *ptr_key) { + + size_t temp_id = __softboundcets_key_id_counter++; + + *ptr_lock = (size_t *)__softboundcets_allocate_lock_location(); + *ptr_key = temp_id; + **ptr_lock = temp_id; + + __softboundcets_add_to_free_map(temp_id, ptr); + // printf("memory allocation ptr=%zx, ptr_key=%zx\n", ptr, temp_id); + __softboundcets_allocation_secondary_trie_allocate(ptr); + + __softboundcets_log_message( + LOG_LEVEL_INFO, "[mem_alloc] lock = %p, ptr_key = %p, key = %zx\n", + *ptr_lock, ptr_key, temp_id); +} + +__RT_VISIBILITY size_t *__softboundcets_get_global_lock(void) { + return __softboundcets_global_lock; +} + +__RT_VISIBILITY void __softboundcets_add_to_free_map(size_t ptr_key, + void *ptr) { + + if (!__SOFTBOUNDCETS_FREE_MAP) + return; + + assert(ptr != NULL); + + size_t counter = 0; + while (1) { + size_t index = (ptr_key + counter) % __SOFTBOUNDCETS_N_FREE_MAP_ENTRIES; + size_t *entry_ptr = &__softboundcets_free_map_table[index]; + size_t tag = *entry_ptr; + + if (tag == 0 || tag == 2) { + // printf("entry_ptr=%zx, ptr=%zx, key=%zx\n", entry_ptr, ptr, + // ptr_key); + *entry_ptr = (size_t)(ptr); + return; + } + if (counter >= (__SOFTBOUNDCETS_N_FREE_MAP_ENTRIES)) { +#ifndef __NOSIM_CHECKS + __softboundcets_abort(); +#else + break; +#endif + } + counter++; + } + return; +} + +__RT_VISIBILITY void __softboundcets_check_remove_from_free_map(size_t ptr_key, + void *ptr) { + + if (!__SOFTBOUNDCETS_FREE_MAP) { + return; + } + size_t counter = 0; + while (1) { + size_t index = (ptr_key + counter) % __SOFTBOUNDCETS_N_FREE_MAP_ENTRIES; + size_t *entry_ptr = &__softboundcets_free_map_table[index]; + size_t tag = *entry_ptr; + + if (tag == 0) { +#ifndef __NOSIM_CHECKS + __softboundcets_abort(); +#else + break; +#endif + } + + if (tag == (size_t)ptr) { + *entry_ptr = 2; + return; + } + + if (counter >= __SOFTBOUNDCETS_N_FREE_MAP_ENTRIES) { + // printf("free map out of entries\n"); +#ifndef __NOSIM_CHECKS + __softboundcets_abort(); +#else + break; +#endif + } + counter++; + } + return; +} + +__RT_VISIBILITY void __softboundcets_metadata_store_vector(void *addr_of_ptr, +#if __SOFTBOUNDCETS_SPATIAL + void *base, + void *bound, +#elif __SOFTBOUNDCETS_TEMPORAL + size_t key, + size_t *lock, +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *base, + void *bound, + size_t key, + size_t *lock, +#endif + int index) { + size_t val = index * 8; + size_t addr = (size_t)addr_of_ptr; + addr = addr + val; + +#if __SOFTBOUNDCETS_SPATIAL + __softboundcets_metadata_store((void *)addr, base, bound); +#elif __SOFTBOUNDCETS_TEMPORAL + __softboundcets_metadata_store((void *)addr, key, lock); +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + __softboundcets_metadata_store((void *)addr, base, bound, key, lock); +#endif +} diff --git a/compiler-rt/lib/softboundcets/softboundcets.h b/compiler-rt/lib/softboundcets/softboundcets.h new file mode 100644 index 000000000000..2c76c1845f10 --- /dev/null +++ b/compiler-rt/lib/softboundcets/softboundcets.h @@ -0,0 +1,517 @@ +//=== softboundcets.h - headers for functions introduced by SoftBound+CETS--*- C +//-*===// +// Copyright (c) 2014 Santosh Nagarakatte, Milo M. K. Martin. All rights +// reserved. + +// Developed by: Santosh Nagarakatte, Milo M.K. Martin, +// Jianzhou Zhao, Steve Zdancewic +// Department of Computer and Information Sciences, +// University of Pennsylvania +// http://www.cis.upenn.edu/acg/softbound/ + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin, +// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, nor +// the names of its contributors may be used to endorse or promote +// products derived from this Software without specific prior +// written permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. +//===---------------------------------------------------------------------===// + +#ifndef __SOFTBOUNDCETS_H__ +#define __SOFTBOUNDCETS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __SOFTBOUNDCETS_SPATIAL + __SOFTBOUNDCETS_TEMPORAL + \ + __SOFTBOUNDCETS_SPATIAL_TEMPORAL != \ + 1 +#error Require one and only one of: __SOFTBOUNDCETS_SPATIAL + __SOFTBOUNDCETS_TEMPORAL + __SOFTBOUNDCETS_SPATIAL_TEMPORAL +#endif + +/* Trie represented by the following by a structure with four fields + * if both __SOFTBOUNDCETS_SPATIAL and __SOFTBOUNDCETS_TEMPORAL are + * specified. It has key and lock with size_t + */ + +typedef void *sbcets_base_t; +typedef void *sbcets_bound_t; +typedef size_t sbcets_key_t; +typedef size_t *sbcets_lock_t; + +typedef struct { + +#if __SOFTBOUNDCETS_SPATIAL + void *base; + void *bound; + +#define __SOFTBOUNDCETS_METADATA_NUM_FIELDS 2 +#define __BASE_INDEX 0 +#define __BOUND_INDEX 1 +#define __KEY_INDEX 10000000 +#define __LOCK_INDEX 10000000 + +#elif __SOFTBOUNDCETS_TEMPORAL + size_t key; + size_t *lock; +#define __SOFTBOUNDCETS_METADATA_NUM_FIELDS 2 +#define __KEY_INDEX 0 +#define __LOCK_INDEX 1 +#define __BASE_INDEX 10000000 +#define __BOUND_INDEX 10000000 + +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + + void *base; + void *bound; + size_t key; + size_t *lock; +#define __SOFTBOUNDCETS_METADATA_NUM_FIELDS 4 + +#define __BASE_INDEX 0 +#define __BOUND_INDEX 1 +#define __KEY_INDEX 2 +#define __LOCK_INDEX 3 + +#endif + +} __softboundcets_metadata_t; + +#if defined(__APPLE__) +#define SOFTBOUNDCETS_MMAP_FLAGS (MAP_ANON | MAP_NORESERVE | MAP_PRIVATE) +#else +#define SOFTBOUNDCETS_MMAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE) +#endif + +// Logging levels +#define LOG_LEVEL_DEBUG 1 +#define LOG_LEVEL_INFO 2 +#define LOG_LEVEL_WARN 3 +#define LOG_LEVEL_ERROR 4 +#define LOG_LEVEL_NOTHING 5 + +#if __SOFTBOUNDCETS_BENCHMARKING + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_LEVEL_NOTHING +#endif + +#define __SOFTBOUNDCETS_CONTINUE_ON_ABORT 1 + +#elif __SOFTBOUNDCETS_TEST_BENCHMARKING + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_LEVEL_ERROR +#endif + +#define __SOFTBOUNDCETS_CONTINUE_ON_ABORT 1 + +#endif + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_LEVEL_ERROR +#endif + +#ifndef __SOFTBOUNDCETS_PREALLOCATE_TRIE +#define __SOFTBOUNDCETS_PREALLOCATE_TRIE 1 +#endif + +#ifndef __SOFTBOUNDCETS_CHECK_LOADS +#define __SOFTBOUNDCETS_CHECK_LOADS 1 +#endif + +#define UNLIKELY(x) __builtin_expect(!!(x), 0) + +#if __SOFTBOUNDCETS_PREVENT_SEGFAULTS_ON_INVALID_LOCKS +#define IS_INVALID_LOCK(x) UNLIKELY((x) < 0x10000 || (x) > 0x8fff00000000) +#else +#define IS_INVALID_LOCK(x) (0) +#endif + +#define __SOFTBOUNDCETS_FREE_MAP 0 + +// check if __WORDSIZE works with clang on both Linux and MacOSX +/* Allocating one million entries for the temporal key */ +#if __WORDSIZE == 32 +static const size_t __SOFTBOUNDCETS_N_TEMPORAL_ENTRIES = + ((size_t)4 * (size_t)1024 * (size_t)1024); +static const size_t __SOFTBOUNDCETS_LOWER_ZERO_POINTER_BITS = 2; +static const size_t __SOFTBOUNDCETS_N_STACK_TEMPORAL_ENTRIES = + ((size_t)1024 * (size_t)64); +static const size_t __SOFTBOUNDCETS_N_GLOBAL_LOCK_SIZE = + ((size_t)1024 * (size_t)32); +// 2^23 entries each will be 8 bytes each +static const size_t __SOFTBOUNDCETS_TRIE_PRIMARY_TABLE_ENTRIES = + ((size_t)8 * (size_t)1024 * (size_t)1024); +static const size_t __SOFTBOUNDCETS_SHADOW_STACK_ENTRIES = + ((size_t)128 * (size_t)32); +/* 256 Million simultaneous objects */ +static const size_t __SOFTBOUNDCETS_N_FREE_MAP_ENTRIES = + ((size_t)32 * (size_t)1024 * (size_t)1024); +// each secondary entry has 2^ 22 entries +static const size_t __SOFTBOUNDCETS_TRIE_SECONDARY_TABLE_ENTRIES = + ((size_t)4 * (size_t)1024 * (size_t)1024); + +#else + +static const size_t __SOFTBOUNDCETS_N_TEMPORAL_ENTRIES = + ((size_t)64 * (size_t)1024 * (size_t)1024); +static const size_t __SOFTBOUNDCETS_LOWER_ZERO_POINTER_BITS = 3; + +static const size_t __SOFTBOUNDCETS_N_STACK_TEMPORAL_ENTRIES = + ((size_t)1024 * (size_t)64); +static const size_t __SOFTBOUNDCETS_N_GLOBAL_LOCK_SIZE = + ((size_t)1024 * (size_t)32); + +// 2^23 entries each will be 8 bytes each +static const size_t __SOFTBOUNDCETS_TRIE_PRIMARY_TABLE_ENTRIES = + ((size_t)8 * (size_t)1024 * (size_t)1024); + +static const size_t __SOFTBOUNDCETS_SHADOW_STACK_ENTRIES = + ((size_t)128 * (size_t)32); + +/* 256 Million simultaneous objects */ +static const size_t __SOFTBOUNDCETS_N_FREE_MAP_ENTRIES = + ((size_t)32 * (size_t)1024 * (size_t)1024); +// each secondary entry has 2^ 22 entries +static const size_t __SOFTBOUNDCETS_TRIE_SECONDARY_TABLE_ENTRIES = + ((size_t)4 * (size_t)1024 * (size_t)1024); + +#endif + +#define __WEAK__ __attribute__((__weak__)) + +#if __SOFTBOUNDCETS_DYNAMIC_RT +#define __RT_VISIBILITY extern "C" __attribute__((__visibility__("default"))) +#else +#define __RT_VISIBILITY \ + extern "C" __attribute__((__weak__, always_inline, \ + __visibility__("default"), retain, used)) +#endif + +__WEAK__ extern __softboundcets_metadata_t **__softboundcets_trie_primary_table; + +__WEAK__ extern size_t *__softboundcets_shadow_stack_ptr; +__WEAK__ extern size_t *__softboundcets_temporal_space_begin; + +__WEAK__ extern size_t *__softboundcets_stack_temporal_space_begin; +__WEAK__ extern size_t *__softboundcets_free_map_table; + +__attribute__((__weak__)) +#if !__SOFTBOUNDCETS_CONTINUE_ON_ABORT +__attribute__((__noreturn__)) +#endif +extern void +__softboundcets_abort(void); + +__WEAK__ extern void __softboundcets_printf(const char *str, ...); +__WEAK__ extern void __softboundcets_debug_printf(const char *str, ...); +__WEAK__ extern void __softboundcets_error_printf(const char *str, ...); + +__attribute__((always_inline,__weak__)) void +__softboundcets_log_message(int level, const char *str, ...); + +extern size_t *__softboundcets_global_lock; + +__RT_VISIBILITY void +__softboundcets_allocation_secondary_trie_allocate(void *addr_of_ptr); +__RT_VISIBILITY void __softboundcets_add_to_free_map(size_t ptr_key, void *ptr); + +static __softboundcets_metadata_t dummy_invalid_metadata = {0, 0, 0, 0}; + +static int softboundcets_initialized = 0; + +__RT_VISIBILITY void __softboundcets_init(void); + +__RT_VISIBILITY void softboundcets_init_ctype(void); + +__RT_VISIBILITY int __softboundcets_wrap_main(int argc, char *argv[], + char *envp[]); + +/******************************************************************************/ + +/* Layout of the shadow stack + + 1) size of the previous stack frame + 2) size of the current stack frame + 3) base/bound/key/lock of each argument + + Allocation: read the current stack frames size, increment the + shadow_stack_ptr by current_size + 2, store the previous size into + the new prev value, calcuate the allocation size and store in the + new current stack size field; Deallocation: read the previous size, + and decrement the shadow_stack_ptr */ + +__RT_VISIBILITY void +__softboundcets_allocate_shadow_stack_space(size_t num_pointer_args); + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadow_stack_metadata_ptr(size_t arg_no); + +__RT_VISIBILITY void *__softboundcets_load_base_shadow_stack(size_t arg_no); + +__RT_VISIBILITY void *__softboundcets_load_bound_shadow_stack(size_t arg_no); + +__RT_VISIBILITY size_t __softboundcets_load_key_shadow_stack(size_t arg_no); + +__RT_VISIBILITY size_t *__softboundcets_load_lock_shadow_stack(size_t arg_no); + +__RT_VISIBILITY void __softboundcets_store_base_shadow_stack(void *base, + size_t arg_no); + +__RT_VISIBILITY void __softboundcets_store_bound_shadow_stack(void *bound, + size_t arg_no); + +__RT_VISIBILITY void __softboundcets_store_key_shadow_stack(size_t key, + size_t arg_no); + +__RT_VISIBILITY void __softboundcets_store_lock_shadow_stack(size_t *lock, + size_t arg_no); + +__RT_VISIBILITY void __softboundcets_store_metadata_shadow_stack( +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *base, void *bound, +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + size_t key, size_t *lock, +#endif + size_t arg_no); + +__RT_VISIBILITY void __softboundcets_deallocate_shadow_stack_space(void); + +__RT_VISIBILITY __softboundcets_metadata_t *__softboundcets_trie_allocate(void); + +#if 0 + +//These are primary used to test and introspect metadata during testing + +__RT_VISIBILITY void __softboundcets_print_metadata(void* base, void* bound, void* ptr, size_t key, size_t* lock){ + + printf("[print metadata] ptr = %p, base=%p, bound=%p, key = %zd, lock = %p, *lock = %zd\n", ptr, base, bound, key, lock, *lock); + +} + +__RT_VISIBILITY void __softboundcets_intermediate(char cmp1, char cmp2, char cmp3, size_t loaded_lock){ + + printf("cmp = %d, cmp2 =%d cmp=%d, loaded_lock=%zd\n", cmp1, cmp2, cmp3, loaded_lock); + +} + +#endif + +__RT_VISIBILITY void __softboundcets_dummy(void); +__RT_VISIBILITY void __softboundcets_introspect_metadata(void *ptr, void *base, + void *bound, + int arg_no); + +__RT_VISIBILITY +void __softboundcets_copy_metadata(void *dest, void *from, size_t size); + +__RT_VISIBILITY void +__softboundcets_shrink_bounds(void *new_base, void *new_bound, void *old_base, + void *old_bound, void **base_alloca, + void **bound_alloca); + +__RT_VISIBILITY void __softboundcets_spatial_call_dereference_check(void *base, + void *bound, + void *ptr); + +extern void *malloc_address; +__RT_VISIBILITY void +__softboundcets_spatial_load_dereference_check(void *base, void *bound, + void *ptr, size_t size_of_type); + +__RT_VISIBILITY void +__softboundcets_spatial_store_dereference_check(void *base, void *bound, + void *ptr, size_t size_of_type); + +/* Memcopy check, different variants based on spatial, temporal and + spatial+temporal modes +*/ + +__RT_VISIBILITY void __softboundcets_memcopy_check( + void *dest, void *src, size_t size + +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + void *dest_base, void *dest_bound, void *src_base, void *src_bound +#endif + +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + size_t dest_key, void *dest_lock, size_t src_key, void *src_lock +#endif +); +/* Memset check, different variants based on spatial, temporal and + spatial+temporal modes */ + +__RT_VISIBILITY void __softboundcets_memset_check(void *dest, size_t size +#if __SOFTBOUNDCETS_SPATIAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + sbcets_base_t dest_base, + sbcets_bound_t dest_bound +#endif +#if __SOFTBOUNDCETS_TEMPORAL || __SOFTBOUNDCETS_SPATIAL_TEMPORAL + , + sbcets_key_t dest_key, + sbcets_lock_t dest_lock +#endif +); + +/* Metadata store parameterized by the mode of checking */ + +#if __SOFTBOUNDCETS_SPATIAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + void *base, void *bound); + +#elif __SOFTBOUNDCETS_TEMPORAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + size_t key, size_t *lock); + +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + +__RT_VISIBILITY void __softboundcets_metadata_store(void *addr_of_ptr, + void *base, void *bound, + size_t key, size_t *lock); + +#endif + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_metadata_ptr(void *addr_of_ptr); + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_metadata_ptr_create_secondary_tries( + void *addr_of_ptr); + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_vector_metadata_ptr(void *addr_of_ptr, int index); + +__RT_VISIBILITY __softboundcets_metadata_t * +__softboundcets_shadowspace_masked_vector_metadata_ptr(void *addr_of_ptr, + int index, bool mask); + +#if __SOFTBOUNDCETS_SPATIAL_TEMPORAL || __SOFTBOUNDCETS_SPATIAL +__RT_VISIBILITY sbcets_base_t __softboundcets_metadata_load_base(void *address); + +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_load_bound(void *address); + +__RT_VISIBILITY sbcets_base_t +__softboundcets_metadata_load_vector_base(void *ptr, int index); +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_load_vector_bound(void *ptr, int index); + +__RT_VISIBILITY sbcets_base_t __softboundcets_metadata_masked_load_vector_base( + void *ptr, int index, bool mask); +__RT_VISIBILITY sbcets_bound_t +__softboundcets_metadata_masked_load_vector_bound(void *ptr, int index, + bool mask); + +#endif + +#if __SOFTBOUNDCETS_SPATIAL_TEMPORAL || __SOFTBOUNDCETS_TEMPORAL +__RT_VISIBILITY sbcets_key_t __softboundcets_metadata_load_key(void *address); + +__RT_VISIBILITY sbcets_lock_t __softboundcets_metadata_load_lock(void *address); + +__RT_VISIBILITY sbcets_lock_t +__softboundcets_metadata_load_vector_lock(void *ptr, int index); +__RT_VISIBILITY sbcets_key_t +__softboundcets_metadata_load_vector_key(void *ptr, int index); + +__RT_VISIBILITY sbcets_lock_t __softboundcets_metadata_masked_load_vector_lock( + void *ptr, int index, bool mask); +__RT_VISIBILITY sbcets_key_t __softboundcets_metadata_masked_load_vector_key( + void *ptr, int index, bool mask); + +#endif + +/******************************************************************************/ + +extern __WEAK__ size_t __softboundcets_key_id_counter; +extern __WEAK__ size_t *__softboundcets_lock_next_location; +extern __WEAK__ size_t *__softboundcets_lock_new_location; + +__RT_VISIBILITY void +__softboundcets_temporal_load_dereference_check(size_t *pointer_lock, + size_t key); + +__RT_VISIBILITY void +__softboundcets_temporal_store_dereference_check(size_t *pointer_lock, + size_t key); + +__RT_VISIBILITY void __softboundcets_stack_memory_deallocation(size_t ptr_key); + +__RT_VISIBILITY void __softboundcets_memory_deallocation(size_t *ptr_lock, + size_t ptr_key); + +__RT_VISIBILITY void *__softboundcets_allocate_lock_location(void); + +__RT_VISIBILITY void +__softboundcets_allocation_secondary_trie_allocate_range(void *initial_ptr, + size_t size); + +__RT_VISIBILITY void +__softboundcets_allocation_secondary_trie_allocate(void *addr_of_ptr); + +__RT_VISIBILITY void __softboundcets_stack_memory_allocation(size_t **ptr_lock, + size_t *ptr_key); + +__RT_VISIBILITY void __softboundcets_memory_allocation(void *ptr, + size_t **ptr_lock, + size_t *ptr_key); + +__RT_VISIBILITY sbcets_lock_t __softboundcets_get_global_lock(void); + +__RT_VISIBILITY void __softboundcets_add_to_free_map(size_t ptr_key, void *ptr); + +__RT_VISIBILITY void __softboundcets_check_remove_from_free_map(size_t ptr_key, + void *ptr); + +__RT_VISIBILITY void __softboundcets_metadata_store_vector(void *addr_of_ptr, +#if __SOFTBOUNDCETS_SPATIAL + void *base, + void *bound, +#elif __SOFTBOUNDCETS_TEMPORAL + size_t key, + size_t *lock, +#elif __SOFTBOUNDCETS_SPATIAL_TEMPORAL + void *base, + void *bound, + size_t key, + size_t *lock, +#endif + int index); + +#endif diff --git a/compiler-rt/test/softboundcets/CMakeLists.txt b/compiler-rt/test/softboundcets/CMakeLists.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/llvm/lib/Transforms/CMakeLists.txt b/llvm/lib/Transforms/CMakeLists.txt index 2a0abebdf19b..458e1694270b 100644 --- a/llvm/lib/Transforms/CMakeLists.txt +++ b/llvm/lib/Transforms/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory(HelloNew) add_subdirectory(ObjCARC) add_subdirectory(Coroutines) add_subdirectory(CFGuard) +add_subdirectory(SoftBoundCETS) diff --git a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp index 828fd49524ec..b12ef5698230 100644 --- a/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp +++ b/llvm/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -154,6 +154,10 @@ static cl::opt MaxArraySize("instcombine-maxarray-size", cl::init(1024), cl::desc("Maximum array size considered when doing a combine")); +static cl::opt +ClDisableGEPConstantOffsetAccumulation("softboundcets-disable-gep-constant-offset-accumulation-instcombine", cl::init(false), + cl::desc("Disable constant offset accumulation for GEPs in order to make sub-object bounds checking possible.")); + // FIXME: Remove this flag when it is no longer necessary to convert // llvm.dbg.declare to avoid inaccurate debug info. Setting this to false // increases variable availability at the cost of accuracy. Variables that @@ -2433,7 +2437,7 @@ Instruction *InstCombinerImpl::visitGetElementPtrInst(GetElementPtrInst &GEP) { // analysis of unions. If "A" is also a bitcast, wait for A/X to be merged. unsigned OffsetBits = DL.getIndexTypeSizeInBits(GEPType); APInt Offset(OffsetBits, 0); - if (!isa(SrcOp) && GEP.accumulateConstantOffset(DL, Offset)) { + if (!ClDisableGEPConstantOffsetAccumulation && !isa(SrcOp) && GEP.accumulateConstantOffset(DL, Offset)) { // If this GEP instruction doesn't move the pointer, just replace the GEP // with a bitcast of the real input to the dest type. if (!Offset) { diff --git a/llvm/lib/Transforms/SoftBoundCETS/CMakeLists.txt b/llvm/lib/Transforms/SoftBoundCETS/CMakeLists.txt new file mode 100644 index 000000000000..71ebba9644e5 --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/CMakeLists.txt @@ -0,0 +1,29 @@ +# Build shared lib +add_llvm_library(LLVMSoftBoundCETSLTO MODULE BUILDTREE_ONLY + SoftBoundCETS.cpp + FixByValAttributes.cpp + Utils.cpp + + DEPENDS + intrinsics_gen + + PLUGIN_TOOL + opt +) + +target_compile_definitions(LLVMSoftBoundCETSLTO PRIVATE SOFTBOUNDCETS_EP_LTO) + +add_llvm_library(LLVMSoftBoundCETS MODULE BUILDTREE_ONLY + SoftBoundCETS.cpp + FixByValAttributes.cpp + Utils.cpp + + DEPENDS + intrinsics_gen + + PLUGIN_TOOL + opt +) + +target_compile_definitions(LLVMSoftBoundCETS PRIVATE SOFTBOUNDCETS_EP_OPT_LAST) + diff --git a/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.cpp b/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.cpp new file mode 100644 index 000000000000..ea73bd918617 --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.cpp @@ -0,0 +1,301 @@ +///=== FixByValAttributes.cpp --*- C++ -*=====/// +// Copyright (c) 2016 Santosh Nagarakatte. All rights reserved. + +// Developed by: Santosh Nagarakatte, Rutgers University +// http://www.cs.rutgers.edu/~santosh.nagarakatte/softbound/ + +// The SoftBoundCETS project had contributions from: +// Santosh Nagarakatte, Rutgers University, +// Milo M K Martin, University of Pennsylvania, +// Steve Zdancewic, University of Pennsylvania, +// Jianzhou Zhao, University of Pennsylvania + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of its developers nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. +//===---------------------------------------------------------------------===// + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Instrumentation.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ValueMapper.h" +#include +#include +#include + +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Pass.h" + +#include "llvm-c/Target.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#include "FixByValAttributes.h" + +#include + +using namespace llvm; + +// static cl::opt fix_all_byval("fix_all_byval", +// cl::desc("Transform all byval +// attributes"), cl::init(true)); + +// static cl::opt +// fix_specific_byval("fix_specific_byval", +// cl::desc("Transform only pointer byval attributes"), +// cl::init(false)); + +char FixByValAttributesPass::ID; + +bool FixByValAttributesPass::runOnModule(Module &M) { + + bool Change = true; + + while (Change) { + Change = false; + for (auto &Fn : M.functions()) { + if (Fn.getName().contains("fopencookie")) // ignore wrappers + continue; + Change = transformFunction(Fn); + // evaluate if restart of loop when a function has been transformed is + // warranted -> no + // restart anew to avoid incorrect pointers as we change the function list + // while we iterate through it + if (Change) { + LLVM_DEBUG(dbgs() << "FixByValAttributes transformed function: " + << Fn.getName() << "\n"); + break; + } + } + } + + return true; +} + +// copy OldCallSiteArg Pointee to NewCallSiteAlloca (NewCallSiteAlloca is +// replacement in function call) +// do this in a recursive way: +// build indices for GEP and pass them to recursive createGEPStores calls +// first call should be with Indices = [ConstantInt(0)] +void FixByValAttributesPass::createGEPStores(Value *NewCallSiteAlloca, + Value *OldCallSiteArg, + StructType *StructTy, + Instruction *InsertBefore, + std::vector Indices) { + + int Idx = 0; + + // iterate over all struct members + for (auto I = StructTy->element_begin(), E = StructTy->element_end(); I != E; + ++I, Idx++) { + + std::vector NewIndices; + Type *ElTy = *I; + + for (auto const &OldIdx : Indices) { + NewIndices.push_back(OldIdx); + } + + Constant *LoopIdx = ConstantInt::get( + Type::getInt32Ty(NewCallSiteAlloca->getType()->getContext()), Idx, + false); + NewIndices.push_back(LoopIdx); + + if (isa(ElTy)) { + StructType *ElStructTy = dyn_cast(ElTy); + createGEPStores(NewCallSiteAlloca, OldCallSiteArg, ElStructTy, + InsertBefore, NewIndices); + } else { + + GetElementPtrInst *SrcGEP = GetElementPtrInst::Create( + nullptr, OldCallSiteArg, NewIndices, "", InsertBefore); + + GetElementPtrInst *DestGEP = GetElementPtrInst::Create( + nullptr, NewCallSiteAlloca, NewIndices, "", InsertBefore); + + LoadInst *Load = new LoadInst(SrcGEP->getType()->getContainedType(0), + SrcGEP, "", InsertBefore); + auto *DestGEPCast = new BitCastInst(DestGEP, SrcGEP->getType(), "", InsertBefore); + new StoreInst(Load, DestGEPCast, false, InsertBefore); + } + } +} + +bool FixByValAttributesPass::transformFunction(Function &F) { + + // for each function: + // step 1: change function signature by removing all byval attributes for + // those function arguments which have it + // step 2: change all affected function calls by + // - allocating a new object on the stack before the fn call + // - copying the original function arg which had a byval attribute to this + // object and + //- passing a pointer to this new object to the called function (instead of + // the old byval arg) + + bool FnHasByValArg = false; + for (auto &FnArg : F.args()) { + + if (FnArg.hasByValAttr()) { + FnHasByValArg = true; + } + } + if (!FnHasByValArg) + return false; + + // with an empty VMap we keep all arguments + ValueToValueMapTy VMap; + auto *ClonedFunc = CloneFunction(&F, VMap); + unsigned ArgIdx = 0; + + for (auto &FnArg : ClonedFunc->args()) { + if (FnArg.hasByValAttr()) { + ClonedFunc->removeParamAttr(ArgIdx, Attribute::ByVal); + } + ArgIdx++; + } + replaceFunctionCallsWithByValParams(F, *ClonedFunc); + + F.eraseFromParent(); + return true; +} + +/// Goes over OldF calls and replaces them with a call to NewF +/// by replacing byval arguments with allocas +void FixByValAttributesPass::replaceFunctionCallsWithByValParams( + Function &OldFn, Function &NewFn) { + + std::set AllUsers; + + for (User *U : OldFn.users()) { + if (auto *CB = dyn_cast(U)) { + AllUsers.insert(CB); + } + else { + for (auto *U2 : U->users()){ + if (auto *CB = dyn_cast(U2)) { + AllUsers.insert(CB); + } + else{ + assert(0 && "User not a direct or indirect callbase!"); + } + } + } + } + + for (CallBase *CB : AllUsers) { + + SmallVector NewArgs; + + // iterate over function arguments and over CallSite arguments + auto *CallSiteArg = CB->arg_begin(); + for (auto FnArg = OldFn.arg_begin(), FnArgEnd = OldFn.arg_end(); + FnArg != FnArgEnd; ++FnArg, ++CallSiteArg) { + + if (FnArg->hasByValAttr()) { + + Type *ArgTy = getContainedByValType(FnArg); + + AllocaInst *ByValAlloca; + StructType *StructTy; + + if ((StructTy = dyn_cast(ArgTy))) { + ByValAlloca = new AllocaInst(StructTy, 0, "", CB); + } else if (Type *ElTy = GetElementPtrInst::getTypeAtIndex( + ArgTy, (uint64_t)0)) { + StructTy = dyn_cast(ElTy); + assert(StructTy && "non-struct byval parameters?"); + ByValAlloca = new AllocaInst(StructTy, 0, "", CB); + + } else { + assert(0 && "byval parameter not yet handled!"); + } + + std::vector Indices; + Constant *StartIdx = ConstantInt::get( + Type::getInt64Ty(ByValAlloca->getType()->getContext()), 0, false); + Indices.push_back(StartIdx); + + createGEPStores(ByValAlloca, *CallSiteArg, StructTy, CB, Indices); + NewArgs.push_back(ByValAlloca); + + } else { + NewArgs.push_back(*CallSiteArg); + } + } + // new functioncall: with pointer to alloca instead of original pointer + CallBase *NewCB; + + if (auto *CI = dyn_cast(CB)) { + NewCB = CallInst::Create(&NewFn, NewArgs); + } else if (auto *II = dyn_cast(CB)) { + NewCB = InvokeInst::Create(&NewFn, II->getNormalDest(), + II->getUnwindDest(), NewArgs); + } else { + assert(0 && "Function User not handled!"); + } + + NewCB->setCallingConv(NewFn.getCallingConv()); + if (!CB->use_empty()) + CB->replaceAllUsesWith(NewCB); + ReplaceInstWithInst(CB, NewCB); + } +} diff --git a/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.h b/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.h new file mode 100644 index 000000000000..a48ecf7ea0a9 --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/FixByValAttributes.h @@ -0,0 +1,83 @@ +#include "llvm/Transforms/Instrumentation.h" +#include "llvm-c/Target.h" +#include "llvm-c/TargetMachine.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Pass.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/raw_ostream.h" + + +#include +#include +#include + +#include "Utils.h" + +using namespace llvm; + +#define DEBUG_TYPE "softboundcets" + +/// Why do we need to fix arguments with a byval attribute? +/// byval Attribute: https://llvm.org/docs/LangRef.html#parameter-attributes +/// Why byval: https://lists.llvm.org/pipermail/llvm-dev/2018-April/122714.html + +/// byval is treated/lowered in the backend, but SoftBoundCETS needs to pass its metadata also to functions receiving a byval pointer +/// Solution: iterate over all functions, remove byval attributes and make copy of passed object on the stack before the function call + +// NOTE: As of now, this pass does only work when running in LTO. +// If we run it in OptimizerLast, we do not change function calls in other modules. +// This leads to a linking error function references to the original functions cannot be resolved. +// Possible solution: iterate in each Module over CallInst/Invoke where Arg with pointers is passed ByVal +// Change these calls as we do it now: allocas before function call and associate metadata +class FixByValAttributesPass: public ModulePass{ + + private: + bool runOnModule(Module &) override; + bool transformFunction(Function&); + bool checkPtrsInST(StructType*); + void replaceFunctionCallsWithByValParams(Function &OldF, Function &NewF); + void createGEPStores(Value*, Value*,StructType*, Instruction*, + std::vector); + + public: + static char ID; + FixByValAttributesPass(): ModulePass(ID){ + } + + llvm::StringRef getPassName() const override { return "FixByValAttributes";} +}; + diff --git a/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.cpp b/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.cpp new file mode 100644 index 000000000000..0af631491b5b --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.cpp @@ -0,0 +1,6199 @@ +//=== SoftBoundCETS/SoftBoundCETS.cpp --*- C++ -*=====/// +// Pointer based Spatial and Temporal Memory Safety Pass +// Copyright (c) 2014 Santosh Nagarakatte, Milo M. K. Martin. All rights +// reserved. +// +// Developed by: Santosh Nagarakatte, +// Department of Computer Science, +// Rutgers University +// http://www.cs.rutgers.edu/~santosh.nagarakatte/softbound/ +// +// in collaboration with +// Milo Martin, Jianzhou Zhao, Steve Zdancewic +// University of Pennsylvania +// +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin, +// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, +// Rutgers University, nor the names of its contributors may be +// used to endorse or promote products derived from this Software +// without specific prior written permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. +//===---------------------------------------------------------------------===// + +#include "llvm-c/Transforms/AggressiveInstCombine.h" +#include "llvm/ADT/TinyPtrVector.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Metadata.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Transforms/AggressiveInstCombine/AggressiveInstCombine.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/Transforms/IPO/Inliner.h" +#include "llvm/Transforms/InstCombine/InstCombine.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" +#include "llvm/Transforms/Utils/ValueMapper.h" +#include +#include + +#include "FixByValAttributes.h" +#include "SoftBoundCETS.h" +#include "Utils.h" + +static cl::opt ClEliminateStructChecks( + "eliminate_struct_checks", + cl::desc("don't perform any spatial checking for structure accesses"), + cl::init(false)); + +static cl::opt ClSimpleMetadataMode( + "simple_metadata_mode", + cl::desc("use wrapper functions for metadata loads instead of allocas"), + cl::init(false)); + +static cl::opt + ClDisableSpatialCheckOpt("disable_spatial_check_opt", + cl::desc("disable spatial check optimizations"), + cl::init(false)); + +static cl::opt + ClDisableTemporalCheckOpt("disable_temporal_check_opt", + cl::desc("disable temporal check optimizations"), + cl::init(false)); + +static cl::opt + ClSpatialSafety("softboundcets-spatial-safety", + cl::desc("Enable instrumentation for spatial safety"), + cl::init(true)); + +static cl::opt + ClTemporalSafety("softboundcets-temporal-safety", + cl::desc("Enable instrumentaion for temporal safety"), + cl::init(true)); + +static cl::opt ClAssociateMissingMetadata( + "softboundcets-associate-missing-metadata", + cl::desc( + "Associate missing metadata for a value where is is missing (mostly " + "due to incompleteness/errors in the SoftBoundCETS compiler pass)"), + cl::init(false)); + +static cl::opt ClAssociateOmnivalidMetadataWhenMissing( + "softboundcets-associate-omnivalid-metadata-when-missing", + cl::desc("Associate omnivalid metadata for a value where is is missing " + "(this has " + "only an effect when ClAssociateMissingMetadata is true)"), + cl::init(true)); + +static cl::opt ClExternalLibsAreInstrumented( + "softboundcets-external-libs-are-instrumented", + cl::desc("Assume external libraries are instrumented by SoftBoundCETS as " + "well. This implies instrumenting calls to functions in external " + "libraries."), + cl::init(false)); + +static cl::opt ClMaximalCompatibilityWithExternalLibs( + "softboundcets-maximal-compatibility-with-external-libs", + cl::desc("Associate returned pointers of calls to external functions with " + "omnivalid metadata. Has only an effect if " + "ClExternalLibrariesAreInstrumented is false."), + cl::init(true)); + +static cl::opt ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata( + "softboundcets-zeroinit-globals-omnivalid-metadata", + cl::desc("Associate zeroinitialized globals with omnivalid metadata. " + "This helps when the globals are instead initialized at runtime " + "by external" + "uninstrumented code, preventing false positives."), + cl::init(true)); + +static cl::opt ClAssociateIntToPtrCastsWithOmnivalidMetadata( + "softboundcets-int2ptr-casts-omnivalid-metadata", + cl::desc("Associate Int to Ptr casts with omnivalid metadata. " + "This helps for compatibility with (legacy) code."), + cl::init(true)); + +static cl::opt ClAssociateVaargPointerWithOmnivalidMetadata( + "softboundcets-vaarg-pointer-omnivalid-metadata", + cl::desc("Associate pointers loaded from variadic arguments with omnivalid " + "metadata. " + "Until now, SBCETS has no concept how to deal with them."), + cl::init(true)); + +static cl::opt ClCreateSecondaryShadowSpaceTriesWhenMissing( + "softboundcets-create-secondary-tries", + cl::desc( + "Call to metadata shadowspace function which checks if the secondary " + "tries is non-NULL, and if no, allocates the secondary trie."), + cl::init(false)); + +static cl::opt ClIntraObjectBounds( + "softboundcets-check-intra-object-bounds", + cl::desc("Resize base and bound when taking a GEP of an aggregate value. " + "This allows for detecting intra-object over- and underflows."), + cl::init(false)); + +static cl::opt ClPrintInstrumentedFunctions( + "softboundcets-print-instrumented-functions", + cl::desc("A comma-separated list of functions " + "to be printed after instrumentation."), + cl::init("")); +static cl::opt ClPrintAllInstrumentedFunctions( + "softboundcets-print-all-instrumented-functions", + cl::desc("Print all instrumented functions."), cl::init(false)); + +static cl::opt + ClInlineRuntimeLibFunctions("softboundcets-inline-rtlib-functions", + cl::desc("Inline SoftBoundCETS runtime library " + "functions into the main program."), + cl::init(false)); + +static cl::opt + ClPropagateMetadataOnly("softboundcets_mdprop_only", + cl::desc("perform only metadata propagation"), + cl::init(false)); + +static cl::opt ClInsertCheckIntrinsics( + "softboundcets_chk_intrinsic", + cl::desc("insert intrinsics for spatial and temporal safety"), + cl::init(false)); + +static cl::opt ClInsertSpatialLoadChecks( + "softboundcets_spatial_safety_load_checks", + cl::desc("introduce load dereference checks for spatial safety"), + cl::init(true)); + +static cl::opt ClInsertSpatialStoreChecks( + "softboundcets_spatial_safety_store_checks", + cl::desc("introduce store dereference checks for spatial safety"), + cl::init(true)); + +static cl::opt ClInsertTemporalLoadChecks( + "softboundcets_temporal_load_checks", + cl::desc("introduce temporal load dereference checks"), cl::init(true)); + +static cl::opt ClInsertTemporalStoreChecks( + "softboundcets_temporal_store_checks", + cl::desc("introduce temporal store dereference checks"), cl::init(true)); + +static cl::opt FUNCDOMTEMPORALCHECKOPT( + "softboundcets_func_dom_temporal_check_opt", + cl::desc( + "eliminate function based redundant checks with dominator analysis"), + cl::init(true)); + +static cl::opt + STRUCTOPT("softboundcets_struct_opt", + cl::desc("enable or disable structure optimization"), + cl::init(true)); + +static cl::opt BOUNDSCHECKOPT( + "softboundcets_bounds_check_opt", + cl::desc("enable dominator based load dereference check elimination"), + cl::init(true)); + +static cl::opt SHRINKBOUNDS( + "softboundcets_shrink_bounds", + cl::desc("enable shrinking bounds for the softboundboundcetswithss pass"), + cl::init(false)); + +static cl::opt + DISABLE_MEMCOPYCHECK("softboundcets_disable_memcopy_check", + cl::desc("disable check memcopy calls"), + cl::init(false)); + +static cl::opt + GLOBALCONSTANTOPT("softboundcets_global_const_opt", + cl::desc("global constant expressions are not checked"), + cl::init(false)); + +static cl::opt CALLCHECKS("softboundcets_call_checks", + cl::desc("introduce call checks"), + cl::init(true)); + +static cl::opt + INDIRECTCALLCHECKS("softboundcets_indirect_call_checks", + cl::desc("introduce indirect call checks"), + cl::init(false)); + +static cl::opt OPAQUECALLS( + "softboundcets_opaque_calls", + cl::desc("consider all calls as opaque for func_dom_check_elimination"), + cl::init(true)); + +static cl::opt TEMPORALBOUNDSCHECKOPT( + "softboundcets_temporal_bounds_check_opt", + cl::desc("enable or disable temporal dominator based check elimination"), + cl::init(false)); + +static cl::opt STACKTEMPORALCHECKOPT( + "softboundcets_stack_temporal_check_opt", + cl::desc("eliminate temporal checks for stack variables"), cl::init(true)); + +static cl::opt GLOBALTEMPORALCHECKOPT( + "softboundcets_global_temporal_check_opt", + cl::desc("eliminate temporal checks for global variables"), cl::init(true)); + +static cl::opt BBDOMTEMPORALCHECKOPT( + "softboundcets_bb_dom_temporal_check_opt", + cl::desc("eliminate redundant checks in the basic block"), cl::init(true)); + +static cl::opt DISABLE_MEMCOPY_METADATA_COPIES( + "softboundcets_disable_memcpy_metadata_copies", + cl::desc("disable metadata copies with memcopy"), cl::init(false)); + +#if 0 +static cl::opt +unsafe_byval_opt +("unsafe_byval_opt", + cl::desc("Unbound byval attributed pointers so that check always succeeds"), + cl::init(false)); +#endif + +const char kSoftBoundCETSCtorName[] = "__softboundcets_ctor"; +static const unsigned kSoftBoundCETSCtorPriority = 0; +const char kSoftBoundCETSInitializerName[] = "__softboundcets_init"; +const char kSoftBoundCETSGlobalsInitializerName[] = + "__softboundcets_init_globals"; +const char kSoftBoundCETSWrapMainFnName[] = "__softboundcets_wrap_main"; +const char kSoftBoundCETSRealMainFnName[] = "__softboundcets_real_main"; +const char kSoftBoundCETSSpatialLoadDereferenceCheckFnName[] = + "__softboundcets_spatial_load_dereference_check"; +const char kSoftBoundCETSSpatialStoreDereferenceCheckFnName[] = + "__softboundcets_spatial_store_dereference_check"; +const char kSoftBoundCETSTemporalLoadDereferenceCheckFnName[] = + "__softboundcets_temporal_load_dereference_check"; +const char kSoftBoundCETSTemporalStoreDereferenceCheckFnName[] = + "__softboundcets_temporal_store_dereference_check"; +const char kSoftBoundCETSMemcpyDereferenceCheckFnName[] = + "__softboundcets_memcopy_check"; +const char kSoftBoundCETSMemsetDereferenceCheckFnName[] = + "__softboundcets_memset_check"; +const char kSoftBoundCETSIntrospectMetadataFnName[] = + "__softboundcets_introspect_metadata"; +const char kSoftBoundCETSCopyMetadataFnName[] = "__softboundcets_copy_metadata"; +const char kSoftBoundCETSGetGlobalLockFnName[] = + "__softboundcets_get_global_lock"; +const char kSoftBoundCETSLoadMetadataPtrFnName[] = + "__softboundcets_shadowspace_metadata_ptr"; +const char kSoftBoundCETSLoadMetadataPtrWithSecTriesFnName[] = + "__softboundcets_shadowspace_metadata_ptr_create_secondary_tries"; +const char kSoftBoundCETSLoadVectorMetadataPtrFnName[] = + "__softboundcets_shadowspace_vector_metadata_ptr"; +const char kSoftBoundCETSLoadMaskedVectorMetadataPtrFnName[] = + "__softboundcets_shadowspace_masked_vector_metadata_ptr"; +const char kSoftBoundCETSLoadMetadataBaseFnName[] = + "__softboundcets_metadata_load_base"; +const char kSoftBoundCETSLoadMetadataBoundFnName[] = + "__softboundcets_metadata_load_bound"; +const char kSoftBoundCETSLoadMetadataKeyFnName[] = + "__softboundcets_metadata_load_key"; +const char kSoftBoundCETSLoadMetadataLockFnName[] = + "__softboundcets_metadata_load_lock"; + +const char kSoftBoundCETSLoadVectorMetadataBaseFnName[] = + "__softboundcets_metadata_load_vector_base"; +const char kSoftBoundCETSLoadVectorMetadataBoundFnName[] = + "__softboundcets_metadata_load_vector_bound"; +const char kSoftBoundCETSLoadVectorMetadataKeyFnName[] = + "__softboundcets_metadata_load_vector_key"; +const char kSoftBoundCETSLoadVectorMetadataLockFnName[] = + "__softboundcets_metadata_load_vector_lock"; +const char kSoftBoundCETSMaskedLoadVectorMetadataBaseFnName[] = + "__softboundcets_metadata_masked_load_vector_base"; +const char kSoftBoundCETSMaskedLoadVectorMetadataBoundFnName[] = + "__softboundcets_metadata_masked_load_vector_bound"; +const char kSoftBoundCETSMaskedLoadVectorMetadataKeyFnName[] = + "__softboundcets_metadata_masked_load_vector_key"; +const char kSoftBoundCETSMaskedLoadVectorMetadataLockFnName[] = + "__softboundcets_metadata_masked_load_vector_lock"; + +const char kSoftBoundCETSStoreMetadataFnName[] = + "__softboundcets_metadata_store"; +const char kSoftBoundCETSStoreVectorMetadataFnName[] = + "__softboundcets_metadata_store_vector"; +const char kSoftBoundCETSAllocateStackLockAndKeyFnName[] = + "__softboundcets_stack_memory_allocation"; +const char kSoftBoundCETSDeallocateStackLockAndKeyFnName[] = + "__softboundcets_stack_memory_deallocation"; +const char kSoftBoundCETSCallDereferenceCheckFnName[] = + "__softboundcets_spatial_call_dereference_check"; +const char kSoftBoundCETSAllocateShadowStackFnName[] = + "__softboundcets_allocate_shadow_stack_space"; +const char kSoftBoundCETSDeallocateShadowStackFnName[] = + "__softboundcets_deallocate_shadow_stack_space"; +const char kSoftBoundCETSLoadBaseShadowStackFnName[] = + "__softboundcets_load_base_shadow_stack"; +const char kSoftBoundCETSLoadBoundShadowStackFnName[] = + "__softboundcets_load_bound_shadow_stack"; +const char kSoftBoundCETSStoreMetadataShadowStackFnName[] = + "__softboundcets_store_metadata_shadow_stack"; +const char kSoftBoundCETSLoadKeyShadowStackFnName[] = + "__softboundcets_load_key_shadow_stack"; +const char kSoftBoundCETSLoadLockShadowStackFnName[] = + "__softboundcets_load_lock_shadow_stack"; +const char kSoftBoundCETSLoadShadowStackMetadataPtrFnName[] = + "__softboundcets_shadow_stack_metadata_ptr"; + +// #define SOFTBOUNDCETS_CHK_INTRINSIC 1 + +char SoftBoundCETSPass::ID = 0; + +#ifdef SOFTBOUNDCETS_EP_LTO + +static RegisterStandardPasses SoftBoundCETSLTO( + PassManagerBuilder::EP_FullLinkTimeOptimizationLast, + [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + if (ClInlineRuntimeLibFunctions) { + PM.add(createAlwaysInlinerLegacyPass()); + } + PM.add(new FixByValAttributesPass()); + PM.add(new SoftBoundCETSPass()); + PM.add(createAggressiveInstCombinerPass()); + PM.add(createInstructionCombiningPass()); + PM.add(createGlobalOptimizerPass()); + PM.add(createGlobalDCEPass()); + PM.add(createInstructionCombiningPass()); + if (ClInlineRuntimeLibFunctions) + PM.add(createAlwaysInlinerLegacyPass()); + }); + +static RegisterStandardPasses SoftBoundCETSLTO0( + PassManagerBuilder::EP_EnabledOnOptLevel0, + [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + PM.add(new FixByValAttributesPass()); + PM.add(new SoftBoundCETSPass()); + }); + +#endif + +#ifdef SOFTBOUNDCETS_EP_OPT_LAST + +static RegisterStandardPasses SoftBoundCETS( + PassManagerBuilder::EP_OptimizerLast, + [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + PM.add(new FixByValAttributesPass()); + PM.add(new SoftBoundCETSPass()); + }); + +static RegisterStandardPasses SoftBoundCETS0( + PassManagerBuilder::EP_EnabledOnOptLevel0, + [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) { + PM.add(new FixByValAttributesPass()); + PM.add(new SoftBoundCETSPass()); + }); + +#endif + +StringMap SoftBoundCETSPass::MFunctionWrappersAvailable = { + {"system", true}, + {"setreuid", true}, + {"mkstemp", true}, + {"getrlimit", true}, + {"setrlimit", true}, + {"fread", true}, + {"mkdir", true}, + {"chroot", true}, + {"rmdir", true}, + {"stat", true}, + {"fputc", true}, + {"fileno", true}, + {"fgetc", true}, + {"strncmp", true}, + {"fwrite", true}, + {"atof", true}, + {"feof", true}, + {"remove", true}, + {"tmpfile", true}, + {"ferror", true}, + {"ftell", true}, + {"fstat", true}, + {"fflush", true}, + {"fputs", true}, + {"fopen", true}, + {"fdopen", true}, + {"fseek", true}, + {"ftruncate", true}, + {"popen", true}, + {"fclose", true}, + {"pclose", true}, + {"rewind", true}, + {"readdir", true}, + {"opendir", true}, + {"closedir", true}, + {"rename", true}, + {"getcwd", true}, + {"chown", true}, + {"chdir", true}, + {"strcmp", true}, + {"strcasecmp", true}, + {"strncasecmp", true}, + {"strlen", true}, + {"strpbrk", true}, + {"gets", true}, + {"fgets", true}, + {"perror", true}, + {"strspn", true}, + {"strcspn", true}, + {"memcmp", true}, + {"memchr", true}, + {"rindex", true}, + {"strtoul", true}, + {"strtod", true}, + {"strtol", true}, + {"strchr", true}, + {"strrchr", true}, + {"strcpy", true}, + {"atoi", true}, + {"strtok", true}, + {"strdup", true}, + {"strcat", true}, + {"strncat", true}, + {"strncpy", true}, + {"strstr", true}, + {"signal", true}, + {"atol", true}, + {"realloc", true}, + {"calloc", true}, + {"malloc", true}, + {"mmap", true}, + + {"times", true}, + {"strftime", true}, + {"localtime", true}, + {"time", true}, + {"free", true}, + {"ctime", true}, + {"setbuf", true}, + {"getenv", true}, + {"atexit", true}, + {"strerror", true}, + {"unlink", true}, + // TODO[orthen]: fix wrapper to check for arguments as functions with same + // name can have different declarations + // {"open", true}, + {"read", true}, + {"write", true}, + {"gettimeofday", true}, + {"select", true}, + {"__errno_location", true}, + {"__ctype_b_loc", true}, + {"__ctype_toupper_loc", true}, + {"__ctype_tolower_loc", true}, + {"qsort", true}, + {"puts", true}, + {"setlocale", true}, + // string.h wrappers + {"__mempcpy", true}, + {"__stpcpy", true}, + {"__stpncpy", true}, + {"basename", true}, + {"explicit_bzero", true}, + {"memccpy", true}, + {"memfrob", true}, + {"memmem", true}, + {"memmove", true}, + {"memset", true}, + {"rawmemchr", true}, + {"stpncpy", true}, + {"strcasestr", true}, + {"strcoll", true}, + {"strcoll_l", true}, + {"strerror_l", true}, + {"strfry", true}, + {"strnlen", true}, + {"strsep", true}, + {"strsignal", true}, + {"strtok_r", true}, + {"strverscmp", true}, + {"strxfrm", true}, + {"strxfrm_l", true}, + // strings.h wrappers + {"bcmp", true}, + {"bcopy", true}, + {"bzero", true}, + {"index", true}, + {"strcasecmp_l", true}, + {"strncasecmp_l", true}, + // stdio.h wrappers + {"__asprintf", true}, + {"__overflow", true}, + {"__uflow", true}, + {"asprintf", true}, + {"clearerr", true}, + {"clearerr_unlocked", true}, + {"ctermid", true}, + {"cuserid", true}, + {"dprintf", true}, + {"feof_unlocked", true}, + {"ferror_unlocked", true}, + {"fflush_unlocked", true}, + {"fgetc_unlocked", true}, + {"fgetpos", true}, + {"fgetpos64", true}, + {"fgets_unlocked", true}, + {"fileno_unlocked", true}, + {"flockfile", true}, + {"fmemopen", true}, + {"fopen64", true}, + {"fopencookie", true}, + {"fprintf", true}, + {"fputc_unlocked", true}, + {"fputs_unlocked", true}, + {"freopen", true}, + {"freopen64", true}, + {"fscanf", true}, + {"fseeko64", true}, + {"fsetpos", true}, + {"fsetpos64", true}, + {"ftello", true}, + {"ftello64", true}, + {"ftrylockfile", true}, + {"funlockfile", true}, + {"fwrite_unlocked", true}, + {"getc", true}, + {"getc_unlocked", true}, + {"getdelim", true}, + {"getline", true}, + {"getw", true}, + {"obstack_printf", true}, + {"obstack_vprintf", true}, + {"open_memstream", true}, + {"printf", true}, + {"putc", true}, + {"putc_unlocked", true}, + {"putw", true}, + {"renameat2", true}, + {"scanf", true}, + {"setbuffer", true}, + {"setlinebuf", true}, + {"setvbuf", true}, + {"snprintf", true}, + {"sprintf", true}, + {"sscanf", true}, + {"tempnam", true}, + {"tmpfile64", true}, + {"tmpnam", true}, + {"tmpnam_r", true}, + {"vasprintf", true}, + {"vdprintf", true}, + {"vfprintf", true}, + {"vfscanf", true}, + {"vprintf", true}, + {"vscanf", true}, + {"vsnprintf", true}, + {"vsprintf", true}, + {"vsscanf", true}, + {"wcscpy", true}, + // Wrappers for unistd.h + {"access", true}, + {"brk", true}, + {"confstr", true}, + {"copy_file_range", true}, + // {"crypt", true}, TODO: The wrapper did not compile + {"eaccess", true}, + {"euidaccess", true}, + // {"execl", true}, + // {"execle", true}, + // {"execlp", true}, + {"execv", true}, + {"execve", true}, + {"execveat", true}, + {"execvp", true}, + {"execvpe", true}, + {"faccessat", true}, + {"fexecve", true}, + {"get_current_dir_name", true}, + {"getdomainname", true}, + {"getgroups", true}, + {"gethostname", true}, + {"getlogin", true}, + {"getlogin_r", true}, + {"getpass", true}, + {"getresgid", true}, + {"getresuid", true}, + {"getusershell", true}, + {"getwd", true}, + {"lchown", true}, + {"link", true}, + {"pipe", true}, + {"pipe2", true}, + {"pread", true}, + {"pread64", true}, + {"profil", true}, + {"pwrite", true}, + {"pwrite64", true}, + {"readlink", true}, + {"revoke", true}, + {"sbrk", true}, + {"setdomainname", true}, + {"sethostname", true}, + {"setlogin", true}, + {"swab", true}, + {"symlink", true}, + {"truncate", true}, + {"truncate64", true}, + {"ttyname", true}, + {"ttyname_r", true}, + {"acct", true}, + {"getentropy", true}, + // stdlib.h + {"a64l", true}, + {"aligned_alloc", true}, + {"at_quick_exit", true}, + {"atoll", true}, + {"canonicalize_file_name", true}, + {"drand48_r", true}, + {"ecvt", true}, + {"ecvt_r", true}, + {"erand48", true}, + {"erand48_r", true}, + {"fcvt", true}, + {"fcvt_r", true}, + {"gcvt", true}, + {"getloadavg", true}, + {"getsubopt", true}, + {"initstate", true}, + {"initstate_r", true}, + {"jrand48", true}, + {"jrand48_r", true}, + {"l64a", true}, + {"lcong48", true}, + {"lcong48_r", true}, + {"lrand48_r", true}, + {"mblen", true}, + {"mbstowcs", true}, + {"mbtowc", true}, + {"mkostemp64", true}, + {"mkostemps", true}, + {"mkostemps64", true}, + {"mkstemps", true}, + {"mkstemps64", true}, + {"mktemp", true}, + {"mrand48_r", true}, + {"nrand48", true}, + {"nrand48_r", true}, + {"on_exit", true}, + {"posix_memalign", true}, + {"ptsname", true}, + {"ptsname_r", true}, + {"putenv", true}, + {"qecvt", true}, + {"qecvt_r", true}, + {"qfcvt", true}, + {"qfcvt_r", true}, + {"qgcvt", true}, + {"qsort_r", true}, + {"rand_r", true}, + {"random_r", true}, + {"realpath", true}, + {"secure_getenv", true}, + {"seed48", true}, + {"seed48_r", true}, + {"setstate", true}, + {"setstate_r", true}, + {"srand48_r", true}, + {"srandom_r", true}, + {"strfromd", true}, + {"strfromf", true}, + {"strfromf32", true}, + {"strfromf32x", true}, + {"strfromf64", true}, + {"strfromf64x", true}, + {"strfroml", true}, + {"strtod_l", true}, + {"strtof", true}, + {"strtof32", true}, + {"strtof32_l", true}, + {"strtof32x", true}, + {"strtof32x_l", true}, + {"strtof64", true}, + {"strtof64_l", true}, + {"strtof64x", true}, + {"strtof64x_l", true}, + {"strtof_l", true}, + {"strtol_l", true}, + {"strtold", true}, + {"strtold_l", true}, + {"strtoll", true}, + {"strtoll_l", true}, + {"strtoq", true}, + {"strtoul_l", true}, + {"strtoull", true}, + {"strtoull_l", true}, + {"strtouq", true}, + {"wcstombs", true}, + {"wctomb", true}, + {"reallocarray", true}, + // malloc.h + {"malloc_info", true}, + {"malloc_usable_size", true}, + {"memalign", true}, + {"pvalloc", true}, + {"valloc", true}, +}; + +StringMap SoftBoundCETSPass::MFunctionHasSoftboundCETSDefinition = { + + {"__softboundcets_intermediate", true}, + {"__softboundcets_dummy", true}, + {"__softboundcets_print_metadata", true}, + {"main", true}, + {kSoftBoundCETSCtorName, true}, + {kSoftBoundCETSIntrospectMetadataFnName, true}, + {kSoftBoundCETSCopyMetadataFnName, true}, + {kSoftBoundCETSAllocateShadowStackFnName, true}, + {kSoftBoundCETSDeallocateShadowStackFnName, true}, + + {"__softboundcets_trie_allocate", true}, + {"__shrinkBounds", true}, + {kSoftBoundCETSMemcpyDereferenceCheckFnName, true}, + {kSoftBoundCETSMemsetDereferenceCheckFnName, true}, + + {kSoftBoundCETSSpatialLoadDereferenceCheckFnName, true}, + {kSoftBoundCETSSpatialStoreDereferenceCheckFnName, true}, + {kSoftBoundCETSTemporalLoadDereferenceCheckFnName, true}, + {kSoftBoundCETSTemporalStoreDereferenceCheckFnName, true}, + {kSoftBoundCETSCallDereferenceCheckFnName, true}, + {kSoftBoundCETSAllocateStackLockAndKeyFnName, true}, + {"__softboundcets_memory_allocation", true}, + {kSoftBoundCETSGetGlobalLockFnName, true}, + {"__softboundcets_add_to_free_map", true}, + {"__softboundcets_check_remove_from_free_map", true}, + {"__softboundcets_allocation_secondary_trie_allocate", true}, + {"__softboundcets_allocation_secondary_trie_allocate_range", true}, + {"__softboundcets_allocate_lock_location", true}, + {"__softboundcets_memory_deallocation", true}, + {kSoftBoundCETSDeallocateStackLockAndKeyFnName, true}, + + {kSoftBoundCETSStoreVectorMetadataFnName, true}, + {kSoftBoundCETSLoadMetadataLockFnName, true}, + {kSoftBoundCETSLoadMetadataKeyFnName, true}, + {kSoftBoundCETSLoadMetadataBaseFnName, true}, + {kSoftBoundCETSLoadMetadataBoundFnName, true}, + + {kSoftBoundCETSLoadVectorMetadataLockFnName, true}, + {kSoftBoundCETSLoadVectorMetadataKeyFnName, true}, + {kSoftBoundCETSLoadVectorMetadataBaseFnName, true}, + {kSoftBoundCETSLoadVectorMetadataBoundFnName, true}, + {kSoftBoundCETSMaskedLoadVectorMetadataLockFnName, true}, + {kSoftBoundCETSMaskedLoadVectorMetadataKeyFnName, true}, + {kSoftBoundCETSMaskedLoadVectorMetadataBaseFnName, true}, + {kSoftBoundCETSMaskedLoadVectorMetadataBoundFnName, true}, + + {kSoftBoundCETSStoreMetadataFnName, true}, + + {kSoftBoundCETSCtorName, true}, + {kSoftBoundCETSInitializerName, true}, + {kSoftBoundCETSGlobalsInitializerName, true}, + {kSoftBoundCETSWrapMainFnName, true}, + {"__softboundcets_abort", true}, + {"__softboundcets_printf", true}, + {"__softboundcets_debug_printf", true}, + {"__softboundcets_error_printf", true}, + {"__softboundcets_log_message", true}, +}; + +StringSet<> SoftBoundCETSPass::MIgnorableLLVMIntrinsics = { + "llvm.lifetime.start.p0i8", "llvm.lifetime.end.p0i8"}; + +// +// Method: getAssociateFuncLock() +// +// Description: +// +// This method looks up the "lock" for global variables associated +// with the function. Every will have a getGlobalLockAddr function +// inserted at the beginning which will serve as the lock for all the +// global variables used in the function. +// +// +// Inputs: +// +// Pointer_inst: An instruction that is manipulating a global pointer +// value. +// +// Return value: +// +// Returns the "lock associated with the function. Should never return +// NULL. +// + +// Method: initializeInitFunctions() +// +// Description: This function declares our runtime's initialization functions. +// +// Input: +// +// M: Module to insert the function declarations into. +void SoftBoundCETSPass::initializeInitFunctions(Module &M) { + + auto &C = M.getContext(); + auto *VoidTy = Type::getVoidTy(C); + auto *Int8Ty = Type::getInt8Ty(C); + auto *Int32Ty = Type::getInt32Ty(C); + auto *Int8PtrTy = PointerType::getUnqual(Int8Ty); + auto *Int8PtrPtrTy = PointerType::getUnqual(Int8PtrTy); + + M.getOrInsertFunction(kSoftBoundCETSInitializerName, VoidTy); + + M.getOrInsertFunction(kSoftBoundCETSWrapMainFnName, Int32Ty, Int32Ty, + Int8PtrPtrTy, Int8PtrPtrTy); +} + +// Method: initializeDereferenceCheckHandlers() +// +// Description: This function declares our dereference check handlers. +// +// Input: +// +// M: Module to insert the function declarations into. +void SoftBoundCETSPass::initializeDereferenceCheckHandlers(Module &M) { + + auto &C = M.getContext(); + Type *VoidTy = Type::getVoidTy(C); + Type *VoidPtrTy = PointerType::getUnqual(Type::getInt8Ty(C)); + Type *SizeTy; + if (m_is_64_bit) { + SizeTy = Type::getInt64Ty(C); + } else { + SizeTy = Type::getInt32Ty(C); + } + + if (ClSpatialSafety) { + + SpatialLoadDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSSpatialLoadDereferenceCheckFnName, + VoidTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, SizeTy); + + SpatialStoreDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSSpatialStoreDereferenceCheckFnName, + VoidTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, SizeTy); + + CallDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSCallDereferenceCheckFnName, VoidTy, + VoidPtrTy, VoidPtrTy, VoidPtrTy); + } + + if (ClTemporalSafety) { + + TemporalLoadDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSTemporalLoadDereferenceCheckFnName, + VoidTy, MLockPtrTy, SizeTy); + + TemporalStoreDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSTemporalStoreDereferenceCheckFnName, + VoidTy, MLockPtrTy, SizeTy); + } + + if (ClSpatialSafety && !ClTemporalSafety) { + + MemcpyDereferenceCheckFn = M.getOrInsertFunction( + kSoftBoundCETSMemcpyDereferenceCheckFnName, VoidTy, VoidPtrTy, + VoidPtrTy, SizeTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, VoidPtrTy); + + MemsetDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSMemsetDereferenceCheckFnName, + VoidTy, VoidPtrTy, SizeTy, VoidPtrTy, VoidPtrTy); + + } else if (!ClSpatialSafety && ClTemporalSafety) { + + MemcpyDereferenceCheckFn = M.getOrInsertFunction( + kSoftBoundCETSMemcpyDereferenceCheckFnName, VoidTy, VoidPtrTy, + VoidPtrTy, SizeTy, SizeTy, MLockPtrTy, SizeTy, MLockPtrTy); + + MemsetDereferenceCheckFn = + M.getOrInsertFunction(kSoftBoundCETSMemsetDereferenceCheckFnName, + VoidTy, VoidPtrTy, SizeTy, SizeTy, MLockPtrTy); + + } else if (ClSpatialSafety && ClTemporalSafety) { + + MemcpyDereferenceCheckFn = M.getOrInsertFunction( + kSoftBoundCETSMemcpyDereferenceCheckFnName, VoidTy, VoidPtrTy, + VoidPtrTy, SizeTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, SizeTy, + MLockPtrTy, SizeTy, MLockPtrTy); + + MemsetDereferenceCheckFn = M.getOrInsertFunction( + kSoftBoundCETSMemsetDereferenceCheckFnName, VoidTy, VoidPtrTy, SizeTy, + VoidPtrTy, VoidPtrTy, MKeyTy, MLockPtrTy); + } +} + +// Method: initializeMetadataHandlers() +// +// Description: This function declares our metadata handlers. +// +// Input: +// +// M: Module to insert the function declarations into. +void SoftBoundCETSPass::initializeMetadataHandlers(Module &M) { + + auto &C = M.getContext(); + Type *VoidTy = Type::getVoidTy(C); + Type *VoidPtrTy = PointerType::getUnqual(Type::getInt8Ty(C)); + + Type *SizeTy; + if (m_is_64_bit) { + SizeTy = Type::getInt64Ty(C); + } else { + SizeTy = Type::getInt32Ty(C); + } + + Type *LockPtrPtrTy = PointerType::getUnqual(MLockPtrTy); + Type *SizePtrTy = PointerType::getUnqual(SizeTy); + Type *Int32Ty = Type::getInt32Ty(C); + Type *Int1Ty = Type::getInt1Ty(C); + + StructType *MetadataStruct = + StructType::getTypeByName(C, "struct.__softboundcets_metadata_t"); + + if (!MetadataStruct) { + MetadataStruct = StructType::create(C, "struct.__softboundcets_metadata_t"); + + if (ClSpatialSafety && !ClTemporalSafety) { + MetadataStruct->setBody({MVoidPtrTy, MVoidPtrTy}); + } else if (!ClSpatialSafety && ClTemporalSafety) { + MetadataStruct->setBody({MKeyTy, MLockPtrTy}); + } else if (ClSpatialSafety && ClTemporalSafety) { + MetadataStruct->setBody({MVoidPtrTy, MVoidPtrTy, MKeyTy, MLockPtrTy}); + } + } + + Type *MetadataStructPtrTy = PointerType::getUnqual(MetadataStruct); + + if (ClCreateSecondaryShadowSpaceTriesWhenMissing) + LoadMetadataPtrFn = + M.getOrInsertFunction(kSoftBoundCETSLoadMetadataPtrWithSecTriesFnName, + MetadataStructPtrTy, MVoidPtrTy); + else + LoadMetadataPtrFn = M.getOrInsertFunction( + kSoftBoundCETSLoadMetadataPtrFnName, MetadataStructPtrTy, MVoidPtrTy); + + LoadVectorMetadataPtrFn = + M.getOrInsertFunction(kSoftBoundCETSLoadVectorMetadataPtrFnName, + MetadataStructPtrTy, MVoidPtrTy, Int32Ty); + + LoadMaskedVectorMetadataPtrFn = + M.getOrInsertFunction(kSoftBoundCETSLoadMaskedVectorMetadataPtrFnName, + MetadataStructPtrTy, MVoidPtrTy, Int32Ty, Int1Ty); + + IntrospectMetadataFn = + M.getOrInsertFunction(kSoftBoundCETSIntrospectMetadataFnName, VoidTy, + VoidPtrTy, VoidPtrTy, Int32Ty); + + CopyMetadataFn = M.getOrInsertFunction(kSoftBoundCETSCopyMetadataFnName, + VoidTy, VoidPtrTy, VoidPtrTy, SizeTy); + + GetGlobalLockFn = + M.getOrInsertFunction(kSoftBoundCETSGetGlobalLockFnName, MLockPtrTy); + + // ======================================= + // shadow stack handlers + AllocateShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSAllocateShadowStackFnName, VoidTy, SizeTy); + + DeallocateShadowStackFn = + M.getOrInsertFunction(kSoftBoundCETSDeallocateShadowStackFnName, VoidTy); + + LoadMetadataPtrShadowStackFn = + M.getOrInsertFunction(kSoftBoundCETSLoadShadowStackMetadataPtrFnName, + MetadataStructPtrTy, MSizetTy); + + if (ClSpatialSafety && !ClTemporalSafety) { + StoreMetadataShadowStackFn = + M.getOrInsertFunction(kSoftBoundCETSStoreMetadataShadowStackFnName, + VoidTy, MVoidPtrTy, MVoidPtrTy, SizeTy); + } else if (!ClSpatialSafety && ClTemporalSafety) { + StoreMetadataShadowStackFn = + M.getOrInsertFunction(kSoftBoundCETSStoreMetadataShadowStackFnName, + VoidTy, MKeyTy, MLockPtrTy, SizeTy); + } else if (ClSpatialSafety && ClTemporalSafety) { + StoreMetadataShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSStoreMetadataShadowStackFnName, VoidTy, MVoidPtrTy, + MVoidPtrTy, MKeyTy, MLockPtrTy, SizeTy); + } + + // ======================================= + + if (ClSpatialSafety) { + LoadMetadataBaseFn = M.getOrInsertFunction( + kSoftBoundCETSLoadMetadataBaseFnName, VoidPtrTy, VoidPtrTy); + + LoadMetadataBoundFn = M.getOrInsertFunction( + kSoftBoundCETSLoadMetadataBoundFnName, VoidPtrTy, VoidPtrTy); + + LoadVectorMetadataBaseFn = + M.getOrInsertFunction(kSoftBoundCETSLoadVectorMetadataBaseFnName, + VoidPtrTy, VoidPtrTy, Int32Ty); + + LoadVectorMetadataBoundFn = + M.getOrInsertFunction(kSoftBoundCETSLoadVectorMetadataBoundFnName, + VoidPtrTy, VoidPtrTy, Int32Ty); + MaskedLoadVectorMetadataBaseFn = + M.getOrInsertFunction(kSoftBoundCETSMaskedLoadVectorMetadataBaseFnName, + VoidPtrTy, VoidPtrTy, Int32Ty, Int1Ty); + + MaskedLoadVectorMetadataBoundFn = + M.getOrInsertFunction(kSoftBoundCETSMaskedLoadVectorMetadataBoundFnName, + VoidPtrTy, VoidPtrTy, Int32Ty, Int1Ty); + + LoadBaseShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSLoadBaseShadowStackFnName, VoidPtrTy, SizeTy); + + LoadBoundShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSLoadBoundShadowStackFnName, VoidPtrTy, SizeTy); + } + + if (ClTemporalSafety) { + + AllocateStackLockAndKeyFn = + M.getOrInsertFunction(kSoftBoundCETSAllocateStackLockAndKeyFnName, + VoidTy, LockPtrPtrTy, SizePtrTy); + + DeallocateStackLockAndKeyFn = M.getOrInsertFunction( + kSoftBoundCETSDeallocateStackLockAndKeyFnName, VoidTy, SizeTy); + + LoadMetadataLockFn = M.getOrInsertFunction( + kSoftBoundCETSLoadMetadataLockFnName, MLockPtrTy, VoidPtrTy); + + LoadMetadataKeyFn = M.getOrInsertFunction( + kSoftBoundCETSLoadMetadataKeyFnName, MKeyTy, VoidPtrTy); + + LoadVectorMetadataLockFn = + M.getOrInsertFunction(kSoftBoundCETSLoadVectorMetadataLockFnName, + MLockPtrTy, VoidPtrTy, Int32Ty); + + LoadVectorMetadataKeyFn = M.getOrInsertFunction( + kSoftBoundCETSLoadVectorMetadataKeyFnName, MKeyTy, VoidPtrTy, Int32Ty); + + MaskedLoadVectorMetadataLockFn = + M.getOrInsertFunction(kSoftBoundCETSMaskedLoadVectorMetadataLockFnName, + MLockPtrTy, VoidPtrTy, Int32Ty, Int1Ty); + + MaskedLoadVectorMetadataKeyFn = + M.getOrInsertFunction(kSoftBoundCETSMaskedLoadVectorMetadataKeyFnName, + MKeyTy, VoidPtrTy, Int32Ty, Int1Ty); + + LoadKeyShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSLoadKeyShadowStackFnName, SizeTy, SizeTy); + + LoadLockShadowStackFn = M.getOrInsertFunction( + kSoftBoundCETSLoadLockShadowStackFnName, MLockPtrTy, SizeTy); + } + + if (ClSpatialSafety && !ClTemporalSafety) { + + StoreMetadataFn = + M.getOrInsertFunction(kSoftBoundCETSStoreMetadataFnName, VoidTy, + VoidPtrTy, VoidPtrTy, VoidPtrTy); + + StoreVectorMetadataFn = + M.getOrInsertFunction(kSoftBoundCETSStoreVectorMetadataFnName, VoidTy, + VoidPtrTy, VoidPtrTy, VoidPtrTy, Int32Ty); + + } else if (!ClSpatialSafety && ClTemporalSafety) { + + StoreMetadataFn = + M.getOrInsertFunction(kSoftBoundCETSStoreMetadataFnName, VoidTy, + VoidPtrTy, MKeyTy, MLockPtrTy); + + StoreVectorMetadataFn = + M.getOrInsertFunction(kSoftBoundCETSStoreVectorMetadataFnName, VoidTy, + VoidPtrTy, SizeTy, MLockPtrTy, Int32Ty); + + } else if (ClSpatialSafety && ClTemporalSafety) { + + StoreMetadataFn = M.getOrInsertFunction(kSoftBoundCETSStoreMetadataFnName, + VoidTy, VoidPtrTy, VoidPtrTy, + VoidPtrTy, SizeTy, MLockPtrTy); + + StoreVectorMetadataFn = M.getOrInsertFunction( + kSoftBoundCETSStoreVectorMetadataFnName, VoidTy, VoidPtrTy, VoidPtrTy, + VoidPtrTy, SizeTy, MLockPtrTy, Int32Ty); + } +} + +// Method: initializeSoftBoundVariables() +// +// Description: +// +// +// Input: +// +// +void SoftBoundCETSPass::initializeSoftBoundVariables(Module &M) { + + auto &Ctxt = M.getContext(); + + MVoidPtrTy = PointerType::getUnqual(Type::getInt8Ty(Ctxt)); + + size_t InfBound; + if (m_is_64_bit) { + MSizetTy = Type::getInt64Ty(Ctxt); + } else { + MSizetTy = Type::getInt32Ty(Ctxt); + } + MArgNoTy = MSizetTy; + MKeyTy = MSizetTy; + + if (m_is_64_bit) { + InfBound = (size_t)pow(2, 48); + } else { + InfBound = (size_t)(2147483647); + } + + Constant *InfiniteBound = ConstantInt::get(MSizetTy, InfBound, false); + MInfiniteBoundPtr = ConstantExpr::getIntToPtr(InfiniteBound, MVoidPtrTy); + + MVoidNullPtr = ConstantPointerNull::get(MVoidPtrTy); + + MLockPtrTy = PointerType::getUnqual(MKeyTy); + + MConstantIntOne = ConstantInt::get(MSizetTy, 1); + MConstantIntZero = ConstantInt::get(MSizetTy, 0); + + MGlobalLockOne = new GlobalVariable( + M, MConstantIntOne->getType(), true, GlobalValue::InternalLinkage, + MConstantIntOne, "__softboundcets_global_lock1"); + + MGlobalLockOnes.insert(MGlobalLockOne); +} + +void SoftBoundCETSPass::insertGlobalCtor(Module &M) { + + // Create a new constructor and add it to the global constructor list. + auto *CtorFn = createSanitizerCtor(M, kSoftBoundCETSCtorName); + appendToGlobalCtors(M, CtorFn, kSoftBoundCETSCtorPriority); + + // Before anything else, our constructor has to call the runtime initializer. + auto *InitFn = M.getFunction(kSoftBoundCETSInitializerName); + IRBuilder<> IRB(CtorFn->getEntryBlock().getTerminator()); + IRB.CreateCall(InitFn, {}); +} + +// Method: hasAllocaInst() +// +// Description: +// +// This function checks whether internal function has an alloca +// instruction in the function. This function is useful to determine +// whether we need to allocate a key and a lock for the function or +// not. +// +bool SoftBoundCETSPass::isAllocaPresent(Function *func) { + + for (Function::iterator bb_begin = func->begin(), bb_end = func->end(); + bb_begin != bb_end; ++bb_begin) { + + for (BasicBlock::iterator i_begin = bb_begin->begin(), + i_end = bb_begin->end(); + i_begin != i_end; ++i_begin) { + + Instruction *alloca_inst = dyn_cast(i_begin); + + if (isa(alloca_inst) && + MPresentInOriginal.count(alloca_inst)) { + return true; + } + } + } + return false; +} + +// +// Method: getFunctionKeyLock() +// +// Description: +// +// This function introduces a memory allocation call for allocating a +// new "key" and "lock" for the stack frames on function entry. This +// function also stores the key and lock in the reference Value* +// arguments provided to the function. Further, key and lock is +// allocated only when temporal checking is performed. +// +// Inputs: +// +// func: Function* of the function performing the allocation +// func_key: Value* & is the reference argument to return the key +// func_lock: Value* & is the reference_argument to return the lock +// func_xmm_lock: Value* & is the reference argument that will be +// eventually used to return the key and lock as wide parameters. +// + +void SoftBoundCETSPass::getFunctionKeyLock(Function *func, Value *&func_key, + Value *&func_lock, + Value *&func_xmm_key_lock) { + + Instruction *func_alloca_inst = NULL; + func_key = NULL; + func_lock = NULL; + func_xmm_key_lock = NULL; + if (!ClTemporalSafety) + return; + + if (!isAllocaPresent(func)) + return; + + func_alloca_inst = dyn_cast(func->begin()->begin()); + assert(func_alloca_inst && "func begin null?"); + addMemoryAllocationCall(func, func_key, func_lock, func_alloca_inst); + + return; +} + +// +// Method: addMemoryAllocationCall() +// +// This function introduces a call to the C-handler function for +// allocating key and lock for stack frames. After the handler call, +// it performs the load of the key and the lock to use it as the +// metadata for pointers pointing to stack allocations in the +// function. +// +// Inputs: +// +// func: Function for which the key and the lock is being allocated +// +// ptr_key: Reference argument to return the key after the key and lock +// allocation +// +// ptr_lock: Reference argument to return the lock after +// the key and lock allocation +// +// insert_at: Instruction* before which the C-handler is introduced +// +// Outputs: +// +// A new key and lock is allocated by the C-handler and then returned +// via reference arguments that is used as key and lock for pointers +// pointing to stack allocations in the function. +// + +void SoftBoundCETSPass::addMemoryAllocationCall(Function *func, Value *&ptr_key, + Value *&ptr_lock, + Instruction *insert_at) { + SmallVector args; + Instruction *first_inst_func = cast(func->begin()->begin()); + AllocaInst *lock_alloca = + new AllocaInst(MLockPtrTy, 0, "lock_alloca", first_inst_func); + AllocaInst *key_alloca = new AllocaInst(Type::getInt64Ty(func->getContext()), + 0, "key_alloca", first_inst_func); + args.push_back(lock_alloca); + args.push_back(key_alloca); + + Instruction *flc_call = + CallInst::Create(AllocateStackLockAndKeyFn, args, "", first_inst_func); + + // + // Load the key and lock from the reference arguments passed to the + // C-handler + // + + Instruction *next_inst = getNextInstruction(flc_call); + Instruction *alloca_lock = new LoadInst(lock_alloca->getAllocatedType(), + lock_alloca, "lock.load", next_inst); + Instruction *alloca_key = new LoadInst(key_alloca->getAllocatedType(), + key_alloca, "key.load", next_inst); + + ptr_key = alloca_key; + ptr_lock = alloca_lock; +} + +// Method: transformAndRedirectMain() +// +// Description: +// +// This method transforms the module's main function to always return int (users +// may declare main to return void) and always take three arguments (argument +// count, argument vector, and environment pointer; users may only declare a +// subset of these arguments or none at all). If the original main function +// returns void, the transformed main will always return zero. The transformed +// function is also renamed as specified in kSoftBoundCETSRealMainFnName. +// +// To create metadata for the arguments stored in the argument vector and the +// environment variables pointed to by the environment pointer, this method also +// redirects the execution of the transformed main function through a wrapper +// function. The wrapper is defined in the SoftBound+CETS runtime and named as +// specified in kSoftBoundCETSWrapMainFnName. For this redirection, the method +// inserts a new main function into the module that forwards all three arguments +// to the wrapper. +void SoftBoundCETSPass::transformAndRedirectMain(Module &M) { + auto &C = M.getContext(); + Type *VoidTy = Type::getVoidTy(C); + Type *Int8Ty = Type::getInt8Ty(C); + Type *Int32Ty = Type::getInt32Ty(C); + Type *Int8PtrTy = PointerType::getUnqual(Int8Ty); + Type *Int8PtrPtrTy = PointerType::getUnqual(Int8PtrTy); + + // Transform main if we find it in the module. + if (auto *OldMainFn = M.getFunction("main")) { + + // Create new main. + auto *RealMainDeclaration = M.getFunction(kSoftBoundCETSRealMainFnName); + auto *NewMainFn = Function::Create( + FunctionType::get(Int32Ty, {Int32Ty, Int8PtrPtrTy, Int8PtrPtrTy}, + false), + GlobalValue::ExternalLinkage, kSoftBoundCETSRealMainFnName, &M); + + // Map new main's arguments to old main's arguments and additionally copy + // over the names. + ValueToValueMapTy VMap; + for (auto OldMainArgIter = OldMainFn->arg_begin(), + OldMainArgEnd = OldMainFn->arg_end(), + NewMainArgIter = NewMainFn->arg_begin(); + OldMainArgIter != OldMainArgEnd; ++OldMainArgIter, ++NewMainArgIter) { + assert(NewMainArgIter->getType() == OldMainArgIter->getType() && + "Arguments with differing types?"); + NewMainArgIter->setName(OldMainArgIter->getName()); + VMap[&*OldMainArgIter] = &*NewMainArgIter; + } + + // Clone old main into new main. + SmallVector Returns; + CloneFunctionInto(NewMainFn, OldMainFn, VMap, true, Returns); + + // If old main returned void, replace all return instructions and return + // zero by default. + if (NewMainFn->getReturnType() != OldMainFn->getReturnType()) { + assert(OldMainFn->getReturnType() == VoidTy && + "Main with non-standard return type?"); + auto *ReturnValue = Constant::getNullValue(Int32Ty); + for (auto *RI : Returns) { + ReturnInst::Create(C, ReturnValue, RI); + RI->eraseFromParent(); + } + } + + // Erase old main. + OldMainFn->eraseFromParent(); + + // Create new main that returns int and takes the argument count, argument + // vector, and environment pointer as inputs. + auto *MainFn = Function::Create( + FunctionType::get(Int32Ty, {Int32Ty, Int8PtrPtrTy, Int8PtrPtrTy}, + false), + GlobalValue::ExternalLinkage, "main", &M); + auto *MainFnBB = BasicBlock::Create(C, "", MainFn); + + // We forward all arguments to the wrapper. + SmallVector WrapMainFnArgs; + for (auto &Arg : MainFn->args()) + WrapMainFnArgs.push_back(&Arg); + + // Create a call to the wrapper and forward its return value. + auto *WrapMainFn = M.getFunction(kSoftBoundCETSWrapMainFnName); + auto *MainFnCI = CallInst::Create(WrapMainFn, WrapMainFnArgs, "", MainFnBB); + ReturnInst::Create(C, MainFnCI, MainFnBB); + + if (RealMainDeclaration) { + RealMainDeclaration->replaceAllUsesWith(NewMainFn); + } + } +} + +bool SoftBoundCETSPass::isExternalDefinitelyInstrumentedFunction( + const StringRef &Str) { + return isFunctionNotToInstrument(Str) || + (MFunctionWrappersAvailable.count(Str) > 0); +} + +// +// Method: isFuncDefSoftBound +// +// Description: +// +// This function checks if the input function name is a +// SoftBound/CETS defined function +// + +bool SoftBoundCETSPass::isFunctionNotToInstrument(const StringRef &str) { + + // Is the function name in the above list? + if (MFunctionHasSoftboundCETSDefinition.count(str) > 0) { + return true; + } + + // FIXME: handling new intrinsics which have isoc99 in their name + if (str.find("isoc99") != std::string::npos) { + return true; + } + + if (str.find(kSoftBoundCETSRealMainFnName) == 0) { + return false; + } + + if (str.contains("softboundcets")) { + return true; + } + + // If the function is an llvm intrinsic, don't transform it + if (str.find("llvm.") == 0) { + return true; + } + + return false; +} + +bool SoftBoundCETSPass::isIgnorableLLVMIntrinsic(const StringRef &Str) { + return (MIgnorableLLVMIntrinsics.count(Str) > 0); +} + +// +// Method: identifyFuncToTrans +// +// Description: This function traverses the module and identifies the +// functions that need to be transformed by SoftBound/CETS +// + +void SoftBoundCETSPass::identifyFuncToTrans(Module &module) { + + for (Module::iterator fb_it = module.begin(), fe_it = module.end(); + fb_it != fe_it; ++fb_it) { + + Function *func = dyn_cast(fb_it); + assert(func && " Not a function"); + + // Check if the function is defined in the module + if (!func->isDeclaration()) { + if (isFunctionNotToInstrument(func->getName())) + continue; + + m_func_softboundcets_transform[func->getName()] = true; + if (hasPtrArgRetType(func)) { + m_func_to_transform[func->getName()] = true; + } + } + } +} + +// +// Method: introduceGlobalLockFunction() +// +// Description: +// +// This function introduces the function to retrieve the lock for the +// global variables. This function should be introduced only once for +// every function in the entry block of the function. +// + +Value *SoftBoundCETSPass::introduceGlobalLockFunction(Instruction *insert_at) { + + SmallVector args; + Value *call_inst = CallInst::Create(GetGlobalLockFn, args, "", insert_at); + return call_inst; +} + +// Method: castToVoidPtr() +// +// Description: +// +// This function introduces a bitcast instruction in the IR when a pointer is +// not of type i8*. This is required as all the runtime handlers take i8* +// pointers. +Value *SoftBoundCETSPass::castToVoidPtr(Value *Ptr, IRBuilder<> &IRB) { + if (Ptr->getType() == MVoidPtrTy) + return Ptr; + + if (Constant *C = dyn_cast(Ptr)) + return ConstantExpr::getBitCast(C, MVoidPtrTy); + + return IRB.CreateBitCast(Ptr, MVoidPtrTy, Ptr->getName() + ".voidptr"); +} + +// Helper function castToVoidPtr that creates an IRBuilder from the Instruction. +// TODO[zieris]: Remove when finished rewriting the pass to use IRBuilder +// instead of adding instructions manually. +Value *SoftBoundCETSPass::castToVoidPtr(Value *Op, Instruction *InsertAt) { + IRBuilder<> IRB(InsertAt); + return castToVoidPtr(Op, IRB); +} + +// +// Method: hasPtrArgRetType() +// +// Description: +// +// This function checks if the function has either pointer arguments +// or returns a pointer value. This function is used to determine +// whether shadow stack loads/stores need to be introduced for +// metadata propagation. +// + +bool SoftBoundCETSPass::hasPtrArgRetType(Function *func) { + + const Type *ret_type = func->getReturnType(); + if (isa(ret_type)) + return true; + + for (Function::arg_iterator i = func->arg_begin(), e = func->arg_end(); + i != e; ++i) { + + if (isa(i->getType())) + return true; + } + return false; +} + +// +// Method: addStoreBaseBoundFunc +// +// Description: +// +// This function inserts metadata stores into the bitcode whenever a +// pointer is being stored to memory. +// +// Inputs: +// +// pointer_dest: address where the pointer being stored +// +// pointer_base, pointer_bound, pointer_key, pointer_lock: metadata +// associated with the pointer being stored +// +// pointer : pointer being stored to memory +// +// size_of_type: size of the access +// +// insert_at: the insertion point in the bitcode before which the +// metadata store is introduced. +// +void SoftBoundCETSPass::insertPointerMetadataStore(Value *StoreDest, + Value *Base, Value *Bound, + Value *Key, Value *Lock, + Instruction *InsertAt) { + Value *BaseCast = NULL; + Value *BoundCast = NULL; + + Value *StoreDestCast = castToVoidPtr(StoreDest, InsertAt); + + if (ClSpatialSafety) { + BaseCast = castToVoidPtr(Base, InsertAt); + BoundCast = castToVoidPtr(Bound, InsertAt); + } + + SmallVector Args; + + Args.push_back(StoreDestCast); + + if (ClSpatialSafety) { + Args.push_back(BaseCast); + Args.push_back(BoundCast); + } + + if (ClTemporalSafety) { + Args.push_back(Key); + Args.push_back(Lock); + } + CallInst::Create(StoreMetadataFn, Args, "", InsertAt); +} + +void SoftBoundCETSPass::insertVectorMetadataStore(Value *StoreDest, + Value *Bases, Value *Bounds, + Value *Keys, Value *Locks, + Instruction *InsertAt) { + SmallVector ExtractedBases, ExtractedBounds, ExtractedKeys, + ExtractedLocks; + + if (ClSpatialSafety) { + ExtractedBases = extractVectorValues(Bases, InsertAt); + ExtractedBounds = extractVectorValues(Bounds, InsertAt); + } + if (ClTemporalSafety) { + ExtractedKeys = extractVectorValues(Keys, InsertAt); + ExtractedLocks = extractVectorValues(Locks, InsertAt); + } + + const VectorType *VectorTy; + if (ClSpatialSafety) { + VectorTy = dyn_cast(Bases->getType()); + } else { + VectorTy = dyn_cast(Keys->getType()); + } + ElementCount NumElements = VectorTy->getElementCount(); + + Value *PtrOpBitcast = castToVoidPtr(StoreDest, InsertAt); + SmallVector Args; + for (uint64_t i = 0; i < NumElements.getValue(); i++) { + + Args.clear(); + Constant *Idx = + ConstantInt::get(Type::getInt32Ty(StoreDest->getContext()), i); + Args.push_back(PtrOpBitcast); + + if (ClSpatialSafety) { + Value *PtrBase = ExtractedBases[i]; + Value *PtrBound = ExtractedBounds[i]; + Args.push_back(PtrBase); + Args.push_back(PtrBound); + } + if (ClTemporalSafety) { + Value *PtrKey = ExtractedKeys[i]; + Value *PtrLock = ExtractedKeys[i]; + Args.push_back(PtrKey); + Args.push_back(PtrLock); + } + + Args.push_back(Idx); + + CallInst::Create(StoreVectorMetadataFn, Args, "", InsertAt); + } +} + +// +// The metadata propagation for PHINode occurs in two passes. In the +// first pass, SoftBound/CETS transformation just creates the metadata +// PHINodes and records it in the maps maintained by +// SoftBound/CETS. In the second pass, it populates the incoming +// values of the PHINodes. This two pass approach ensures that every +// incoming value of the original PHINode will have metadata in the +// SoftBound/CETS maps +// + +// +// Method: handlePHIPass1() +// +// Description: +// +// This function creates a PHINode for the metadata in the bitcode for +// pointer PHINodes. It is important to note that this function just +// creates the PHINode and does not populate the incoming values of +// the PHINode, which is handled by the handlePHIPass2. +// + +void SoftBoundCETSPass::handlePHIPass1(PHINode *PHI) { + Type *PHITy = PHI->getType(); + + // Not a Pointer PHINode, then just return + if (!isTypeWithPointers(PHITy)) + return; + + unsigned NumIncomingValues = PHI->getNumIncomingValues(); + + auto MetadataOrder = getMetadataOrder(PHITy); + + if (ClSpatialSafety) { + TinyPtrVector Bases, Bounds; + + PHINode *BasePHI, *BoundPHI; + + for (auto &MetadataType : MetadataOrder) { + if (std::get<0>(MetadataType) == 0) { + BasePHI = + PHINode::Create(MVoidPtrTy, NumIncomingValues, "phi.base", PHI); + + BoundPHI = + PHINode::Create(MVoidPtrTy, NumIncomingValues, "phi.bound", PHI); + } else if (std::get<0>(MetadataType) == 1) { + + auto NumPtrs = std::get<1>(MetadataType); + VectorType *MetadataVectorTy = + VectorType::get(MVoidPtrTy, NumPtrs, false); + BasePHI = PHINode::Create(MetadataVectorTy, NumIncomingValues, + "phi.vector.bases", PHI); + BoundPHI = PHINode::Create(MetadataVectorTy, NumIncomingValues, + "phi.vector.bounds", PHI); + } else { + assert(0 && "Invalid MetadataType returned by getMetadataOrder"); + } + Bases.push_back(BasePHI); + Bounds.push_back(BoundPHI); + } + associateAggregateBaseBound(PHI, Bases, Bounds); + } + if (ClTemporalSafety) { + TinyPtrVector Keys, Locks; + + PHINode *KeyPHI, *LockPHI; + for (auto &MetadataType : MetadataOrder) { + if (std::get<0>(MetadataType) == 0) { + KeyPHI = PHINode::Create(MKeyTy, NumIncomingValues, "phi.key", PHI); + LockPHI = + PHINode::Create(MLockPtrTy, NumIncomingValues, "phi.lock", PHI); + } else if (std::get<0>(MetadataType) == 1) { + auto NumPtrs = std::get<1>(MetadataType); + VectorType *LockMetadataVectorTy = + VectorType::get(MLockPtrTy, NumPtrs, false); + VectorType *KeyMetadataVectorTy = + VectorType::get(MKeyTy, NumPtrs, false); + + KeyPHI = PHINode::Create(KeyMetadataVectorTy, NumIncomingValues, + "phi.vector.keys", PHI); + LockPHI = PHINode::Create(LockMetadataVectorTy, NumIncomingValues, + "phi.vector.locks", PHI); + } else { + assert(0 && "Invalid MetadataType returned by getMetadataOrder"); + } + Keys.push_back(KeyPHI); + Locks.push_back(LockPHI); + } + associateAggregateKeyLock(PHI, Keys, Locks); + } +} + +// +// Method: handlePHIPass2() +// +// Description: This pass fills the incoming values for the metadata +// PHINodes inserted in the first pass. There are four cases that +// needs to be handled for each incoming value. First, if the +// incoming value is a ConstantPointerNull, then base, bound, key, +// lock will be default values. Second, the incoming value can be an +// undef which results in default metadata values. Third, Global +// variables need to get the same base and bound for each +// occurence. So we maintain a map which maps the base and boundfor +// each global variable in the incoming value. Fourth, by default it +// retrieves the metadata from the SoftBound/CETS maps. + +// Check if we need separate global variable and constant expression +// cases. + +void SoftBoundCETSPass::handlePHIPass2(PHINode *PHI) { + Type *PHITy = PHI->getType(); + + // Not a Pointer PHINode, then just return + if (!isTypeWithPointers(PHITy)) + return; + + unsigned NumIncomingValues = PHI->getNumIncomingValues(); + // each PHI can have multiple metadata for each pointer/ptrvector contained in + // the type + ArrayRef PHIBases; + ArrayRef PHIBounds; + ArrayRef PHIKeys; + ArrayRef PHILocks; + size_t BasesSize; + size_t KeysSize; + + if (ClSpatialSafety) { + PHIBases = getAssociatedBases(PHI); + PHIBounds = getAssociatedBounds(PHI); + BasesSize = PHIBases.size(); + } + if (ClTemporalSafety) { + PHIKeys = getAssociatedKeys(PHI); + PHILocks = getAssociatedLocks(PHI); + KeysSize = PHIKeys.size(); + } + + for (unsigned M = 0; M < NumIncomingValues; M++) { + auto *IncomingBB = PHI->getIncomingBlock(M); + auto *IncomingVal = PHI->getIncomingValue(M); + + // each incoming value has also its metadata + if (ClSpatialSafety) { + auto IncomingBases = getAssociatedBases(IncomingVal); + auto IncomingBasesSize = IncomingBases.size(); + assert( + (BasesSize == IncomingBasesSize) && + "PHIPass2: Bases of PHI do not have same length as incoming Bases"); + auto IncomingBounds = getAssociatedBounds(IncomingVal); + + // iterate over bases + for (size_t J = 0; J < BasesSize; J++) { + auto *PHIBase = dyn_cast(PHIBases[J]); + auto *PHIBound = dyn_cast(PHIBounds[J]); + PHIBase->addIncoming(IncomingBases[J], IncomingBB); + PHIBound->addIncoming(IncomingBounds[J], IncomingBB); + } + } + if (ClTemporalSafety) { + auto IncomingKeys = getAssociatedKeys(IncomingVal); + auto IncomingKeysSize = IncomingKeys.size(); + assert( + (KeysSize == IncomingKeysSize) && + "PHIPass2: Bases of PHI do not have same length as incoming Bases"); + auto IncomingLocks = getAssociatedLocks(IncomingVal); + + // iterate over keys + for (size_t J = 0; J < KeysSize; J++) { + auto *PHIKey = dyn_cast(PHIKeys[J]); + auto *PHILock = dyn_cast(PHILocks[J]); + PHIKey->addIncoming(IncomingKeys[J], IncomingBB); + PHILock->addIncoming(IncomingLocks[J], IncomingBB); + } + } + } // Iterating over incoming values ends +} + +// +// Method: propagateMetadata +// +// Descripton; +// +// This function propagates the metadata from the source to the +// destination in the map for pointer arithmetic operations~(gep) and +// bitcasts. This is the place where we need to shrink bounds. +// + +void SoftBoundCETSPass::propagateMetadata(Value *Src, Instruction *Dest) { + // Need to just propagate the base and bound here if I am not + // shrinking bounds + if (checkMetadataPresent(Dest)) { + return; + } + + if (ClSpatialSafety) { + auto Bases = TinyPtrVector(getAssociatedBases(Src)); + auto Bounds = TinyPtrVector(getAssociatedBounds(Src)); + associateAggregateBaseBound(Dest, Bases, Bounds); + } + if (ClTemporalSafety) { + auto Keys = TinyPtrVector(getAssociatedKeys(Src)); + auto Locks = TinyPtrVector(getAssociatedLocks(Src)); + associateAggregateKeyLock(Dest, Keys, Locks); + } + return; +} + +// +// Method: handleBitCast +// +// Description: Propagate metadata from source to destination with +// pointer bitcast operations. + +void SoftBoundCETSPass::handleBitCast(BitCastInst *BC) { + Value *PtrOp = BC->getOperand(0); + propagateMetadata(PtrOp, BC); +} + +// +// Method: introduceShadowStackAllocation +// +// Description: For every function call that has a pointer argument or +// a return value, shadow stack is used to propagate metadata. This +// function inserts the shadow stack allocation C-handler that +// reserves space in the shadow stack by reserving the requiste amount +// of space based on the input passed to it(number of pointer +// arguments/return). + +void SoftBoundCETSPass::introduceShadowStackAllocation(CallBase *CallInst, + int NumPtrs) { + Value *NumTotalPtrArgs = ConstantInt::get( + Type::getInt64Ty(CallInst->getType()->getContext()), NumPtrs, false); + TinyPtrVector Args; + Args.push_back(NumTotalPtrArgs); + CallInst::Create(AllocateShadowStackFn, Args, "", CallInst); +} + +// +// Method: introduceShadowStackStores +// +// Description: This function inserts a call to the shadow stack store +// C-handler that stores the metadata, before the function call in the +// bitcode for pointer arguments. + +size_t SoftBoundCETSPass::introduceShadowStackStores(Value *Val, + Instruction *InsertAt, + int ArgNo) { + size_t NumPtrs = countPointersInType(Val->getType()); + if (NumPtrs == 0) + return 0; + + SmallVector Args, UnpackedBases, UnpackedBounds, UnpackedKeys, + UnpackedLocks; + unsigned MetadataSize = 0; + IRBuilder<> IRB(InsertAt); + + /** (ds) + * Push metadata of each contained pointer on the shadow stack linearly. + */ + if (ClSpatialSafety) { + auto AssociatedBases = getAssociatedBases(Val); + auto AssociatedBounds = getAssociatedBounds(Val); + UnpackedBases = unpackMetadataArray(AssociatedBases, InsertAt); + UnpackedBounds = unpackMetadataArray(AssociatedBounds, InsertAt); + MetadataSize = UnpackedBases.size(); + } + if (ClTemporalSafety) { + auto AssociatedKeys = getAssociatedKeys(Val); + auto AssociatedLocks = getAssociatedLocks(Val); + UnpackedKeys = unpackMetadataArray(AssociatedKeys, InsertAt); + UnpackedLocks = unpackMetadataArray(AssociatedLocks, InsertAt); + MetadataSize = UnpackedKeys.size(); + } + + for (unsigned Idx = 0; Idx < MetadataSize; ++Idx) { + Args.clear(); + if (ClSpatialSafety) { + Value *BaseCast = castToVoidPtr(UnpackedBases[Idx], InsertAt); + Args.push_back(BaseCast); + Value *BoundCast = castToVoidPtr(UnpackedBounds[Idx], InsertAt); + Args.push_back(BoundCast); + } + if (ClTemporalSafety) { + Args.push_back(UnpackedKeys[Idx]); + Args.push_back(UnpackedLocks[Idx]); + } + Args.push_back(ConstantInt::get(MArgNoTy, ArgNo + Idx, false)); + IRB.CreateCall(StoreMetadataShadowStackFn, Args); + } + + return NumPtrs; +} + +// +// Method: introduceShadowStackDeallocation +// +// Description: This function inserts a call to the C-handler that +// deallocates the shadow stack space on function exit. + +void SoftBoundCETSPass::introduceShadowStackDeallocation( + CallBase *CallInst, Instruction *InsertAt) { + TinyPtrVector Args; + CallInst::Create(DeallocateShadowStackFn, Args, "", InsertAt); +} + +// +// Method: getNumPointerArgs +// +// Description: Returns the number of pointer arguments and return. +// +size_t SoftBoundCETSPass::getNumPointerArgs(const CallBase *CB) { + size_t NumPointerArgs = 0; + + for (const Use &Arg : CB->args()) + NumPointerArgs += countPointersInType(Arg->getType()); + + return NumPointerArgs; +} + +// +// Method: introduceShadowStackLoads +// +// Description: This function introduces calls to the C-handlers that +// performs the loads from the shadow stack to retrieve the metadata. +// This function also associates the loaded metadata with the pointer +// arguments in the SoftBound/CETS maps. + +size_t SoftBoundCETSPass::introduceShadowStackLoads(Value *Val, + Instruction *InsertAt, + int ArgNo) { + size_t NumPtrs = countPointersInType(Val->getType()); + if (!NumPtrs) + return 0; + + IRBuilder<> IRB(InsertAt); + + Type *ValTy = Val->getType(); + + TinyPtrVector Bases; + TinyPtrVector Bounds; + TinyPtrVector Keys; + TinyPtrVector Locks; + + unsigned KeyIdx = 0; + unsigned LockIdx = 1; + if (ClSpatialSafety) { + KeyIdx = 2; + LockIdx = 3; + } + + if (ClSimpleMetadataMode) { + + SmallVector Args; + for (unsigned PtrIdx = 0; PtrIdx < NumPtrs; ++PtrIdx) { + Args.clear(); + + Args.push_back(ConstantInt::get(MArgNoTy, ArgNo + PtrIdx, false)); + if (ClSpatialSafety) { + Value *Base = IRB.CreateCall(LoadBaseShadowStackFn, Args, ""); + Bases.push_back(Base); + + Value *Bound = IRB.CreateCall(LoadBoundShadowStackFn, Args, ""); + Bounds.push_back(Bound); + } + + if (ClTemporalSafety) { + Value *Key = IRB.CreateCall(LoadKeyShadowStackFn, Args, ""); + Keys.push_back(Key); + Value *Lock = IRB.CreateCall(LoadLockShadowStackFn, Args, ""); + Locks.push_back(Lock); + } + } + + } else { + + for (unsigned PtrIdx = 0; PtrIdx < NumPtrs; ++PtrIdx) { + auto *MetadataPtr = + IRB.CreateCall(LoadMetadataPtrShadowStackFn, + {ConstantInt::get(MArgNoTy, ArgNo + PtrIdx, false)}, + "shadow_stack_metadata_ptr"); + + if (ClSpatialSafety) { + auto *BasePtr = IRB.CreateStructGEP(MetadataPtr, 0, "baseptr"); + auto *Base = IRB.CreateLoad(BasePtr, "base"); + auto *BoundPtr = IRB.CreateStructGEP(MetadataPtr, 1, "boundptr"); + auto *Bound = IRB.CreateLoad(BoundPtr, "bound"); + Bases.push_back(Base); + Bounds.push_back(Bound); + } + if (ClTemporalSafety) { + auto *KeyPtr = IRB.CreateStructGEP(MetadataPtr, KeyIdx, "keyptr"); + auto *Key = IRB.CreateLoad(KeyPtr, "key"); + auto *LockPtr = IRB.CreateStructGEP(MetadataPtr, LockIdx, "lockptr"); + auto *Lock = IRB.CreateLoad(LockPtr, "lock"); + Keys.push_back(Key); + Locks.push_back(Lock); + } + } + } + + if (ClSpatialSafety) { + auto PackedBases = packMetadataArray(Bases, ValTy, InsertAt); + auto PackedBounds = packMetadataArray(Bounds, ValTy, InsertAt); + associateAggregateBaseBound(Val, PackedBases, PackedBounds); + } + if (ClTemporalSafety) { + auto PackedKeys = packMetadataArray(Keys, ValTy, InsertAt); + auto PackedLocks = packMetadataArray(Locks, ValTy, InsertAt); + associateAggregateKeyLock(Val, PackedKeys, PackedLocks); + } + + return NumPtrs; +} +// +// Method: dissociateKeyLock +// +// Description: This function removes the key lock metadata associated +// with the pointer operand in the SoftBound/CETS maps. +void SoftBoundCETSPass::dissociateKeyLock(Value *pointer_operand) { + if (MValueKeyMap.count(pointer_operand)) { + MValueKeyMap.erase(pointer_operand); + } + if (MValueLockMap.count(pointer_operand)) { + MValueLockMap.erase(pointer_operand); + } + assert((MValueKeyMap.count(pointer_operand) == 0) && + "dissociating key failed"); + assert((MValueLockMap.count(pointer_operand) == 0) && + "dissociating lock failed"); +} +// +// Method: dissociateBaseBound +// +// Description: This function removes the base/bound metadata +// associated with the pointer operand in the SoftBound/CETS maps. + +void SoftBoundCETSPass::dissociateBaseBound(Value *pointer_operand) { + if (MValueBaseMap.count(pointer_operand)) { + MValueBaseMap.erase(pointer_operand); + } + if (MValueBoundMap.count(pointer_operand)) { + MValueBoundMap.erase(pointer_operand); + } + assert((MValueBaseMap.count(pointer_operand) == 0) && + "dissociating base failed\n"); + assert((MValueBoundMap.count(pointer_operand) == 0) && + "dissociating bound failed"); +} +void SoftBoundCETSPass::associateMetadata( + Value *Val, const SoftBoundCETSMetadata &Metadata) { + if (ClSpatialSafety) { + associateBaseBound(Val, Metadata.Base, Metadata.Bound); + } + if (ClTemporalSafety) { + associateKeyLock(Val, Metadata.Key, Metadata.Lock); + } +} + +void SoftBoundCETSPass::associateMetadata(Value *Val, Value *Base, Value *Bound, + Value *Key, Value *Lock) { + if (ClSpatialSafety) { + associateBaseBound(Val, Base, Bound); + } + if (ClTemporalSafety) { + associateKeyLock(Val, Key, Lock); + } +} + +void SoftBoundCETSPass::associateAggregateMetadata( + Value *Val, TinyPtrVector &Bases, TinyPtrVector &Bounds, + TinyPtrVector &Keys, TinyPtrVector &Locks) { + if (ClSpatialSafety) { + associateAggregateBaseBound(Val, Bases, Bounds); + } + if (ClTemporalSafety) { + associateAggregateKeyLock(Val, Keys, Locks); + } +} + +Value * +SoftBoundCETSPass::createMetadataVector(ArrayRef SingleMetadataVals, + Instruction *InsertAt) { + + uint64_t NumPtrs = SingleMetadataVals.size(); + FixedVectorType *MetadataVectorTy = + FixedVectorType::get(SingleMetadataVals.front()->getType(), NumPtrs); + Value *MetadataVector = UndefValue::get(MetadataVectorTy); + for (uint64_t J = 0; J < NumPtrs; J++) { + Constant *Idx = + ConstantInt::get(Type::getInt32Ty(InsertAt->getContext()), J); + MetadataVector = InsertElementInst::Create( + MetadataVector, SingleMetadataVals[J], Idx, "", InsertAt); + } + return MetadataVector; +} + +SmallVector +SoftBoundCETSPass::extractVectorBases(Value *Val, Instruction *InsertAt) { + auto *BasesVector = getAssociatedBase(Val); + return extractVectorValues(BasesVector, InsertAt); +} + +SmallVector +SoftBoundCETSPass::extractVectorBounds(Value *Val, Instruction *InsertAt) { + auto *BoundsVector = getAssociatedBound(Val); + return extractVectorValues(BoundsVector, InsertAt); +} + +SmallVector +SoftBoundCETSPass::extractVectorKeys(Value *Val, Instruction *InsertAt) { + auto *KeysVector = getAssociatedKey(Val); + return extractVectorValues(KeysVector, InsertAt); +} + +SmallVector +SoftBoundCETSPass::extractVectorLocks(Value *Val, Instruction *InsertAt) { + auto *LocksVector = getAssociatedLock(Val); + return extractVectorValues(LocksVector, InsertAt); +} + +SmallVector +SoftBoundCETSPass::extractVectorValues(Value *MetadataVector, + Instruction *InsertAt) { + const VectorType *VectorTy = dyn_cast(MetadataVector->getType()); + assert(VectorTy && "MetadataVector Value is not a VectorType"); + ElementCount NumElements = VectorTy->getElementCount(); + SmallVector Metadata; + + for (uint64_t I = 0; I < NumElements.getValue(); I++) { + Constant *Idx = + ConstantInt::get(Type::getInt32Ty(InsertAt->getContext()), I); + Value *Metadatum = + ExtractElementInst::Create(MetadataVector, Idx, "", InsertAt); + Metadata.push_back(Metadatum); + } + return Metadata; +} + +// +// Method: associateKeyLock +// +// Description: This function associates the key lock with the pointer +// operand in the SoftBound/CETS maps. + +inline void SoftBoundCETSPass::associateKeyLock(Value *Val, Value *Key, + Value *Lock) { + if (MValueKeyMap.count(Val)) { + dissociateKeyLock(Val); + } + + assert(isValidMetadata(Key, MetadataType::Key) && "Invalid key metadata"); + assert(isValidMetadata(Lock, MetadataType::Lock) && "Invalid lock metadata"); + + MValueKeyMap[Val] = {Key}; + MValueLockMap[Val] = {Lock}; +} + +inline void +SoftBoundCETSPass::associateAggregateBase(Value *Val, + TinyPtrVector &Bases) { + auto NumMetadata = countMetadata(Val->getType()); + assert(NumMetadata == Bases.size() && + "Bases size is not equal to number of metadata in type"); + + for (auto &Base : Bases) { + assert(isValidMetadata(Base, MetadataType::Base) && + "Invalid base metadata"); + } + MValueBaseMap[Val] = Bases; +} + +inline void +SoftBoundCETSPass::associateAggregateBound(Value *Val, + TinyPtrVector &Bounds) { + auto NumMetadata = countMetadata(Val->getType()); + assert(NumMetadata == Bounds.size() && + "Bounds size is not equal to number of metadata in type"); + + for (auto &Bound : Bounds) { + assert(isValidMetadata(Bound, MetadataType::Bound) && + "Invalid bound metadata"); + } + MValueBoundMap[Val] = Bounds; +} + +inline void SoftBoundCETSPass::associateAggregateBaseBound( + Value *Val, TinyPtrVector &Bases, TinyPtrVector &Bounds) { + if (MValueBaseMap.count(Val)) { + dissociateBaseBound(Val); + } + associateAggregateBase(Val, Bases); + associateAggregateBound(Val, Bounds); +} + +inline void SoftBoundCETSPass::associateAggregateKeyLock( + Value *Val, TinyPtrVector &Keys, TinyPtrVector &Locks) { + if (MValueBaseMap.count(Val)) { + dissociateKeyLock(Val); + } + + auto MetadataSize = countMetadata(Val->getType()); + assert(MetadataSize == Keys.size() && + "Keys size is not equal to metadata size of type"); + assert(MetadataSize == Locks.size() && + "Locks size is not equal to metadata size of type"); + + for (auto &Key : Keys) { + assert(isValidMetadata(Key, MetadataType::Key) && "Invalid key metadata"); + } + for (auto &Lock : Locks) { + assert(isValidMetadata(Lock, MetadataType::Lock) && + "Invalid lock metadata"); + } + + MValueKeyMap[Val] = Keys; + MValueLockMap[Val] = Locks; +} + +inline bool SoftBoundCETSPass::isValidMetadata(Value *Metadatum, + MetadataType MetadataTy) { + auto *ValTy = Metadatum->getType(); + switch (MetadataTy) { + case Base: + case Bound: { + if (ValTy == MVoidPtrTy) + return true; + auto *VectorTy = dyn_cast(ValTy); + if (VectorTy && (VectorTy->getElementType() == MVoidPtrTy)) + return true; + return false; + + break; + } + case Key: { + if (ValTy == MKeyTy) + return true; + auto *VectorTy = dyn_cast(ValTy); + if (VectorTy && (VectorTy->getElementType() == MKeyTy)) + return true; + return false; + + break; + } + case Lock: { + if (ValTy == MLockPtrTy) + return true; + auto *VectorTy = dyn_cast(ValTy); + if (VectorTy && (VectorTy->getElementType() == MLockPtrTy)) + return true; + return false; + + break; + } + } + + return false; +} + +// +// Method: associateBaseBound +// +// Description: This function associates the base bound with the +// pointer operand in the SoftBound/CETS maps. + +inline void SoftBoundCETSPass::associateBaseBound(Value *Val, Value *Base, + Value *Bound) { + if (MValueBaseMap.count(Val)) { + dissociateBaseBound(Val); + } + + assert(isValidMetadata(Base, MetadataType::Base) && "Invalid base metadata"); + assert(isValidMetadata(Bound, MetadataType::Bound) && + "Invalid bound metadata"); + + MValueBaseMap[Val] = {Base}; + MValueBoundMap[Val] = {Bound}; +} +// +// Method: handleSelect +// +// This function propagates the metadata with Select IR instruction. +void SoftBoundCETSPass::handleSelect(SelectInst *Select) { + auto *SelectTy = Select->getType(); + if (!isTypeWithPointers(SelectTy)) + return; + + Value *Condition = Select->getOperand(0); + ArrayRef OpBases[2]; + ArrayRef OpBounds[2]; + ArrayRef OpKeys[2]; + ArrayRef OpLocks[2]; + + for (unsigned M = 0; M < 2; M++) { + Value *Op = Select->getOperand(M + 1); + + if (ClSpatialSafety) { + OpBases[M] = getAssociatedBases(Op); + OpBounds[M] = getAssociatedBounds(Op); + } + + if (ClTemporalSafety) { + OpKeys[M] = getAssociatedKeys(Op); + OpLocks[M] = getAssociatedLocks(Op); + } + } + + // two possibilities: + // 1) Condition is an i1; then we just create a select for each metadata the + // type contains + // 2) Condition is a vector of i1; this means both arguments are + // also vectors + // then we need to unpack each metadata vector of each argument, create + // selects for the contained bases/bounds... and repack the metadata into a + // vector for the Select result + + if (Condition->getType()->isIntegerTy(1)) { + + if (ClSpatialSafety) { + TinyPtrVector Bases, Bounds; + + for (size_t M = 0; M < OpBases[0].size(); M++) { + SelectInst *Base, *Bound; + Base = SelectInst::Create(Condition, OpBases[0][M], OpBases[1][M], + "select.base", Select); + + Bound = SelectInst::Create(Condition, OpBounds[0][M], OpBounds[1][M], + "select.bound", Select); + + Bases.push_back(Base); + Bounds.push_back(Bound); + } + + associateAggregateBaseBound(Select, Bases, Bounds); + } + + if (ClTemporalSafety) { + TinyPtrVector Keys, Locks; + + for (size_t M = 0; M < OpKeys[0].size(); M++) { + SelectInst *Key, *Lock; + Key = SelectInst::Create(Condition, OpKeys[0][M], OpKeys[1][M], + "select.key", Select); + + Lock = SelectInst::Create(Condition, OpLocks[0][M], OpLocks[1][M], + "select.lock", Select); + + Keys.push_back(Key); + Locks.push_back(Lock); + } + + associateAggregateKeyLock(Select, Keys, Locks); + } + } else { // Condition is a vector of i1 + // TODO: merge this with previous if branch + assert(Condition->getType()->isVectorTy() && + "select condition not a vector"); + + if (ClSpatialSafety) { + auto *Base = SelectInst::Create(Condition, OpBases[0][0], OpBases[1][0], + "select.base", Select); + auto *Bound = SelectInst::Create(Condition, OpBounds[0][0], + OpBounds[1][0], "select.bound", Select); + associateBaseBound(Select, Base, Bound); + } + + if (ClTemporalSafety) { + + auto *Key = SelectInst::Create(Condition, OpKeys[0][0], OpKeys[1][0], + "select.key", Select); + auto *Lock = SelectInst::Create(Condition, OpLocks[0][0], OpLocks[1][0], + "select.lock", Select); + associateKeyLock(Select, Key, Lock); + } + } +} + +bool SoftBoundCETSPass::checkMetadataPresent(Value *Op) { + bool MetadataPresent = false; + if (ClSpatialSafety) { + MetadataPresent = checkBaseBoundMetadataPresent(Op); + } else { + MetadataPresent = true; + } + if (ClTemporalSafety) { + MetadataPresent = (MetadataPresent && checkKeyLockMetadataPresent(Op)); + } + return MetadataPresent; +} + +// +// Method: checkBaseBoundMetadataPresent() +// +// Description: +// Checks if the metadata is present in the SoftBound/CETS maps. + +bool SoftBoundCETSPass::checkBaseBoundMetadataPresent(Value *Op) { + return MValueBaseMap.count(Op) && MValueBoundMap.count(Op); +} + +// +// Method: checkKeyLockMetadataPresent() +// +// Description: +// Checks if the metadata is present in the SoftBound/CETS maps. + +bool SoftBoundCETSPass::checkKeyLockMetadataPresent(Value *Op) { + return MValueKeyMap.count(Op) && MValueLockMap.count(Op); +} + +// +// Method: handleReturnInst +// +// Description: +// This function inserts C-handler calls to store +// metadata for return values in the shadow stack. + +void SoftBoundCETSPass::handleReturnInst(ReturnInst *Ret) { + Value *RetVal = Ret->getReturnValue(); + if (RetVal == NULL) { + return; + } + Type *RetTy = RetVal->getType(); + if (!isTypeWithPointers(RetTy)) + return; + + introduceShadowStackStores(RetVal, Ret, 0); +} + +// +// Method: getConstantExprBaseBound +// +// Description: This function uniform handles all global constant +// expression and obtains the base and bound for these expressions +// without introducing any extra IR modifications. +// +// WARNING: This method only handles constants with a single pointer correctly. +// For constants containing multiple pointers use getConstantExprBaseBoundArray. + +void SoftBoundCETSPass::getConstantExprBaseBound(Constant *given_constant, + Value *&tmp_base, + Value *&tmp_bound) { + TinyPtrVector bases, bounds; + getConstantExprBaseBoundArray(given_constant, bases, bounds); + assert(bases.size() == 1 && bounds.size() == 1 && + "getConstantExprBaseBound called on aggregate"); + tmp_base = bases[0]; + tmp_bound = bounds[0]; +} + +// +// Methods: getAssociatedBase(Array), getAssociatedBound(Array), +// getAssociatedKey(Array), getAssociatedLock(Array) +// +// Description: Retrieves the metadata from SoftBound/CETS maps +// + +/** (ds) + * The methods with an -Array suffix return all associated metadata + * entries while those without the suffix only return a single value. + * Thus, the -Array methods must be used if the value potentially + * contains multiple pointers (i.e. is an aggregate value). + * The non-Array methods exist mostly for compatibility reasons, + * since aggregates are not yet supported everywhere. + * Further, the non-Array methods check whether the given value is a + * constant and call getConstantExprBaseBound in this case. + * When using the -Array methods, this must be checked manually by + * the caller. + */ + +ArrayRef SoftBoundCETSPass::getAssociatedBases(Value *Val) { + if (!MValueBaseMap.count(Val)) { + if (auto *Const = dyn_cast(Val)) { + auto Bases = createConstantBases(Const); + associateAggregateBase(Val, Bases); + } else if (ClAssociateMissingMetadata) { + auto DummyMetadata = + createDummyMetadata(Val->getType(), MVoidNullPtr); + associateAggregateBase(Val, DummyMetadata); + } else { + LLVM_DEBUG(errs() << "Missing base(s) for Value: " << *Val << '\n'); + assert(0 && "No associated base(s)"); + } + } + return MValueBaseMap[Val]; +} + +// WARNING: This method only handles constants with a single pointer correctly. +// For constants containing multiple pointers use getAssociatedBaseArray. +Value *SoftBoundCETSPass::getAssociatedBase(Value *pointer_operand) { + ArrayRef base_array = getAssociatedBases(pointer_operand); + assert(base_array.size() == 1 && "getAssociatedBase called on aggregate"); + Value *pointer_base = base_array[0]; + + return pointer_base; +} + +ArrayRef SoftBoundCETSPass::getAssociatedBounds(Value *Val) { + if (!MValueBoundMap.count(Val)) { + if (auto *Const = dyn_cast(Val)) { + auto Bounds = createConstantBounds(Const); + associateAggregateBound(Val, Bounds); + } else if (ClAssociateMissingMetadata) { + Constant *Metadatum; + if (ClAssociateOmnivalidMetadataWhenMissing) { + Metadatum = MInfiniteBoundPtr; + } else { + Metadatum = MVoidNullPtr; + } + auto DummyMetadata = + createDummyMetadata(Val->getType(), Metadatum); + MValueBoundMap[Val] = DummyMetadata; + } else { + LLVM_DEBUG(errs() << "Missing bound(s) for value: " << *Val << '\n'); + assert(0 && "No associated bound(s)"); + } + } + return MValueBoundMap[Val]; +} + +// WARNING: This method only handles constants with a single pointer correctly. +// For constants containing multiple pointers use getAssociatedBoundArray. +Value *SoftBoundCETSPass::getAssociatedBound(Value *pointer_operand) { + ArrayRef bound_array = getAssociatedBounds(pointer_operand); + assert(bound_array.size() == 1 && "getAssociatedBound called on aggregate"); + Value *pointer_bound = bound_array[0]; + + return pointer_bound; +} + +ArrayRef SoftBoundCETSPass::getAssociatedKeys(Value *Val) { + if (!MValueKeyMap.count(Val)) { + if (isa(Val)) { + auto DummyMetadata = + createDummyMetadata(Val->getType(), MConstantIntOne); + MValueKeyMap[Val] = DummyMetadata; + } else if (ClAssociateMissingMetadata) { + Constant *Metadatum; + if (ClAssociateOmnivalidMetadataWhenMissing) + Metadatum = MConstantIntOne; + else + Metadatum = MConstantIntZero; + + auto DummyMetadata = + createDummyMetadata(Val->getType(), Metadatum); + MValueKeyMap[Val] = DummyMetadata; + } else { + LLVM_DEBUG(errs() << "Missing key(s) for value: " << *Val << '\n'); + assert(0 && "No associated key(s)"); + } + } + return MValueKeyMap[Val]; +} + +// WARNING: This method only handles constants with a single pointer correctly. +// For constants containing multiple pointers use getAssociatedKeyArray. +Value *SoftBoundCETSPass::getAssociatedKey(Value *pointer_operand) { + if (!ClTemporalSafety) { + return NULL; + } + + ArrayRef key_array = getAssociatedKeys(pointer_operand); + assert(key_array.size() == 1 && "getAssociatedKey called on aggregate"); + Value *pointer_key = key_array[0]; + + return pointer_key; +} + +ArrayRef SoftBoundCETSPass::getAssociatedLocks(Value *Val) { + if (!MValueLockMap.count(Val)) { + if (isa(Val)) { + auto DummyMetadata = + createDummyMetadata(Val->getType(), MGlobalLockOne); + MValueLockMap[Val] = DummyMetadata; + } else if (ClAssociateMissingMetadata) { + auto DummyMetadata = createDummyLocks(Val->getType()); + MValueLockMap[Val] = DummyMetadata; + } else { + LLVM_DEBUG(errs() << "Missing lock(s) for value: " << *Val << '\n'); + assert(0 && "No associated lock(s)"); + } + } + return MValueLockMap[Val]; +} + +// WARNING: This method only handles constants with a single pointer correctly. +// For constants containing multiple pointers use getAssociatedLockArray. +Value *SoftBoundCETSPass::getAssociatedLock(Value *pointer_operand) { + if (!ClTemporalSafety) { + return NULL; + } + + ArrayRef lock_array = getAssociatedLocks(pointer_operand); + assert(lock_array.size() == 1 && "getAssociatedLock called on aggregate"); + Value *pointer_lock = lock_array[0]; + + return pointer_lock; +} +// +// Method: transformFunctionName +// +// Description: +// +// This function returns the transformed name for the function. This +// function appends softboundcets_ to the input string. + +std::string SoftBoundCETSPass::transformFunctionName(const std::string &str) { + // If the function name starts with this prefix, don't just + // concatenate, but instead transform the string + return "softboundcets_" + str; +} + +void SoftBoundCETSPass::addMemcopyMemsetCheck(CallBase *CB, Function *F) { + if (DISABLE_MEMCOPYCHECK) + return; + + SmallVector Args; + + if (F->getName().find("llvm.memcpy") == 0 || + F->getName().find("llvm.memmove") == 0) { + + Value *Dest = CB->getArgOperand(0); + Value *Src = CB->getArgOperand(1); + Value *Size = CB->getArgOperand(2); + + Args.push_back(Dest); + Args.push_back(Src); + + Value *SizeCast = Size; + if (Size->getType() != MSizetTy) { + SizeCast = CastInst::CreateZExtOrBitCast(Size, MSizetTy, "size.cast", CB); + } + + Args.push_back(SizeCast); + if (ClSpatialSafety) { + Value *DestBase = getAssociatedBase(Dest); + Value *DestBound = getAssociatedBound(Dest); + + Value *SrcBase = getAssociatedBase(Src); + Value *SrcBound = getAssociatedBound(Src); + + Args.push_back(DestBase); + Args.push_back(DestBound); + + Args.push_back(SrcBase); + Args.push_back(SrcBound); + } + + if (ClTemporalSafety) { + Value *DestKey = getAssociatedKey(Dest); + Value *DestLock = getAssociatedLock(Dest); + + Value *SrcKey = getAssociatedKey(Src); + Value *SrcLock = getAssociatedLock(Src); + + Args.push_back(DestKey); + Args.push_back(DestLock); + Args.push_back(SrcKey); + Args.push_back(SrcLock); + } + + CallInst::Create(MemcpyDereferenceCheckFn, Args, "", CB); + return; + } + + if (F->getName().find("llvm.memset") == 0) { + + Args.clear(); + Value *Dest = CB->getArgOperand(0); + // arg 1 is the byte to be written to memory + Value *Size = CB->getArgOperand(2); + + Value *SizeCast = Size; + if (Size->getType() != MSizetTy) { + SizeCast = CastInst::CreateZExtOrBitCast(Size, MSizetTy, "size.cast", CB); + } + auto *DestCast = castToVoidPtr(Dest, CB); + Args.push_back(DestCast); + Args.push_back(SizeCast); + + if (ClSpatialSafety) { + Value *DestBase = getAssociatedBase(Dest); + Value *DestBound = getAssociatedBound(Dest); + Args.push_back(DestBase); + Args.push_back(DestBound); + } + + if (ClTemporalSafety) { + Value *DestKey = getAssociatedKey(Dest); + Value *DestLock = getAssociatedLock(Dest); + + Args.push_back(DestKey); + Args.push_back(DestLock); + } + CallInst::Create(MemsetDereferenceCheckFn, Args, "", CB); + + return; + } +} + +// +// Method: getSizeOfType +// +// Description: This function returns the size of the memory access +// based on the type of the pointer which is being dereferenced. This +// function is used to pass the size of the access in many checks to +// perform byte granularity checking. +// +// Comments: May we should use TargetData instead of m_is_64_bit +// according Criswell's comments. + +// // Method: getSizeOfType // // Description: This function returns how many +// bytes Ty occupies in memory. This // function is used to pass the size of the +// access in many checks to perform // byte granularity checking. +ConstantInt *SoftBoundCETSPass::getSizeOfType(Type *Ty) { + if (!Ty->isSized()) + return ConstantInt::get(Type::getInt64Ty(*C), 0); + return ConstantInt::get(Type::getInt64Ty(*C), DL->getTypeStoreSize(Ty)); +} + +// Method: isStructOperand +// +// +// Description: This function elides the checks for the structure +// accesses. This is safe when there are no casts in the program. +// +bool SoftBoundCETSPass::isStructOperand(Value *pointer_operand) { + GetElementPtrInst *GEP = dyn_cast(pointer_operand); + return GEP && isa(GEP->getSourceElementType()); +} + +// +// This Code is from SAFECode Project. +// Function: createFaultBlock() +// +// Description: +// Create a basic block which will cause the program to terminate. +// +// Inputs: +// F - A pointer to a function to which a faulting basic block +// will be added. +// +static BasicBlock *createFaultBlock(Function *F) { + // + // Create the basic block. + // + BasicBlock *faultBB = BasicBlock::Create(F->getContext(), "fault", F); + + // + // Terminate the basic block with an unreachable instruction. + // + Instruction *UI = new UnreachableInst(F->getContext(), faultBB); + + // + // Add an instruction that will generate a trap. + // + auto &C = F->getContext(); + auto *M = F->getParent(); + + auto DummyFn = + M->getOrInsertFunction("__softboundcets_dummy", Type::getVoidTy(C)); + CallInst::Create(DummyFn, "", UI); + + auto AbortFn = + M->getOrInsertFunction("__softboundcets_abort", Type::getVoidTy(C)); + CallInst::Create(AbortFn, "", UI); + + return faultBB; +} + +// +// +// Method: addSpatialChecks +// +// Description: This function inserts calls to C-handler spatial +// safety check functions and elides the check if the map says it is +// not necessary to check. + +void SoftBoundCETSPass::addSpatialChecks(Instruction *load_store, + std::map &FDCE_map) { + if (!ClSpatialSafety) + return; + + SmallVector args; + Value *pointer_operand = NULL; + Type *pointee_type = NULL; + + if (LoadInst *ldi = dyn_cast(load_store)) { + if (!ClInsertSpatialLoadChecks) + return; + + pointer_operand = ldi->getPointerOperand(); + pointee_type = ldi->getType(); + } + + if (StoreInst *sti = dyn_cast(load_store)) { + if (!ClInsertSpatialStoreChecks) + return; + + pointer_operand = sti->getPointerOperand(); + pointee_type = sti->getValueOperand()->getType(); + } + + assert(pointer_operand && "pointer operand null?"); + + if (!ClDisableSpatialCheckOpt) { + if (ClEliminateStructChecks) { + if (isStructOperand(pointer_operand)) { + return; + } + } + + // If it is a null pointer which is being loaded, then it must seg + // fault, no dereference check here + + if (isa(pointer_operand)) + return; + + // Find all uses of pointer operand, then check if it dominates and + // if so, make a note in the map + + GlobalVariable *gv = dyn_cast(pointer_operand); + if (gv && GLOBALCONSTANTOPT && + !(isa(gv->getType()) || isa(gv->getType()))) { + return; + } + + if (BOUNDSCHECKOPT) { + // Enable dominator based dereference check optimization only when + // suggested + + if (FDCE_map.count(load_store)) { + return; + } + + // FIXME: Add more comments here Iterate over the uses + + for (Value::use_iterator ui = pointer_operand->use_begin(), + ue = pointer_operand->use_end(); + ui != ue; ++ui) { + + Instruction *temp_inst = dyn_cast(*ui); + if (!temp_inst) + continue; + + if (temp_inst == load_store) + continue; + + if (!isa(temp_inst) && !isa(temp_inst)) + continue; + + if (isa(temp_inst)) { + if (temp_inst->getOperand(1) != pointer_operand) { + // When a pointer is a being stored at at a particular + // address, don't elide the check + continue; + } + } + + if (m_dominator_tree->dominates(load_store, temp_inst)) { + if (!FDCE_map.count(temp_inst)) { + FDCE_map[temp_inst] = true; + continue; + } + } + } // Iterating over uses ends + } // BOUNDSCHECKOPT ends + } + + Value *Base = NULL; + Value *Bound = NULL; + + Constant *given_constant = dyn_cast(pointer_operand); + if (given_constant && GLOBALCONSTANTOPT) + return; + + Base = getAssociatedBase(pointer_operand); + Bound = getAssociatedBound(pointer_operand); + + if ((Base == MVoidNullPtr) && (Bound == MInfiniteBoundPtr)) + return; + + Value *bitcast_base = castToVoidPtr(Base, load_store); + args.push_back(bitcast_base); + + Value *bitcast_bound = castToVoidPtr(Bound, load_store); + args.push_back(bitcast_bound); + + Value *cast_pointer_operand_value = + castToVoidPtr(pointer_operand, load_store); + args.push_back(cast_pointer_operand_value); + + // Pushing the size of the type + Value *size_of_type = getSizeOfType(pointee_type); + args.push_back(size_of_type); + + if (isa(load_store)) { + + CallInst::Create(SpatialLoadDereferenceCheckFn, args, "", load_store); + } else { + CallInst::Create(SpatialStoreDereferenceCheckFn, args, "", load_store); + } + + return; +} + +// +// Method: optimizeGlobalAndStackVariables +// +// Description: This function elides temporal safety checks for stack +// and global variables. + +bool SoftBoundCETSPass::optimizeConstsGlobalAndStackVariableChecks( + Instruction *load_store) { + Value *pointer_operand = NULL; + if (isa(load_store)) { + pointer_operand = load_store->getOperand(0); + } else { + pointer_operand = load_store->getOperand(1); + } + + while (true) { + if (isa(pointer_operand)) + return true; + + if (isa(pointer_operand)) + return true; + + if (isa(pointer_operand)) { + if (STACKTEMPORALCHECKOPT) { + return true; + } else { + return false; + } + } + + if (isa(pointer_operand)) { + if (GLOBALTEMPORALCHECKOPT) { + return true; + } else { + return false; + } + } + + if (isa(pointer_operand)) { + BitCastInst *bitcast_inst = dyn_cast(pointer_operand); + pointer_operand = bitcast_inst->getOperand(0); + continue; + } + + if (isa(pointer_operand)) { + GetElementPtrInst *gep = dyn_cast(pointer_operand); + pointer_operand = gep->getOperand(0); + continue; + } else { + return false; + } + } +} + +// +// Method: bbTemporalCheckElimination +// +// Description: This function eliminates the redundant temporal safety +// checks in the basic block +// +// Comments: Describe the algorithm here + +bool SoftBoundCETSPass::bbTemporalCheckElimination( + Instruction *load_store, std::map &BBTCE_map) { + if (!BBDOMTEMPORALCHECKOPT) + return false; + + if (BBTCE_map.count(load_store)) + return true; + + // Check if the operand is a getelementptr, then get the first + // operand and check for all other load/store instructions in the + // current basic block and check if they are pointer operands are + // getelementptrs. If so, check if it is same the pointer being + // checked now + + Value *pointer_operand = getPointerLoadStore(load_store); + + Value *gep_source = NULL; + if (isa(pointer_operand)) { + GetElementPtrInst *ptr_gep = cast(pointer_operand); + gep_source = ptr_gep->getOperand(0); + } else { + gep_source = pointer_operand; + } + + // Iterate over all other instructions in this basic block and look + // for gep_instructions with the same source + BasicBlock *bb_curr = load_store->getParent(); + assert(bb_curr && "bb null?"); + + Instruction *next_inst = getNextInstruction(load_store); + BasicBlock *next_inst_bb = next_inst->getParent(); + while ((next_inst_bb == bb_curr) && (next_inst != bb_curr->getTerminator())) { + + if (isa(next_inst) && OPAQUECALLS) + break; + + if (checkLoadStoreSourceIsGEP(next_inst, gep_source)) { + BBTCE_map[next_inst] = 1; + } + + next_inst = getNextInstruction(next_inst); + next_inst_bb = next_inst->getParent(); + } + return false; +} +// +// Method:getPointerLoadStore +// +// Description: This function obtains the pointer operand which is +// being dereferenced in the memory access. + +Value *SoftBoundCETSPass::getPointerLoadStore(Instruction *load_store) { + Value *pointer_operand = NULL; + if (isa(load_store)) { + pointer_operand = load_store->getOperand(0); + } + + if (isa(load_store)) { + pointer_operand = load_store->getOperand(1); + } + assert((pointer_operand != NULL) && "pointer_operand null"); + return pointer_operand; +} + +// +// Method : checkLoadSourceIsGEP +// +// Description: This function is used to optimize temporal checks by +// identifying the root object of the pointer being dereferenced. If +// the pointer being deferenced is a bitcast or a GEP instruction then +// the source of GEP/bitcast is noted and checked to ascertain whether +// any check to the root object has been performed and not killed. +// +// Comments: +// +// TODO: A detailed algorithm here + +bool SoftBoundCETSPass::checkLoadStoreSourceIsGEP(Instruction *load_store, + Value *gep_source) { + Value *pointer_operand = NULL; + + if (!isa(load_store) && !isa(load_store)) + return false; + + if (isa(load_store)) { + pointer_operand = load_store->getOperand(0); + } + + if (isa(load_store)) { + pointer_operand = load_store->getOperand(1); + } + + assert(pointer_operand && "pointer_operand null?"); + + if (!isa(pointer_operand)) + return false; + + GetElementPtrInst *gep_ptr = dyn_cast(pointer_operand); + assert(gep_ptr && "gep_ptr null?"); + + Value *gep_ptr_operand = gep_ptr->getOperand(0); + + if (gep_ptr_operand == gep_source) + return true; + + return false; +} + +// +// Method: funcTemporalCheckElimination +// +// Description: This function elides temporal checks for by performing +// root object identification at the function level. + +bool SoftBoundCETSPass::funcTemporalCheckElimination( + Instruction *load_store, std::map &FTCE_map) { + if (!FUNCDOMTEMPORALCHECKOPT) + return false; + + if (FTCE_map.count(load_store)) + return true; + + Value *pointer_operand = getPointerLoadStore(load_store); + + Value *gep_source = NULL; + if (isa(pointer_operand)) { + + GetElementPtrInst *ptr_gep = dyn_cast(pointer_operand); + assert(ptr_gep && "[bbTemporalCheckElimination] gep_inst null?"); + gep_source = ptr_gep->getOperand(0); + } else { + gep_source = pointer_operand; + } + + BasicBlock *bb_curr = load_store->getParent(); + assert(bb_curr && "bb null?"); + + std::set bb_visited; + std::queue bb_worklist; + + bb_worklist.push(bb_curr); + BasicBlock *bb = NULL; + while (bb_worklist.size() != 0) { + + bb = bb_worklist.front(); + assert(bb && "Not a BasicBlock?"); + + bb_worklist.pop(); + if (bb_visited.count(bb)) { + continue; + } + bb_visited.insert(bb); + + bool break_flag = false; + + // Iterating over the successors and adding the successors to the + // work list + + // if this is the current basic block under question + if (bb == bb_curr) { + // bbTemporalCheckElimination should handle this + Instruction *next_inst = getNextInstruction(load_store); + BasicBlock *next_inst_bb = next_inst->getParent(); + while ((next_inst_bb == bb_curr) && + (next_inst != bb_curr->getTerminator())) { + + if (isa(next_inst) && OPAQUECALLS) { + break_flag = true; + break; + } + + if (checkLoadStoreSourceIsGEP(next_inst, gep_source)) { + if (m_dominator_tree->dominates(load_store, next_inst)) { + FTCE_map[next_inst] = 1; + } + } + + next_inst = getNextInstruction(next_inst); + next_inst_bb = next_inst->getParent(); + } + } else { + for (BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i) { + Instruction *new_inst = dyn_cast(i); + if (isa(new_inst) && OPAQUECALLS) { + break_flag = true; + break; + } + + if (checkLoadStoreSourceIsGEP(new_inst, gep_source)) { + + if (m_dominator_tree->dominates(load_store, new_inst)) { + FTCE_map[new_inst] = 1; + } + } + } // Iterating over the instructions in the basic block ends + } + + for (succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) { + + if (break_flag) + break; + + BasicBlock *next_bb = cast(*si); + bb_worklist.push(next_bb); + } + } // Worklist algorithm ends + return false; +} + +bool SoftBoundCETSPass::optimizeTemporalChecks( + Instruction *load_store, std::map &BBTCE_map, + std::map &FTCE_map) { + if (optimizeConstsGlobalAndStackVariableChecks(load_store)) + return true; + + if (bbTemporalCheckElimination(load_store, BBTCE_map)) + return true; + + if (funcTemporalCheckElimination(load_store, FTCE_map)) + return true; + + return false; +} + +void SoftBoundCETSPass::addTemporalChecks(Instruction *load_store, + std::map &BBTCE_map, + std::map &FTCE_map) { + SmallVector args; + Value *pointer_operand = NULL; + + if (!ClTemporalSafety) + return; + + if (!ClDisableTemporalCheckOpt) { + if (optimizeTemporalChecks(load_store, BBTCE_map, FTCE_map)) + return; + } + + if (isa(load_store)) { + if (!ClInsertTemporalLoadChecks) + return; + + LoadInst *ldi = dyn_cast(load_store); + assert(ldi && "not a load instruction"); + pointer_operand = ldi->getPointerOperand(); + } + + if (isa(load_store)) { + if (!ClInsertTemporalStoreChecks) + return; + + StoreInst *sti = dyn_cast(load_store); + assert(sti && "not a store instruction"); + // The pointer where the element is being stored is the second + // operand + pointer_operand = sti->getOperand(1); + } + + assert(pointer_operand && "pointer_operand null?"); + + if (!ClDisableTemporalCheckOpt) { + /* Find all uses of pointer operand, then check if it + * dominates and if so, make a note in the map + */ + + if (TEMPORALBOUNDSCHECKOPT) { + /* Enable dominator based dereference check optimization only + * when suggested + */ + + if (FTCE_map.count(load_store)) { + return; + } + + /* iterate over the uses */ + for (Value::use_iterator ui = pointer_operand->use_begin(), + ue = pointer_operand->use_end(); + ui != ue; ++ui) { + + Instruction *temp_inst = cast(*ui); + if (!temp_inst) + continue; + + if (temp_inst == load_store) + continue; + + if (!isa(temp_inst) && !isa(temp_inst)) + continue; + + if (isa(temp_inst)) { + if (temp_inst->getOperand(1) != pointer_operand) { + /* when a pointer is a being stored at at a particular + * address, don't elide the check + */ + continue; + } + } + + if (m_dominator_tree->dominates(load_store, temp_inst)) { + if (!FTCE_map.count(temp_inst)) { + FTCE_map[temp_inst] = true; + continue; + } + } + } /* Iterating over uses ends */ + } /* TEMPORALBOUNDSCHECKOPT ends */ + } + + Value *Key = getAssociatedKey(pointer_operand); + Value *Lock = getAssociatedLock(pointer_operand); + + auto *ConstantLock = dyn_cast(Lock); + // we do not need checks if we know its omnivalid metadata + if (ConstantLock && (Key == MConstantIntOne) && + (MGlobalLockOnes.count(ConstantLock) > 0)) + return; + + args.push_back(Lock); + + args.push_back(Key); + +#ifdef SOFTBOUNDCETS_CHK_INTRINSIC + + if (ClInsertCheckIntrinsics) { + Module *M = load_store->getParent()->getParent()->getParent(); + Type *Tys[] = {m_void_ptr_type, m_key_type, m_void_ptr_type, + m_void_ptr_type}; + Function *temporal_chk_function = + Intrinsic::getDeclaration(M, Intrinsic::sbcets_temporalchk, Tys); + + CallInst::Create(temporal_chk_function, args, "", load_store); + + return; + } +#endif + + if (isa(load_store)) { + CallInst::Create(TemporalLoadDereferenceCheckFn, args, "", load_store); + } else { + CallInst::Create(TemporalStoreDereferenceCheckFn, args, "", load_store); + } + return; +} + +void SoftBoundCETSPass::addDereferenceChecks(Function *func) { + Function &F = *func; + + if (func->isVarArg()) + return; + + if (ClPropagateMetadataOnly) + return; + + if (Blacklist && + Blacklist->inSection("softboundcets", "fun", func->getName(), "*")) + return; + + std::vector CheckWorkList; + std::map ElideSpatialCheck; + std::map ElideTemporalCheck; + + // identify all the instructions where we need to insert the spatial checks + for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) { + + Instruction *I = &*i; + + if (!MPresentInOriginal.count(I)) { + continue; + } + // add check optimizations here + // add checks for memory fences and atomic exchanges + if (isa(I) || isa(I)) { + CheckWorkList.push_back(I); + } + if (isa(I) || isa(I)) { + assert(0 && "Atomic Instructions not handled"); + } + } + +#if 0 + // spatial check optimizations here + + for(std::vector::iterator i = CheckWorkList.begin(), + e = CheckWorkList.end(); i!= e; ++i){ + + Instruction* inst = *i; + Value* pointer_operand = NULL; + + if(ElideSpatialCheck.count(inst)) + continue; + + if(isa(inst)){ + LoadInst* ldi = dyn_cast(inst); + pointer_operand = ldi->getPointerOperand(); + } + if(isa(inst)){ + StoreInst* st = dyn_cast(inst); + pointer_operand = st->getOperand(1); + } + + for(Value::use_iterator ui = pointer_operand->use_begin(), + ue = pointer_operand->use_end(); + ui != ue; ++ui){ + + Instruction* use_inst = dyn_cast(*ui); + if(!use_inst || (use_inst == inst)) + continue; + + if(!isa(use_inst) && !isa(use_inst)) + continue; + + if(isa(use_inst)){ + if(use_inst->getOperand(1) != pointer_operand) + continue; + } + + if(m_dominator_tree->dominates(inst, use_inst)){ + if(!ElideSpatialCheck.count(use_inst)) + ElideSpatialCheck[use_inst] = true; + } + } + } + +#endif + + // Temporal Check Optimizations + +#ifdef INSERT_CHECKS_DIRECTLY + // the following code adds checks directly into the program. It bypasses the + // *_check functions in the runtime library. This can be helpful if LTO does + // not work properly + for (std::vector::iterator i = CheckWorkList.begin(), + e = CheckWorkList.end(); + i != e; ++i) { + + Instruction *Inst = *i; + + Value *pointer_operand = NULL; + + if (isa(Inst)) { + LoadInst *ldi = dyn_cast(Inst); + pointer_operand = ldi->getPointerOperand(); + } + + if (isa(Inst)) { + StoreInst *st = dyn_cast(Inst); + pointer_operand = st->getOperand(1); + } + + if (isa(pointer_operand)) + continue; + + Value *pointer_base = getAssociatedBase(pointer_operand); + Value *pointer_bound = getAssociatedBound(pointer_operand); + + Value *pointer_key = getAssociatedKey(pointer_operand); + Value *pointer_lock = getAssociatedLock(pointer_operand); + + Builder->SetInsertPoint(Inst); + + Value *cast_pointer_lock = + Builder->CreateBitCast(pointer_lock, m_sizet_ptr_type); + Value *lock_load = Builder->CreateLoad(cast_pointer_lock, ""); + Value *cmp_eq = Builder->CreateICmpNE(pointer_key, lock_load); + + Value *cast_pointer = + Builder->CreateBitCast(pointer_operand, m_void_ptr_type); + + Value *sizeofType = getSizeOfType(pointer_operand->getType()); + + Value *Cmp1 = Builder->CreateICmpULT(cast_pointer, pointer_base); + Value *sub = Builder->CreatePtrDiff(pointer_bound, cast_pointer); + + Value *Cmp2 = Builder->CreateICmpUGT(sizeofType, sub); + + Value *Or = Builder->CreateOr(Cmp1, Cmp2); + + Value *Or2 = Builder->CreateOr(Or, cmp_eq); + +#if 0 + FunctionCallee call_print_metadata = Inst->getParent()->getParent()->getParent()->getOrInsertFunction("__softboundcets_print_metadata"); + Builder->CreateCall(call_print_metadata, {pointer_base, pointer_bound, cast_pointer, pointer_key, cast_pointer_lock}); + + FunctionCallee intermediate = Inst->getParent()->getParent()->getParent()->getOrInsertFunction("__softboundcets_intermediate"); + Builder->CreateCall(intermediate, {Cmp1, Cmp2, cmp_eq, lock_load}); +#endif + + BasicBlock *OldBB = Inst->getParent(); + BasicBlock *Cont = OldBB->splitBasicBlock(Inst); + OldBB->getTerminator()->eraseFromParent(); + + assert(m_faulting_block.count(Inst->getParent()->getParent())); + BasicBlock *faultBB = m_faulting_block[Inst->getParent()->getParent()]; + + BranchInst::Create(faultBB, Cont, Or2, OldBB); + + continue; + } + + return; +#endif + + m_dominator_tree = &getAnalysis(*func).getDomTree(); + + /* intra-procedural load dererference check elimination map */ + std::map func_deref_check_elim_map; + std::map func_temporal_check_elim_map; + + /* WorkList Algorithm for adding dereference checks. Each basic + * block is visited only once. We start by visiting the current + * basic block, then pushing all the successors of the current + * basic block on to the queue if it has not been visited + */ + + std::set bb_visited; + std::queue bb_worklist; + Function::iterator bb_begin = func->begin(); + + BasicBlock *bb = dyn_cast(bb_begin); + assert(bb && "Not a basic block and I am adding dereference checks?"); + bb_worklist.push(bb); + + while (bb_worklist.size() != 0) { + + bb = bb_worklist.front(); + assert(bb && "Not a BasicBlock?"); + bb_worklist.pop(); + + if (bb_visited.count(bb)) { + /* Block already visited */ + continue; + } + + /* If here implies basic block not visited */ + /* Insert the block into the set of visited blocks */ + bb_visited.insert(bb); + + /* Iterating over the successors and adding the successors to + * the worklist + */ + for (succ_iterator si = succ_begin(bb), se = succ_end(bb); si != se; ++si) { + + BasicBlock *next_bb = *si; + assert( + next_bb && + "Not a basic block and I am adding to the base and bound worklist?"); + bb_worklist.push(next_bb); + } + + /* basic block load deref check optimization */ + std::map bb_deref_check_map; + std::map bb_temporal_check_elim_map; + /* structure check optimization */ + std::map bb_struct_check_opt; + + for (BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i) { + Value *v1 = dyn_cast(i); + Instruction *new_inst = dyn_cast(i); + + /* Do the dereference check stuff */ + if (!MPresentInOriginal.count(v1)) + continue; + + if (isa(new_inst) || isa(new_inst)) { + + addSpatialChecks(new_inst, func_deref_check_elim_map); + addTemporalChecks(new_inst, bb_temporal_check_elim_map, + func_temporal_check_elim_map); + continue; + } + + /* check call through function pointers */ + if (CALLCHECKS && isa(new_inst)) { + + SmallVector args; + CallInst *call_inst = dyn_cast(new_inst); + Value *tmp_base = NULL; + Value *tmp_bound = NULL; + + assert(call_inst && "call instruction null?"); + + if (!INDIRECTCALLCHECKS) + continue; + + /* TODO:URGENT : indirect function call checking commented + * out for the time being to test other aspect of the code, + * problem was with spec benchmarks perl and h264. They were + * primarily complaining that the use of a function did not + * have base and bound in the map + */ + + /* here implies its an indirect call */ + Value *indirect_func_called = call_inst->getOperand(0); + + Constant *func_constant = dyn_cast(indirect_func_called); + if (func_constant) { + getConstantExprBaseBound(func_constant, tmp_base, tmp_bound); + } else { + tmp_base = getAssociatedBase(indirect_func_called); + tmp_bound = getAssociatedBound(indirect_func_called); + } + /* Add BitCast Instruction for the base */ + Value *bitcast_base = castToVoidPtr(tmp_base, new_inst); + args.push_back(bitcast_base); + + /* Add BitCast Instruction for the bound */ + Value *bitcast_bound = castToVoidPtr(tmp_bound, new_inst); + args.push_back(bitcast_bound); + Value *pointer_operand_value = + castToVoidPtr(indirect_func_called, new_inst); + args.push_back(pointer_operand_value); + CallInst::Create(CallDereferenceCheckFn, args, "", new_inst); + continue; + } /* Call check ends */ + } + } +} + +void SoftBoundCETSPass::renameFunctions(Module &module) { + bool change = false; + + do { + change = false; + for (Module::iterator ff_begin = module.begin(), ff_end = module.end(); + ff_begin != ff_end; ++ff_begin) { + + Function *func_ptr = dyn_cast(ff_begin); + + if (m_func_transformed.count(func_ptr->getName()) || + isFunctionNotToInstrument(func_ptr->getName())) { + continue; + } + + m_func_transformed[func_ptr->getName()] = true; + m_func_transformed[transformFunctionName(func_ptr->getName().str())] = + true; + bool is_external = func_ptr->isDeclaration(); + renameFunctionName(func_ptr, module, is_external); + change = true; + break; + } + } while (change); +} + +/* Renames a function by changing the function name to softboundcets_* + for only those functions have wrappers + */ + +void SoftBoundCETSPass::renameFunctionName(Function *F, Module &M, + bool External) { + Type *RetTy = F->getReturnType(); + const FunctionType *FTy = F->getFunctionType(); + std::vector Params; + auto FName = F->getName(); + + if (!MFunctionWrappersAvailable.count(FName)) + return; + + LLVM_DEBUG(dbgs() << "\n======================\nWrapping function with " + "SBCETS runtime handler: " + << FName << "\n"); + + SmallVector ParamAttrsVec; + + // #if 0 + + // const AttrListPtr& pal = F->getAttributes(); + // if(Attributes attrs = pal.getRetAttributes()) + // param_attrs_vec.push_back(AttributeWithIndex::get(0, attrs)); + // #endif + + for (Argument &Arg : F->args()) { + + Params.push_back(Arg.getType()); + // #if 0 + // if(Attributes attrs = pal.getParamAttributes(arg_index)) + // param_attrs_vec.push_back(AttributeWithIndex::get(params.size(), + // attrs)); + // #endif + } + + // check if we have loaded the rt-lib bitcode into the module already + auto *FunctionWrapper = M.getFunction(transformFunctionName(FName.str())); + + // if not, we create a function declaration which can be resolved during + // linking + if (!FunctionWrapper) { + FunctionType *FWrapperTy = + FunctionType::get(RetTy, Params, FTy->isVarArg()); + FunctionWrapper = Function::Create(FWrapperTy, F->getLinkage(), + transformFunctionName(FName.str())); + FunctionWrapper->copyAttributesFrom(F); + F->getParent()->getFunctionList().push_back(FunctionWrapper); + } + + // reimplement doRAUW for Constant Users of Function + // where we want to replace the Function with the FunctionWrapper + // NOTE: replaceUsesWithIf does not work for Constants + for (auto UI = F->use_begin(), E = F->use_end(); UI != E;) { + Use &U = *UI; + ++UI; + + if (auto *C = dyn_cast(U.getUser())) { + if (!isa(C)) { + C->handleOperandChange(F, FunctionWrapper); + continue; + } + U.set(FunctionWrapper); + } + } + + F->replaceUsesWithIf(FunctionWrapper, [this](Use &U) -> bool { + auto *Usr = U.getUser(); + auto *I = dyn_cast(Usr); + if (I) { + return InstrumentedFunctions.contains(I->getFunction()); + } + return false; + }); +} + +void SoftBoundCETSPass::handleAlloca(AllocaInst *AI, Value *Key, Value *Lock, + Value *func_xmm_key_lock) { + if (ClSpatialSafety) { + auto *InsertPoint = AI->getNextNode(); + assert(InsertPoint && "Alloca is the last instruction in the basic block?"); + IRBuilder<> IRB(InsertPoint); + + // For any alloca, the base is a bitcast of the alloca and the bound is a + // bitcast of alloca + 1. + Value *Base = castToVoidPtr(AI, IRB); + + Value *Idx; + if (AI->getNumOperands() == 0) { + if (m_is_64_bit) { + Idx = ConstantInt::get(Type::getInt64Ty(AI->getType()->getContext()), 1, + false); + } else { + Idx = ConstantInt::get(Type::getInt32Ty(AI->getType()->getContext()), 1, + false); + } + } else { + Idx = AI->getOperand(0); + } + + Value *Bound = IRB.CreateGEP(AI->getAllocatedType(), AI, Idx, "mtmp"); + Bound = castToVoidPtr(Bound, IRB); + + associateBaseBound(AI, Base, Bound); + } + + if (ClTemporalSafety) { + associateKeyLock(AI, Key, Lock); + } +} + +void SoftBoundCETSPass::handleVectorStore(StoreInst *Store) { + Value *Val = Store->getOperand(0); + Value *StoreDest = Store->getOperand(1); + Instruction *InsertAt = getNextInstruction(Store); + + SmallVector ExtractedBases, ExtractedBounds, ExtractedKeys, + ExtractedLocks; + + if (ClSpatialSafety) { + ExtractedBases = extractVectorBases(Val, InsertAt); + ExtractedBounds = extractVectorBounds(Val, InsertAt); + } + if (ClTemporalSafety) { + ExtractedKeys = extractVectorKeys(Val, InsertAt); + ExtractedLocks = extractVectorLocks(Val, InsertAt); + } + + const VectorType *VectorTy = dyn_cast(Val->getType()); + ElementCount NumElements = VectorTy->getElementCount(); + + Value *PtrOpBitcast = castToVoidPtr(StoreDest, InsertAt); + SmallVector Args; + for (uint64_t i = 0; i < NumElements.getValue(); i++) { + + Args.clear(); + Constant *Idx = ConstantInt::get(Type::getInt32Ty(Store->getContext()), i); + Args.push_back(PtrOpBitcast); + + if (ClSpatialSafety) { + Value *PtrBase = ExtractedBases[i]; + Value *PtrBound = ExtractedBounds[i]; + Args.push_back(PtrBase); + Args.push_back(PtrBound); + } + if (ClTemporalSafety) { + Value *PtrKey = ExtractedKeys[i]; + Value *PtrLock = ExtractedLocks[i]; + Args.push_back(PtrKey); + Args.push_back(PtrLock); + } + + Args.push_back(Idx); + + CallInst::Create(StoreVectorMetadataFn, Args, "", InsertAt); + } +} + +// handles vector which contains pointers +// iterates over each element of vector and inserts function call to load +// metadata of pointer via LUT this metadata is then carried around with the +// vector in a separate vector we need to use other vectors as we otherwise +// cannot associate the pointer extracted from the vector at runtime with its +// metadata as we don't know at compile time the index of the extractelement +// instruction +void SoftBoundCETSPass::handleVectorLoad(LoadInst *Load) { + + // It should be a fixed vector if here + const FixedVectorType *VectorTy = cast(Load->getType()); + + Value *PointerOp = Load->getPointerOperand(); + Instruction *InsertAt = getNextInstruction(Load); + + Instruction *FirstFunctionInst = + dyn_cast(Load->getParent()->getParent()->begin()->begin()); + assert(FirstFunctionInst && + "function doesn't have any instruction and there is load???"); + + uint64_t NumElements = VectorTy->getElementCount().getValue(); + + SoftBoundCETSMetadata Metadata; + + insertVectorMetadataLoad(PointerOp, Metadata, InsertAt, NumElements); + + if (ClSpatialSafety) + associateBaseBound(Load, Metadata.Base, Metadata.Bound); + + if (ClTemporalSafety) + associateKeyLock(Load, Metadata.Key, Metadata.Lock); +} + +void SoftBoundCETSPass::handleAggregateStore(StoreInst *Store) { + Value *StoreVal = Store->getOperand(0); + Type *StoreValTy = StoreVal->getType(); + Value *StoreDest = Store->getOperand(1); + Instruction *InsertAt = getNextInstruction(Store); + /** (ds) + * When writing a aggregate with pointers to memory, + * the metadata for each contained pointer is stored in the runtime + * datastructure. Therefore we must compute the addresses of each pointer + * after being stored. This is achieved by generating GEP instructions using + * the store destination as base. Finally, a call to store metadata in the + * runtime is generated for each pointer. + */ + + // generate GEPs for each contained pointer + SmallVector GEPs; + + generateAggregateGEPs(StoreDest, StoreValTy, InsertAt, GEPs); + + ArrayRef Bases = getAssociatedBases(StoreVal); + ArrayRef Bounds = getAssociatedBounds(StoreVal); + ArrayRef Keys = getAssociatedKeys(StoreVal); + ArrayRef Locks = getAssociatedLocks(StoreVal); + + assert(Bases.size() == GEPs.size() && Bounds.size() == GEPs.size() && + Keys.size() == GEPs.size() && Locks.size() == GEPs.size() && + "The number of metadata entries must match the number of generated " + "GEPs"); + + for (unsigned J = 0; J < GEPs.size(); ++J) { + auto *GEP = GEPs[J]; + auto *GEPElementTy = cast(GEP->getType())->getElementType(); + if (GEPElementTy->isVectorTy()) { + insertVectorMetadataStore(GEP, Bases[J], Bounds[J], Keys[J], Locks[J], + InsertAt); + } else { + insertPointerMetadataStore(GEP, Bases[J], Bounds[J], Keys[J], Locks[J], + InsertAt); + } + } +} + +void SoftBoundCETSPass::handleStore(StoreInst *Store) { + Value *StoreVal = Store->getOperand(0); + Type *StoreValTy = StoreVal->getType(); + Value *StoreDest = Store->getOperand(1); + Instruction *InsertAt = getNextInstruction(Store); + if (!isTypeWithPointers(StoreValTy)) + return; + + /* If a pointer is being stored, then the base and bound + * corresponding to the pointer must be stored in the shadow space + */ + + /* if it is a global expression being stored, then add add + * suitable base and bound + */ + + if (isVectorWithPointers(StoreValTy)) { + handleVectorStore(Store); + } else if (isa(StoreValTy)) { + Value *Base = NULL; + Value *Bound = NULL; + if (ClSpatialSafety) { + Base = getAssociatedBase(StoreVal); + Bound = getAssociatedBound(StoreVal); + } + + Value *Key = NULL; + Value *Lock = NULL; + if (ClTemporalSafety) { + Key = getAssociatedKey(StoreVal); + Lock = getAssociatedLock(StoreVal); + } + + insertPointerMetadataStore(StoreDest, Base, Bound, Key, Lock, InsertAt); + } else if (StoreValTy->isAggregateType()) { + handleAggregateStore(Store); + } else { + assert(0 && "Storing value with pointers that is unhandled"); + } +} + +// Currently just a placeholder for functions introduced by us +bool SoftBoundCETSPass::checkIfFunctionOfInterest(Function *func) { + if (isFunctionNotToInstrument(func->getName())) + return false; + + if (func->isDeclaration()) + return false; + + /* TODO: URGENT: Need to do base and bound propagation in variable + * argument functions + */ +#if 0 + if(func.isVarArg()) + return false; +#endif + + return true; +} + +Instruction *SoftBoundCETSPass::getGlobalsInitializerInsertPoint(Module &M) { + Function *GlobalsInitFn = M.getFunction(kSoftBoundCETSGlobalsInitializerName); + + if (!GlobalsInitFn) { + // Create the globals initializer and call it from our global constructor. + auto *CtorFn = M.getFunction(kSoftBoundCETSCtorName); + assert(CtorFn && "Global constructor not inserted?"); + GlobalsInitFn = + createSanitizerCtor(M, kSoftBoundCETSGlobalsInitializerName); + IRBuilder<> IRB(CtorFn->getEntryBlock().getTerminator()); + IRB.CreateCall(GlobalsInitFn, {}); + } + + return GlobalsInitFn->getEntryBlock().getTerminator(); +} + +bool SoftBoundCETSPass::isVaargGep(GetElementPtrInst *GepInst) { + /* (av) + We want to identify all getelementptr instructions that load the + pointer to overflow_arg_area or the reg_save_area. That is e.g. + %0 = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* + %arraydecay2, i32 0, i32 3 or %overflow_arg_area_p = getelementptr inbounds + %struct.__va_list_tag, %struct.__va_list_tag* %arraydecay2, i32 0, i32 2 As + this struct could be part of another struct or array, we have to iteratively + follow the indices of the instruction. + */ + + // return false; + auto GEPIndices = GepInst->getNumIndices(); + if (GEPIndices < 2) + return false; + + auto *CompTy = GepInst->getSourceElementType(); + + auto *Idx = GepInst->idx_begin(); + + Value *ThirdLastIdx = NULL; + Value *SecondLastIdx = *Idx; + + Idx++; + + for (unsigned int NextIndexPos = 2; Idx != GepInst->idx_end(); + Idx++, NextIndexPos++) { + + if (!CompTy) + return false; + + // integer value of index + uint64_t LastIndex = dyn_cast(*Idx) + ? dyn_cast(*Idx)->getZExtValue() + : 0; + + // stop if we followed all but the last two indices + // check if the second to last is 0 and the last is 2 or 3 + if (NextIndexPos >= GEPIndices) { + if (LastIndex != 2 && LastIndex != 3) + return false; + break; + } + + ThirdLastIdx = SecondLastIdx; + SecondLastIdx = *Idx; + + if (NextIndexPos > 1) { + CompTy = GetElementPtrInst::getTypeAtIndex(CompTy, ThirdLastIdx); + } + } + + StructType *StTy = dyn_cast_or_null(CompTy); + + return StTy && StTy->hasName() && + StTy->getName().contains("struct.__va_list_tag"); +} + +void SoftBoundCETSPass::varargAssociatePointerLoads(Instruction *VarArgInst) { + + SmallVector LoadInsts; + std::set Visited; + collectVarargPointerLoads(VarArgInst, LoadInsts, Visited); + + for (auto *LI : LoadInsts) { + associateOmnivalidMetadata(LI); + } +} + +void SoftBoundCETSPass::handleGEP(GetElementPtrInst *GEP) { + Value *GEPPtrOp = GEP->getPointerOperand(); + // we need to account for + // https://llvm.org/docs/LangRef.html#vector-of-pointers: in short; if a + // vector of offsets is used as indices, the result of the GEP is a vector. As + // we do not calculate subbounds for gep vectors, we need to associate each pointer in the + // resulting vector with the metadata of the GEPPtrOp + FixedVectorType *FixedVectorTy = dyn_cast(GEP->getType()); + if (FixedVectorTy && !isa(GEPPtrOp->getType())) { + auto VectorSize = FixedVectorTy->getElementCount().getValue(); + if (ClSpatialSafety) { + auto *Base = getAssociatedBase(GEPPtrOp); + auto *Bound = getAssociatedBound(GEPPtrOp); + SmallVector Bases(VectorSize, Base); + SmallVector Bounds(VectorSize, Bound); + auto *BaseVector = createMetadataVector(Bases, GEP); + auto *BoundVector = createMetadataVector(Bounds, GEP); + associateBaseBound(GEP, BaseVector, BoundVector); + } + if (ClTemporalSafety) { + auto *Key = getAssociatedKey(GEPPtrOp); + auto *Lock = getAssociatedLock(GEPPtrOp); + SmallVector Keys(VectorSize, Key); + SmallVector Locks(VectorSize, Lock); + auto *KeyVector = createMetadataVector(Keys, GEP); + auto *LockVector = createMetadataVector(Locks, GEP); + associateKeyLock(GEP, KeyVector, LockVector); + } + } else if (ClIntraObjectBounds && !FixedVectorTy) { + + auto *GEPSrcTy = GEP->getSourceElementType(); + + // only narrow bounds if the GEP indexes a struct or a vector + // as detailed in the original paper, we don't want to narrow bounds if we + // index an array or only the pointer + bool GEPIndexesOnlyPtrAndArrays = true; + if (GEPSrcTy->isStructTy() || GEPSrcTy->isVectorTy()) { + if (GEP->getNumIndices() > 1) + GEPIndexesOnlyPtrAndArrays = false; + } + + std::vector GEPIndices(GEP->idx_begin(), GEP->idx_end()); + for (; (GEPIndices.size() > 2); GEPIndices.pop_back()) { + + auto IndexSlice = + std::vector(GEPIndices.begin() + 1, GEPIndices.end() - 1); + auto *IndexedTy = GetElementPtrInst::getIndexedType(GEPSrcTy, GEPIndices); + + if (IndexedTy->isStructTy() || IndexedTy->isVectorTy()) { + GEPIndexesOnlyPtrAndArrays = false; + break; + } + } + + if (!GEPIndexesOnlyPtrAndArrays) { + + IRBuilder<> IRB(getNextInstruction(GEP)); + + // Calculate max(original base of GEPPtrOp, GEP) + auto *OriginalBase = getAssociatedBase(GEPPtrOp); + // we use the GEP as Base if we haven't removed any indices. + // otherwise we create a new GEP for the base + auto *GEPBase = (GEPIndices.size() == GEP->getNumIndices()) + ? GEP + : IRB.CreateGEP(GEPSrcTy, GEPPtrOp, GEPIndices); + auto *GEPCast = + IRB.CreateBitCast(GEPBase, MVoidPtrTy, GEP->getName() + ".voidptr"); + auto *CmpBases = IRB.CreateICmpUGT(OriginalBase, GEPCast); + auto *NewBase = IRB.CreateSelect(CmpBases, OriginalBase, GEPCast); + + // Calculate min(original bound of GEPPtrOp, GEP+1) + auto *OriginalBound = getAssociatedBound(GEPPtrOp); + auto *GEPBound = IRB.CreateGEP(GEPBase, IRB.getInt32(1)); + auto *GEPBoundCast = + IRB.CreateBitCast(GEPBound, MVoidPtrTy, GEP->getName() + ".voidptr"); + auto *CmpBounds = IRB.CreateICmpULT(OriginalBound, GEPBoundCast); + auto *NewBound = IRB.CreateSelect(CmpBounds, OriginalBound, GEPBoundCast); + + associateBaseBound(GEP, NewBase, NewBound); + + auto *Key = getAssociatedKey(GEPPtrOp); + auto *Lock = getAssociatedLock(GEPPtrOp); + associateKeyLock(GEP, Key, Lock); + } else + propagateMetadata(GEPPtrOp, GEP); + } else + propagateMetadata(GEPPtrOp, GEP); +} + +void SoftBoundCETSPass::handleShuffleVector(ShuffleVectorInst *SV) { + auto *V0 = SV->getOperand(0); + auto *V1 = SV->getOperand(0); + auto Mask = SV->getShuffleMask(); + + if (ClSpatialSafety) { + auto *Bases0 = getAssociatedBase(V0); + auto *Bases1 = getAssociatedBase(V1); + auto *Bounds0 = getAssociatedBound(V0); + auto *Bounds1 = getAssociatedBound(V1); + + auto *NewBases = + new ShuffleVectorInst(Bases0, Bases1, Mask, "shuffle_vector.bases", SV); + auto *NewBounds = new ShuffleVectorInst(Bounds0, Bounds1, Mask, + "shuffle_vector.bounds", SV); + associateBaseBound(SV, NewBases, NewBounds); + } + if (ClTemporalSafety) { + auto *Keys0 = getAssociatedKey(V0); + auto *Keys1 = getAssociatedKey(V1); + auto *Locks0 = getAssociatedLock(V0); + auto *Locks1 = getAssociatedLock(V1); + + auto *NewKeys = + new ShuffleVectorInst(Keys0, Keys1, Mask, "shuffle_vector.keys", SV); + auto *NewLocks = + new ShuffleVectorInst(Locks0, Locks1, Mask, "shuffle_vector.locks", SV); + associateKeyLock(SV, NewKeys, NewLocks); + } +} + +void SoftBoundCETSPass::handleMaskedVectorLoad(CallBase *CB) { + + auto *InsertAt = getNextInstruction(CB); + IRBuilder<> IRB(InsertAt); + + auto *LoadSrc = CB->getArgOperand(0); + auto *LoadSrcBitcast = castToVoidPtr(LoadSrc, InsertAt); + + auto *Mask = CB->getArgOperand(2); + auto *MaskTy = dyn_cast(Mask->getType()); + auto VecSize = MaskTy->getNumElements(); + auto *PassThru = CB->getArgOperand(3); + + SmallVector Bases; + SmallVector Bounds; + SmallVector Keys; + SmallVector Locks; + + for (unsigned VecIdx = 0; VecIdx < VecSize; VecIdx++) { + + ConstantInt *IdxArg = ConstantInt::get(Type::getInt32Ty(*C), VecIdx); + Value *MaskValAtIdx = + ExtractElementInst::Create(Mask, IdxArg, "", InsertAt); + + SmallVector Args; + + Args.push_back(LoadSrcBitcast); + + Args.push_back(IdxArg); + Args.push_back(MaskValAtIdx); + + if (ClSimpleMetadataMode) { + if (ClSpatialSafety) { + Value *Base = CallInst::Create(MaskedLoadVectorMetadataBaseFn, Args, "", + InsertAt); + Value *Bound = CallInst::Create(MaskedLoadVectorMetadataBoundFn, Args, + "", InsertAt); + Bases.push_back(Base); + Bounds.push_back(Bound); + } + if (ClTemporalSafety) { + Value *Lock = CallInst::Create(MaskedLoadVectorMetadataLockFn, Args, "", + InsertAt); + Value *Key = + CallInst::Create(MaskedLoadVectorMetadataKeyFn, Args, "", InsertAt); + + Keys.push_back(Key); + Locks.push_back(Lock); + } + + } else { + + auto *MetadataPtr = IRB.CreateCall(LoadMaskedVectorMetadataPtrFn, Args); + + unsigned KeyIdx = 0; + unsigned LockIdx = 1; + + if (ClSpatialSafety) { + auto *BasePtr = IRB.CreateStructGEP(MetadataPtr, 0, "baseptr"); + Value *Base = IRB.CreateLoad(BasePtr, "base"); + Bases.push_back(Base); + auto *BoundPtr = IRB.CreateStructGEP(MetadataPtr, 1, "boundptr"); + Value *Bound = IRB.CreateLoad(BoundPtr, "bound"); + Bounds.push_back(Bound); + + KeyIdx = 2; + LockIdx = 3; + } + if (ClTemporalSafety) { + auto *KeyPtr = IRB.CreateStructGEP(MetadataPtr, KeyIdx, "keyptr"); + Value *Key = IRB.CreateLoad(KeyPtr, "key"); + Keys.push_back(Key); + auto *LockPtr = IRB.CreateStructGEP(MetadataPtr, LockIdx, "lockptr"); + Value *Lock = IRB.CreateLoad(LockPtr, "lock"); + Locks.push_back(Lock); + } + } + } + + if (ClSpatialSafety) { + auto *VecBase = createMetadataVector(Bases, InsertAt); + auto *VecBound = createMetadataVector(Bounds, InsertAt); + auto *PassthruBase = getAssociatedBase(PassThru); + auto *PassthruBound = getAssociatedBound(PassThru); + + auto *SelectBase = SelectInst::Create(Mask, VecBase, PassthruBase, + "select.masked.load.base", InsertAt); + auto *SelectBound = SelectInst::Create(Mask, VecBound, PassthruBound, + "select.masked.load.base", InsertAt); + associateBaseBound(CB, SelectBase, SelectBound); + } + if (ClTemporalSafety) { + auto *VecKey = createMetadataVector(Keys, InsertAt); + auto *VecLock = createMetadataVector(Locks, InsertAt); + auto *PassthruKey = getAssociatedKey(PassThru); + auto *PassthruLock = getAssociatedLock(PassThru); + + auto *SelectKey = SelectInst::Create(Mask, VecKey, PassthruKey, + "select.masked.load.base", InsertAt); + auto *SelectLock = SelectInst::Create(Mask, VecLock, PassthruLock, + "select.masked.load.base", InsertAt); + associateKeyLock(CB, SelectKey, SelectLock); + } +} + +void SoftBoundCETSPass::handleMemcpy(CallBase *CB) { + if (DISABLE_MEMCOPY_METADATA_COPIES) + return; + + Function *F = CB->getCalledFunction(); + if (!F) + return; + + assert(F && "function is null?"); + + Value *Arg0 = CB->getArgOperand(0); + Value *Arg1 = CB->getArgOperand(1); + Value *Arg2 = CB->getArgOperand(2); + + SmallVector Args; + Args.push_back(Arg0); + Args.push_back(Arg1); + Args.push_back(Arg2); + + if (Arg2->getType() == Type::getInt64Ty(Arg2->getContext())) { + CallInst::Create(CopyMetadataFn, Args, "", CB); + } else { + // CallInst::Create(CopyMetadataFn, args, "", call_inst); + } + Args.clear(); + +#if 0 + + Value *Arg0Base = castToVoidPtr(getAssociatedBase(Arg0), CB); + Value *Arg0Bound = castToVoidPtr(getAssociatedBound(Arg0), CB); + Value *Arg1Base = castToVoidPtr(getAssociatedBase(Arg1), CB); + Value *Arg1Bound = castToVoidPtr(getAssociatedBound(Arg1), CB); + Args.push_back(Arg0); + Args.push_back(Arg0Base); + Args.push_back(Arg0Bound); + Args.push_back(Arg1); + Args.push_back(Arg1Base); + Args.push_back(Arg1Bound); + Args.push_back(Arg2); + + CallInst::Create(MemcpyDereferenceCheckFn, Args.begin(), Args.end(), "", CB); + +#endif + return; +} + +void SoftBoundCETSPass::handleExtractElement(ExtractElementInst *EEI) { + if (!isa(EEI->getType())) + return; + + auto *VecOp = EEI->getOperand(0); + + auto *Idx = EEI->getOperand(1); + + if (ClSpatialSafety) { + auto *VectorBases = getAssociatedBase(VecOp); + auto *VectorBounds = getAssociatedBound(VecOp); + + Value *PtrBase = ExtractElementInst::Create(VectorBases, Idx, "", EEI); + Value *PtrBound = ExtractElementInst::Create(VectorBounds, Idx, "", EEI); + + associateBaseBound(EEI, PtrBase, PtrBound); + } + + if (ClTemporalSafety) { + auto *VectorKeys = getAssociatedKey(VecOp); + auto *VectorLocks = getAssociatedLock(VecOp); + + Value *PtrKey = ExtractElementInst::Create(VectorKeys, Idx, "", EEI); + Value *PtrLock = ExtractElementInst::Create(VectorLocks, Idx, "", EEI); + + associateKeyLock(EEI, PtrKey, PtrLock); + } +} + +void SoftBoundCETSPass::handleInsertElement(InsertElementInst *IEI) { + Value *VecOp = IEI->getOperand(0); + Value *ValOp = IEI->getOperand(1); + Value *Idx = IEI->getOperand(2); + + // check second operand is Pointer (implies first operand is vector of + // pointers) + if (!isa(ValOp->getType())) + return; + + if (ClSpatialSafety) { + + // get metadata of pointer to be inserted into vector + Value *Base = getAssociatedBase(ValOp); + Value *Bound = getAssociatedBound(ValOp); + // get metadata vectors of vector where pointer is inserted + auto *VectorBases = getAssociatedBase(VecOp); + auto *VectorBounds = getAssociatedBound(VecOp); + // insert metadata of pointer into metadata vectors, update the metadata + // vectors in the global table + auto *NewBases = InsertElementInst::Create(VectorBases, Base, Idx, "", IEI); + auto *NewBounds = + InsertElementInst::Create(VectorBounds, Bound, Idx, "", IEI); + associateBaseBound(IEI, NewBases, NewBounds); + } + + if (ClTemporalSafety) { + Value *Key = getAssociatedKey(ValOp); + Value *Lock = getAssociatedLock(ValOp); + auto *VectorKeys = getAssociatedKey(VecOp); + auto *VectorLocks = getAssociatedLock(VecOp); + auto *NewKeys = InsertElementInst::Create(VectorKeys, Key, Idx, "", IEI); + auto *NewLocks = InsertElementInst::Create(VectorLocks, Lock, Idx, "", IEI); + + associateKeyLock(IEI, NewKeys, NewLocks); + } +} + +void SoftBoundCETSPass::handleInsertValue(InsertValueInst *IVI) { + + if (!isTypeWithPointers(IVI->getType())) + return; + + Value *AggOp = IVI->getAggregateOperand(); + Value *ValOp = IVI->getInsertedValueOperand(); + + if (!isTypeWithPointers(ValOp->getType())) { + propagateMetadata(AggOp, IVI); + return; + } + + size_t Idx = flattenAggregateIndices(AggOp->getType(), IVI->getIndices()); + + if (ClSpatialSafety) { + TinyPtrVector AggregateBases, AggregateBounds; + AggregateBases = TinyPtrVector(getAssociatedBases(AggOp)); + AggregateBounds = TinyPtrVector(getAssociatedBounds(AggOp)); + + auto Bases = getAssociatedBases(ValOp); + auto Bounds = getAssociatedBounds(ValOp); + for (unsigned J = 0; J < Bases.size(); ++J) { + + static_cast>(AggregateBases)[Idx + J] = Bases[J]; + static_cast>(AggregateBounds)[Idx + J] = + Bounds[J]; + } + + associateAggregateBaseBound(IVI, AggregateBases, AggregateBounds); + } + + if (ClTemporalSafety) { + TinyPtrVector AggregateKeys, AggregateLocks; + AggregateKeys = TinyPtrVector(getAssociatedKeys(AggOp)); + AggregateLocks = TinyPtrVector(getAssociatedLocks(AggOp)); + auto Keys = getAssociatedKeys(ValOp); + auto Locks = getAssociatedLocks(ValOp); + for (unsigned J = 0; J < Keys.size(); ++J) { + static_cast>(AggregateKeys)[Idx + J] = Keys[J]; + static_cast>(AggregateLocks)[Idx + J] = Locks[J]; + } + + associateAggregateKeyLock(IVI, AggregateKeys, AggregateLocks); + } +} + +void SoftBoundCETSPass::handleExtractValue(ExtractValueInst *EVI) { + Type *EVITy = EVI->getType(); + + if (!isTypeWithPointers(EVITy)) { + return; + } + Value *AggOp = EVI->getAggregateOperand(); + + size_t StartIdx = + flattenAggregateIndices(AggOp->getType(), EVI->getIndices()); + size_t MetadataCount = countMetadata(EVITy); + + if (ClSpatialSafety) { + + TinyPtrVector Bases, Bounds; + auto AggregateBases = getAssociatedBases(AggOp); + auto AggregateBounds = getAssociatedBounds(AggOp); + for (size_t J = StartIdx; J < StartIdx + MetadataCount; ++J) { + Value *Base = AggregateBases[J]; + Value *Bound = AggregateBounds[J]; + Bases.push_back(Base); + Bounds.push_back(Bound); + } + + associateAggregateBaseBound(EVI, Bases, Bounds); + } + + if (ClTemporalSafety) { + TinyPtrVector Keys, Locks; + auto AggregateKeys = getAssociatedKeys(AggOp); + auto AggregateLocks = getAssociatedLocks(AggOp); + for (size_t J = StartIdx; J < StartIdx + MetadataCount; ++J) { + Value *Key = AggregateKeys[J]; + Value *Lock = AggregateLocks[J]; + Keys.push_back(Key); + Locks.push_back(Lock); + } + associateAggregateKeyLock(EVI, Keys, Locks); + } +} + +void SoftBoundCETSPass::handleCall(CallBase *CallInst) { + + auto *ReturnTy = CallInst->getType(); + + if (auto *F = CallInst->getCalledFunction()) { + + auto FName = F->getName(); + + // handle calls to external (maybe uninstrumented) functions + if (F->isDeclaration() && + !isExternalDefinitelyInstrumentedFunction(FName) && + !ClExternalLibsAreInstrumented) { + if (isTypeWithPointers(ReturnTy)) { + LLVM_DEBUG( + dbgs() << "Handling call to external uninstrumented function: " + << FName << "\n"); + if (ClMaximalCompatibilityWithExternalLibs) { + associateOmnivalidMetadata(CallInst); + } else { + associateInvalidMetadata(CallInst); + } + } + return; + } + + if (((FName.find("llvm.memcpy") == 0) || + (FName.find("llvm.memmove") == 0))) { + addMemcopyMemsetCheck(CallInst, F); + handleMemcpy(CallInst); + return; + } + + if (FName.find("llvm.memset") == 0) { + addMemcopyMemsetCheck(CallInst, F); + return; + } + + if (FName.find("llvm.masked.load") == 0) { + if (isTypeWithPointers(CallInst->getType())) + handleMaskedVectorLoad(CallInst); + return; + } + + if (FName.find("llvm.stacksave") == 0) { + associateOmnivalidMetadata(CallInst); + return; + } + + if (isIgnorableLLVMIntrinsic(FName)) + return; + + if (isFunctionNotToInstrument(FName)) { + if (getNumPointerArgs(CallInst) > 0) { + LLVM_DEBUG( + dbgs() << "call to function where " + "isFunctionNotToInstrument=true with pointers in args:\n" + << FName << "\n"); + } + if (isTypeWithPointers(ReturnTy)) { + LLVM_DEBUG( + dbgs() + << "call to function where " + "isFunctionNotToInstrument=true with pointer(s) returned:\n" + << FName << "\n"); + if (ClAssociateMissingMetadata) { + if (ClAssociateOmnivalidMetadataWhenMissing) + associateOmnivalidMetadata(CallInst); + else + associateInvalidMetadata(CallInst); + } else + assert(0 && "Should not return pointer"); + } + return; + } + } + + if (isa(CallInst->getCalledOperand())) { + + if (!isTypeWithPointers(ReturnTy)) { + return; + } + LLVM_DEBUG(errs() << "Pointer from inline assembly: " << *CallInst << '\n'); + + associateOmnivalidMetadata(CallInst); + return; + } + + Instruction *InsertAt; + switch (CallInst->getOpcode()) { + case Instruction::Call: { + InsertAt = getNextInstruction(CallInst); + break; + } + case Instruction::Invoke: { + InvokeInst *Invoke = dyn_cast(CallInst); + BasicBlock *NormalBlock = Invoke->getNormalDest(); + + // normal_block might have preds besides the current one + // to guarantee that dealloc is only called after an alloc, + // create a new bb jumping to the original bb after dealloc + BasicBlock *DeallocBlock = BasicBlock::Create( + NormalBlock->getContext(), "", NormalBlock->getParent(), NormalBlock); + BranchInst::Create(NormalBlock, DeallocBlock); + Invoke->setNormalDest(DeallocBlock); + InsertAt = &*DeallocBlock->getFirstInsertionPt(); + for (PHINode &Phi : NormalBlock->phis()) { + int Idx = Phi.getBasicBlockIndex(Invoke->getParent()); + if (Idx == -1) + continue; + Phi.setIncomingBlock(Idx, DeallocBlock); + } + + break; + } + default: { + llvm_unreachable("instruction must be call or invoke!"); + break; + } + } + + // call_inst->setCallingConv(CallingConv::C); + + // Count the number of pointer arguments and whether a pointer return + size_t NumPointerArgs = getNumPointerArgs(CallInst); + size_t NumPointerRets = countPointersInType(ReturnTy); + + if (NumPointerArgs || NumPointerRets) { + introduceShadowStackAllocation(CallInst, NumPointerArgs + NumPointerRets); + } + + if (NumPointerArgs) { + size_t PointerArgNo = NumPointerRets; + + for (Use &Arg : CallInst->args()) { + if (isTypeWithPointers(Arg->getType())) { + PointerArgNo += introduceShadowStackStores(Arg, CallInst, PointerArgNo); + } + } + } + + if (NumPointerRets) { + // Shadow stack slots for return values start at index 0. + introduceShadowStackLoads(CallInst, InsertAt, 0); + } + + if (NumPointerArgs || NumPointerRets) { + introduceShadowStackDeallocation(CallInst, InsertAt); + } +} + +void SoftBoundCETSPass::handleIntToPtr(IntToPtrInst *inttoptrinst) { + Value *I = inttoptrinst; + + if (ClSpatialSafety) { + if (ClAssociateIntToPtrCastsWithOmnivalidMetadata) + associateBaseBound(I, MVoidNullPtr, MInfiniteBoundPtr); + else + associateBaseBound(I, MVoidNullPtr, MVoidNullPtr); + } + + if (ClTemporalSafety) { + if (ClAssociateIntToPtrCastsWithOmnivalidMetadata) + associateKeyLock(I, MConstantIntOne, MGlobalLockOne); + else + associateKeyLock(I, MConstantIntZero, MGlobalLockOne); + } +} + +void SoftBoundCETSPass::gatherBaseBoundPass2(Function &F) { + + std::set BBVisited; + std::queue BBWorklist; + + auto *BB = &F.getEntryBlock(); + BBWorklist.push(BB); + + while (BBWorklist.size() != 0) { + + BB = BBWorklist.front(); + + BBWorklist.pop(); + if (BBVisited.count(BB)) { + /* Block already visited */ + continue; + } + BBVisited.insert(BB); + for (auto SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI) { + BasicBlock *NextBB = *SI; + BBWorklist.push(NextBB); + } + + for (auto &I : *BB) { + + // If the instruction is not present in the original, no instrumentaion + if (!MPresentInOriginal.count(&I)) + continue; + + switch (I.getOpcode()) { + + case Instruction::Store: { + auto *Store = dyn_cast(&I); + assert(Store && "Not a Store instruction?"); + handleStore(Store); + } break; + + case Instruction::PHI: { + auto *PHI = dyn_cast(&I); + assert(PHI && "Not a PHINode?"); + handlePHIPass2(PHI); + } break; + + // TODO: is this necessary? already handled in first pass + case BitCastInst::BitCast: { + auto *BC = dyn_cast(&I); + assert(BC && "Not a bitcast instruction?"); + if (isTypeWithPointers(BC->getType())) { + handleBitCast(BC); + } + } break; + + default: + break; + } /* Switch Ends */ + } /* BasicBlock iterator Ends */ + } /* Function iterator Ends */ +} + +void SoftBoundCETSPass::introspectMetadata(Function *func, Value *ptr_value, + Instruction *insert_at, int arg_no) { + if (func->getName() != "debug_instrument_softboundcets") + return; + + Value *ptr_base = getAssociatedBase(ptr_value); + Value *ptr_bound = getAssociatedBound(ptr_value); + + Value *ptr_value_cast = castToVoidPtr(ptr_value, insert_at); + Value *ptr_base_cast = castToVoidPtr(ptr_base, insert_at); + Value *ptr_bound_cast = castToVoidPtr(ptr_bound, insert_at); + + Value *argno_value; + + argno_value = ConstantInt::get( + Type::getInt32Ty(ptr_value->getType()->getContext()), arg_no, false); + + SmallVector args; + + args.push_back(ptr_value_cast); + args.push_back(ptr_base_cast); + args.push_back(ptr_bound_cast); + args.push_back(argno_value); + + CallInst::Create(IntrospectMetadataFn, args, "", insert_at); +} + +void SoftBoundCETSPass::freeFunctionKeyLock(Function *func, Value *&func_key, + Value *&func_lock, + Value *&func_xmm_key_lock) { + if (func_key == NULL && func_lock == NULL) { + return; + } + + if ((func_key == NULL && func_lock != NULL) && + (func_key != NULL && func_lock == NULL)) { + assert(0 && "inconsistent key lock"); + } + + Instruction *next_inst = NULL; + + for (Function::iterator b = func->begin(), be = func->end(); b != be; ++b) { + + BasicBlock *bb = dyn_cast(b); + assert(bb && "basic block does not exist?"); + + for (BasicBlock::iterator i = bb->begin(), ie = bb->end(); i != ie; ++i) { + + next_inst = dyn_cast(i); + + if (!isa(next_inst)) + continue; + + ReturnInst *ret = dyn_cast(next_inst); + /* Insert a call to deallocate key and lock*/ + SmallVector args; + Instruction *first_inst_func = + dyn_cast(func->begin()->getFirstNonPHI()); + assert(first_inst_func && "function doesn't have any instruction ??"); + args.push_back(func_key); + CallInst::Create(DeallocateStackLockAndKeyFn, args, "", ret); + } + } +} + +void SoftBoundCETSPass::gatherBaseBoundPass1(Function &F) { + // Scan over the pointer arguments and introduce metadata loads from the + // shadow stack. + int ArgCount = countPointersInType(F.getReturnType()); + + for (auto &Arg : F.args()) { + + auto *ArgTy = Arg.getType(); + + if (!isTypeWithPointers(ArgTy)) { + continue; + } + + Instruction *InsertAt = F.begin()->getFirstNonPHI(); + + if (Arg.hasByValAttr()) { + LLVM_DEBUG(dbgs() << "ByVal Attribute: " << Arg << "\n"); + LLVM_DEBUG( + errs() << "Ensure FixByValAttributes Pass runs before " + "SoftBoundCETS! Please note the FixByValAttributes Pass " + "can right now only run in LTO on one single module.\n"); + assert(0 && "Pointer argument has byval attributes and the underlying" + "structure returns pointers"); + } else { + ArgCount += introduceShadowStackLoads(&Arg, InsertAt, ArgCount); + } + } + + // Get lock and key for the function. + Value *Key = NULL; + Value *Lock = NULL; + Value *func_xmm_key_lock = NULL; + getFunctionKeyLock(&F, Key, Lock, func_xmm_key_lock); + MFaultingBlock[&F] = createFaultBlock(&F); + +#if 0 + if(ClTemporalSafety){ + if(Key == NULL || Lock == NULL){ + assert(0 && "function key lock null for the function"); + } + } +#endif + + std::set BBVisited; + std::queue BBWorklist; + + auto *BB = &F.getEntryBlock(); + BBWorklist.push(BB); + + while (BBWorklist.size() != 0) { + + BB = BBWorklist.front(); + + BBWorklist.pop(); + if (BBVisited.count(BB)) { + /* Block already visited */ + continue; + } + BBVisited.insert(BB); + for (auto SI = succ_begin(BB), SE = succ_end(BB); SI != SE; ++SI) { + BasicBlock *NextBB = *SI; + BBWorklist.push(NextBB); + } + + // Scan over the instructions and gather metadata. + for (auto &I : *BB) { + + if (!MPresentInOriginal.count(&I)) + continue; + + // TODO: use llvm instruction visitor class + switch (I.getOpcode()) { + + case Instruction::Alloca: { + auto *AI = dyn_cast(&I); + assert(AI && "Not an Alloca inst?"); + handleAlloca(AI, Key, Lock, func_xmm_key_lock); + } break; + + case Instruction::Load: { + auto *LI = dyn_cast(&I); + assert(LI && "Not a Load inst?"); + handleLoad(LI); + } break; + + case Instruction::GetElementPtr: { + auto *GEP = dyn_cast(&I); + assert(GEP && "Not a GEP inst?"); + handleGEP(GEP); + } break; + + case BitCastInst::BitCast: { + auto *BC = dyn_cast(&I); + assert(BC && "Not a BitCast inst?"); + if (isTypeWithPointers(BC->getType())) { + handleBitCast(BC); + } + } break; + + case Instruction::PHI: { + auto *PHI = dyn_cast(&I); + assert(PHI && "Not a phi node?"); + // printInstructionMap(&I); + handlePHIPass1(PHI); + } break; + + case Instruction::Call: { + auto *CI = dyn_cast(&I); + assert(CI && "Not a Call inst?"); + handleCall(CI); + } break; + + case Instruction::Select: { + auto *SI = dyn_cast(&I); + assert(SI && "Not a select inst?"); + handleSelect(SI); + } break; + + case Instruction::Store: { + break; + } + + case Instruction::IntToPtr: { + auto *IPI = dyn_cast(&I); + assert(IPI && "Not a IntToPtrInst?"); + handleIntToPtr(IPI); + break; + } + + case Instruction::Ret: { + auto *RI = dyn_cast(&I); + assert(RI && "not a return inst?"); + handleReturnInst(RI); + } break; + + case Instruction::ExtractElement: { + auto *EEI = dyn_cast(&I); + assert(EEI && "ExtractElementInst inst?"); + handleExtractElement(EEI); + } break; + + case Instruction::InsertElement: { + auto *IEI = dyn_cast(&I); + assert(IEI && "ExtractElementInst inst?"); + handleInsertElement(IEI); + } break; + + case Instruction::ExtractValue: { + auto *EVI = dyn_cast(&I); + assert(EVI && "handle extract value inst?"); + handleExtractValue(EVI); + } break; + + case Instruction::InsertValue: { + auto *IVI = dyn_cast(&I); + handleInsertValue(IVI); + } break; + + case Instruction::Invoke: { + auto *II = dyn_cast(&I); + handleCall(II); + } break; + // TODO[orthen]: consider if metadata about exception object can be + // refined https://llvm.org/docs/LangRef.html#i-landingpad + // https://llvm.org/devmtg/2011-09-16/EuroLLVM2011-ExceptionHandling.pdf + case Instruction::LandingPad: { + auto *LP = dyn_cast(&I); + associateOmnivalidMetadata(LP); + } break; + + case Instruction::ShuffleVector: { + auto *SV = dyn_cast(&I); + if (isTypeWithPointers(SV->getType())) + handleShuffleVector(SV); + } break; + + default: { + if (isTypeWithPointers(I.getType())) { + LLVM_DEBUG(errs() << "Unhandled instruction: " << I << "\n"); + if (ClAssociateMissingMetadata) + if (ClAssociateOmnivalidMetadataWhenMissing) + associateOmnivalidMetadata(&I); + else + associateInvalidMetadata(&I); + else + assert(0 && "Instruction generating Pointer is not handled"); + } + } break; + } + } // End instruction iteration. + } // End basic block iteration. + + if (ClTemporalSafety) { + freeFunctionKeyLock(&F, Key, Lock, func_xmm_key_lock); + } +} + +void SoftBoundCETSPass::insertPointerMetadataLoad( + Value *LoadSrc, SoftBoundCETSMetadata &Metadata, Instruction *InsertAt) { + + SmallVector Args; + IRBuilder<> IRB(InsertAt); + + /* If the load returns a pointer, then load the base and bound + * from the shadow space + */ + Value *LoadSrcBitcast = castToVoidPtr(LoadSrc, InsertAt); + /* address of pointer being pushed */ + + Args.push_back(LoadSrcBitcast); + + if (ClSimpleMetadataMode) { + if (ClSpatialSafety) { + Metadata.Base = IRB.CreateCall(LoadMetadataBaseFn, Args, ""); + Metadata.Bound = IRB.CreateCall(LoadMetadataBoundFn, Args, ""); + } + + if (ClTemporalSafety) { + Metadata.Lock = IRB.CreateCall(LoadMetadataLockFn, Args, ""); + Metadata.Key = IRB.CreateCall(LoadMetadataKeyFn, Args, ""); + } + + } else { + auto *MetadataPtr = IRB.CreateCall(LoadMetadataPtrFn, {LoadSrcBitcast}); + + unsigned KeyIdx = 0; + unsigned LockIdx = 1; + + if (ClSpatialSafety) { + auto *BasePtr = IRB.CreateStructGEP(MetadataPtr, 0, "baseptr"); + Metadata.Base = IRB.CreateLoad(BasePtr, "base"); + auto *BoundPtr = IRB.CreateStructGEP(MetadataPtr, 1, "boundptr"); + Metadata.Bound = IRB.CreateLoad(BoundPtr, "bound"); + + KeyIdx = 2; + LockIdx = 3; + } + if (ClTemporalSafety) { + auto *KeyPtr = IRB.CreateStructGEP(MetadataPtr, KeyIdx, "keyptr"); + Metadata.Key = IRB.CreateLoad(KeyPtr, "key"); + auto *LockPtr = IRB.CreateStructGEP(MetadataPtr, LockIdx, "lockptr"); + Metadata.Lock = IRB.CreateLoad(LockPtr, "lock"); + } + } +} + +void SoftBoundCETSPass::insertVectorMetadataLoad( + Value *LoadSrc, SoftBoundCETSMetadata &Metadata, Instruction *InsertAt, + size_t NumElements) { + + IRBuilder<> IRB(InsertAt); + Value *PointerOpBitcast = castToVoidPtr(LoadSrc, InsertAt); + SmallVector Bases; + SmallVector Bounds; + SmallVector Keys; + SmallVector Locks; + + for (uint64_t Idx = 0; Idx < NumElements; Idx++) { + + SmallVector Args; + Args.push_back(PointerOpBitcast); + Constant *IdxArg = ConstantInt::get(Type::getInt32Ty(*C), Idx); + Args.push_back(IdxArg); + + if (ClSimpleMetadataMode) { + if (ClSpatialSafety) { + Value *Base = + CallInst::Create(LoadVectorMetadataBaseFn, Args, "", InsertAt); + Value *Bound = + CallInst::Create(LoadVectorMetadataBoundFn, Args, "", InsertAt); + Bases.push_back(Base); + Bounds.push_back(Bound); + } + + if (ClTemporalSafety) { + Value *Lock = + CallInst::Create(LoadVectorMetadataLockFn, Args, "", InsertAt); + Value *Key = + CallInst::Create(LoadVectorMetadataKeyFn, Args, "", InsertAt); + Keys.push_back(Key); + Locks.push_back(Lock); + } + + } else { + + auto *MetadataPtr = IRB.CreateCall(LoadVectorMetadataPtrFn, Args); + + unsigned KeyIdx = 0; + unsigned LockIdx = 1; + + if (ClSpatialSafety) { + auto *BasePtr = IRB.CreateStructGEP(MetadataPtr, 0, "baseptr"); + Value *Base = IRB.CreateLoad(BasePtr, "base"); + Bases.push_back(Base); + auto *BoundPtr = IRB.CreateStructGEP(MetadataPtr, 1, "boundptr"); + Value *Bound = IRB.CreateLoad(BoundPtr, "bound"); + Bounds.push_back(Bound); + + KeyIdx = 2; + LockIdx = 3; + } + if (ClTemporalSafety) { + auto *KeyPtr = IRB.CreateStructGEP(MetadataPtr, KeyIdx, "keyptr"); + Value *Key = IRB.CreateLoad(KeyPtr, "key"); + Keys.push_back(Key); + auto *LockPtr = IRB.CreateStructGEP(MetadataPtr, LockIdx, "lockptr"); + Value *Lock = IRB.CreateLoad(LockPtr, "lock"); + Locks.push_back(Lock); + } + } + } + if (ClSpatialSafety) { + Metadata.Base = createMetadataVector(Bases, InsertAt); + Metadata.Bound = createMetadataVector(Bounds, InsertAt); + } + if (ClTemporalSafety) { + Metadata.Key = createMetadataVector(Keys, InsertAt); + Metadata.Lock = createMetadataVector(Locks, InsertAt); + } +} + +void SoftBoundCETSPass::handlePointerLoad(LoadInst *Load) { + if (checkMetadataPresent(Load)) + return; + SoftBoundCETSMetadata Metadata; + insertPointerMetadataLoad(Load->getPointerOperand(), Metadata, Load); + associateMetadata(Load, Metadata); +} + +void SoftBoundCETSPass::handleAggregateLoad(LoadInst *Load) { + /** (ds) + * Ideally, this would work conversely to storing aggregates. + * So first create allocas for every contained pointer. + * Then generate GEPs and finally calls to the runtime. + */ + + Value *LoadSrc = Load->getPointerOperand(); + Type *LoadTy = Load->getType(); + Instruction *InsertAt = getNextInstruction(Load); + // generate GEPs for each contained pointer + SmallVector GEPs; + generateAggregateGEPs(LoadSrc, LoadTy, InsertAt, GEPs); + + TinyPtrVector Bases, Bounds, Keys, Locks; + + for (unsigned J = 0; J < GEPs.size(); ++J) { + SoftBoundCETSMetadata Metadata; + + auto *GEP = GEPs[J]; + auto *GEPElementTy = cast(GEP->getType())->getElementType(); + assert(GEPElementTy->isPtrOrPtrVectorTy() && + "Loading aggregate member metadata for value which is neither a " + "Pointer nor a Vector of Pointers"); + if (auto *VectorTy = dyn_cast(GEPElementTy)) { + insertVectorMetadataLoad(GEP, Metadata, InsertAt, + VectorTy->getElementCount().getValue()); + } else { + insertPointerMetadataLoad(GEP, Metadata, InsertAt); + } + + if (ClSpatialSafety) { + Bases.push_back(Metadata.Base); + Bounds.push_back(Metadata.Bound); + } + if (ClTemporalSafety) { + Keys.push_back(Metadata.Key); + Locks.push_back(Metadata.Lock); + } + } + + if (ClSpatialSafety) { + assert(Bases.size() == GEPs.size() && Bounds.size() == GEPs.size() && + "The number of metadata entries must match the number of generated " + "GEPs"); + } + if (ClTemporalSafety) { + assert(Keys.size() == GEPs.size() && Locks.size() == GEPs.size() && + "The number of metadata entries must match the number of generated " + "GEPs"); + } + + associateAggregateMetadata(Load, Bases, Bounds, Keys, Locks); +} + +/* handleLoad Takes a load_inst If the load is through a pointer + * which is a global then inserts base and bound for that global + * Also if the loaded value is a pointer then loads the base and + * bound for for the pointer from the shadow space + */ + +void SoftBoundCETSPass::handleLoad(LoadInst *Load) { + Type *LoadTy = Load->getType(); + if (!isTypeWithPointers(LoadTy)) { + return; + } + GetElementPtrInst *GepInst = + dyn_cast(Load->getPointerOperand()); + + if (GepInst && isVaargGep(GepInst)) { + associateOmnivalidMetadata(Load); + if (ClAssociateVaargPointerWithOmnivalidMetadata) + varargAssociatePointerLoads(Load); + } else if (isVectorWithPointers(LoadTy)) { + handleVectorLoad(Load); + } else if (isa(LoadTy)) { + handlePointerLoad(Load); + } else if (LoadTy->isAggregateType()) { + handleAggregateLoad(Load); + } else { + assert(0 && "Loading type which contains pointers but is not handled"); + } +} + +// A definition of a global with an initializer is semantically similar +// to a store of a constant value to the address of the global. +// Thus we gather metadata for all pointers in the initializer and generate +// metadata_store calls, just like we do for actual store instructions. +// The calls will be inserted into the special __softboundcets_globals_ctor +// function, which is called during program initialization. +void SoftBoundCETSPass::addMetadataToGlobals(Module &M) { + + for (auto &GV : M.globals()) { + if (GV.getSection() == "llvm.metadata" || + GV.getName() == "llvm.global_ctors" || + GV.getName() == "llvm.global_dtors" || + GV.getName().contains("softboundcets")) + continue; + + LLVM_DEBUG(dbgs() << "Associating global with metadata: " << GV << "\n"); + addMetadataToGlobal(GV); + } +} + +inline void SoftBoundCETSPass::addMetadataToGlobal(GlobalVariable &GV) { + + TinyPtrVector Bases, Bounds, Keys; + + if (ClSpatialSafety) { + Bases = createConstantBases(dyn_cast(&GV)); + Bounds = createConstantBounds(dyn_cast(&GV), true); + associateAggregateBase(&GV, Bases); + associateAggregateBound(&GV, Bounds); + } + + if (ClTemporalSafety) { + Keys = createConstantKeys(dyn_cast(&GV), true); + auto Locks = createDummyMetadata(GV.getType(), MGlobalLockOne); + associateAggregateKeyLock(&GV, Keys, Locks); + } + + auto *GVValTy = GV.getValueType(); + if (!isTypeWithPointers(GVValTy)) + return; + + Instruction *InsertAt = getGlobalsInitializerInsertPoint(*M); + SmallVector GEPs; + generateAggregateGEPs(&GV, GVValTy, InsertAt, GEPs); + + if (GV.hasInitializer()) { + Constant *GVInitializer = GV.getInitializer(); + LLVM_DEBUG(dbgs() << "Global Initializer: " << *GVInitializer << "\n"); + if (ClSpatialSafety) { + Bases = createConstantBases(dyn_cast(GVInitializer)); + Bounds = + createConstantBounds(dyn_cast(GVInitializer), true); + } + if (ClTemporalSafety) { + Keys = createConstantKeys(dyn_cast(GVInitializer), true); + } + } else { + if (ClSpatialSafety) { + Bases = createDummyMetadata(GVValTy, MVoidNullPtr); + if (ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) { + Bounds = createDummyMetadata(GVValTy, MInfiniteBoundPtr); + } else { + Bounds = createDummyMetadata(GVValTy, MVoidNullPtr); + } + } + if (ClTemporalSafety) { + if (ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) { + Keys = createDummyMetadata(GVValTy, MConstantIntOne); + } else { + Keys = createDummyMetadata(GVValTy, MConstantIntZero); + } + } + } + + for (unsigned Idx = 0; Idx < GEPs.size(); ++Idx) { + Value *Base = NULL; + Value *Bound = NULL; + Value *Key = NULL; + + if (ClSpatialSafety) { + Base = Bases[Idx]; + Bound = Bounds[Idx]; + } + if (ClTemporalSafety) { + Key = Keys[Idx]; + } + + insertPointerMetadataStore(GEPs[Idx], Base, Bound, Key, MGlobalLockOne, + InsertAt); + } +} + +// get the index into the compile-time metadata vector of an aggregate structure +size_t SoftBoundCETSPass::flattenAggregateIndices(Type *Ty, + ArrayRef Indices) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: { + assert(Indices.empty() && "Too many indices for aggregate type!"); + return 0; + } + + case Type::StructTyID: { + assert(!Indices.empty() && "Too few indices for aggregate type!"); + ArrayRef Subtypes = Ty->subtypes(); + assert(Indices[0] < Subtypes.size() && "Aggregate index out of bounds!"); + + size_t Sum = 0; + for (unsigned J = 0; J < Indices[0]; ++J) { + Sum += countMetadata(Subtypes[J]); + } + Sum += flattenAggregateIndices(Subtypes[Indices[0]], Indices.drop_front(1)); + return Sum; + } + + case Type::ArrayTyID: { + assert(!Indices.empty() && "Too few indices for aggregate type!"); + ArrayType *ArrayTy = cast(Ty); + assert(Indices[0] < ArrayTy->getNumElements() && + "Aggregate index out of bounds!"); + Type *ElTy = ArrayTy->getElementType(); + + return Indices[0] * countMetadata(ElTy) + + flattenAggregateIndices(ElTy, Indices.drop_front(1)); + } + + case Type::FixedVectorTyID: { + if (isVectorWithPointers(Ty)) { + return 0; + } + break; + } + default: { + assert(Indices.empty() && "Too many indices for aggregate type!"); + return 0; + } + } + return 0; +} + +/** (ds) + * Traverses an aggregate type (`pointee_type`) recursively to find nested + * pointer types. For each contained pointer type, a GEP value is generated + * using the base (`pointer`). Returns a list of pointer values in the range + * GEP(pointer, 0) to GEP(pointer, 1). For example the pointee type {{i64, ptr}, + * [2 x ptr]} and pointer p would result in [GEP(p, 0, 0, 1), GEP(p, 0, 1, 0), + * GEP(p, 0, 1, 1)] + */ +void SoftBoundCETSPass::generateAggregateGEPs(Value *Ptr, Type *PointeeTy, + Instruction *InsertAt, + SmallVector &GEPs) { + // Inner visitor struct containing state. + struct TypeVisitor { + Value *Ptr; + Type *PointeeTy; + Instruction *InsertAt; + // "path" to the current type, acts like a stack + TinyPtrVector &Indices; + // already generated GEPs + SmallVector &GEPs; + + void visit(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: { + Value *GEP; + // generate (constant) GEP using indices + if (Constant *ConstPtr = dyn_cast(Ptr)) { + GEP = ConstantExpr::getGetElementPtr(PointeeTy, ConstPtr, + ArrayRef(Indices)); + } else { + GEP = GetElementPtrInst::Create(PointeeTy, Ptr, + ArrayRef(Indices), + Ptr->getName() + ".gep", InsertAt); + } + GEPs.push_back(GEP); + break; + } + + case Type::StructTyID: { + ArrayRef SubTypes = Ty->subtypes(); + // visit each subtype in struct + for (unsigned J = 0; J < SubTypes.size(); ++J) { + Indices.push_back( + ConstantInt::get(Type::getInt32Ty(PointeeTy->getContext()), J)); + visit(SubTypes[J]); + Indices.pop_back(); + } + break; + } + + case Type::ArrayTyID: { + ArrayType *ArrayTy = cast(Ty); + Type *ElTy = ArrayTy->getElementType(); + // visit the array element type n times + // this is rather inefficient for larger arrays as we duplicate work + // a better solution would be to visit elt_type once and copy the + // results + for (unsigned J = 0; J < ArrayTy->getNumElements(); ++J) { + Indices.push_back( + ConstantInt::get(Type::getInt32Ty(PointeeTy->getContext()), J)); + visit(ElTy); + Indices.pop_back(); + } + break; + } + + case Type::FixedVectorTyID: { + FixedVectorType *FVTy = cast(Ty); + if (FVTy->getElementType()->isPointerTy()) { + Value *GEP = GetElementPtrInst::Create( + PointeeTy, Ptr, ArrayRef(Indices), + Ptr->getName() + ".gep", InsertAt); + GEPs.push_back(GEP); + } + break; + } + + default: { + } + } + } + }; + + TinyPtrVector Indices; + Indices.push_back( + ConstantInt::get(Type::getInt32Ty(PointeeTy->getContext()), 0)); + struct TypeVisitor Visitor = {Ptr, PointeeTy, InsertAt, Indices, GEPs}; + Visitor.visit(PointeeTy); +} + +SmallVector, 8> +SoftBoundCETSPass::getMetadataOrder(Type *Ty) { + // Inner visitor struct containing state. + struct TypeVisitor { + SmallVector, 8> &MetadataOrder; + + void visit(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: { + MetadataOrder.push_back({0, 1}); + break; + } + + case Type::StructTyID: { + ArrayRef SubTypes = Ty->subtypes(); + // visit each subtype in struct + for (unsigned J = 0; J < SubTypes.size(); ++J) { + visit(SubTypes[J]); + } + break; + } + + case Type::ArrayTyID: { + ArrayType *ArrayTy = cast(Ty); + Type *ElTy = ArrayTy->getElementType(); + // visit the array element type n times + // this is rather inefficient for larger arrays as we duplicate work + // a better solution would be to visit elt_type once and copy the + // results + for (unsigned J = 0; J < ArrayTy->getNumElements(); ++J) { + visit(ElTy); + } + break; + } + + case Type::FixedVectorTyID: { + FixedVectorType *FVTy = cast(Ty); + if (FVTy->getElementType()->isPointerTy()) { + MetadataOrder.push_back({1, FVTy->getNumElements()}); + } + break; + } + + default: { + } + } + } + }; + + SmallVector, 8> MetadataOrder; + struct TypeVisitor Visitor = {MetadataOrder}; + Visitor.visit(Ty); + return MetadataOrder; +} + +template +TinyPtrVector SoftBoundCETSPass::createDummyMetadata(Type *Ty, + Constant *Metadatum) { + TinyPtrVector DummyMetadata; + auto MetadataOrder = getMetadataOrder(Ty); + + for (auto &MetadataType : MetadataOrder) { + if (std::get<0>(MetadataType) == 0) { + DummyMetadata.push_back(Metadatum); + } else if (std::get<0>(MetadataType) == 1) { + auto VectorSize = std::get<1>(MetadataType); + SmallVector MetadataArray(VectorSize, Metadatum); + auto *MetadataVector = ConstantVector::get(MetadataArray); + DummyMetadata.push_back(MetadataVector); + } + } + return DummyMetadata; +} + +TinyPtrVector SoftBoundCETSPass::createDummyLocks(Type *Ty) { + TinyPtrVector DummyLocks; + auto MetadataOrder = getMetadataOrder(Ty); + + for (auto &MetadataType : MetadataOrder) { + if (std::get<0>(MetadataType) == 0) { + auto *Lock = new GlobalVariable( + *M, MConstantIntOne->getType(), false, GlobalValue::InternalLinkage, + MConstantIntOne, "__softboundcets_omnivalid_lock1"); + DummyLocks.push_back(Lock); + MGlobalLockOnes.insert(Lock); + } else if (std::get<0>(MetadataType) == 1) { + auto VectorSize = std::get<1>(MetadataType); + SmallVector MetadataArray; + for (size_t Idx = 0; Idx < VectorSize; Idx++) { + Constant *Lock = new GlobalVariable( + *M, MConstantIntOne->getType(), false, GlobalValue::InternalLinkage, + MConstantIntOne, "__softboundcets_omnivalid_lock1"); + MGlobalLockOnes.insert(Lock); + MetadataArray.push_back(Lock); + } + auto *MetadataVector = ConstantVector::get(MetadataArray); + DummyLocks.push_back(MetadataVector); + } + } + return DummyLocks; +} + +void SoftBoundCETSPass::associateOmnivalidMetadata(Value *Val) { + auto *ValTy = Val->getType(); + if (ClSpatialSafety) { + auto Bases = createDummyMetadata(ValTy, MVoidNullPtr); + auto Bounds = createDummyMetadata(ValTy, MInfiniteBoundPtr); + associateAggregateBaseBound(Val, Bases, Bounds); + } + if (ClTemporalSafety) { + auto Keys = createDummyMetadata(ValTy, MConstantIntOne); + auto Locks = createDummyLocks(ValTy); + associateAggregateKeyLock(Val, Keys, Locks); + } +} + +void SoftBoundCETSPass::associateInvalidMetadata(Value *Val) { + auto *ValTy = Val->getType(); + if (ClSpatialSafety) { + auto Bases = createDummyMetadata(ValTy, MVoidNullPtr); + auto Bounds = createDummyMetadata(ValTy, MVoidNullPtr); + associateAggregateBaseBound(Val, Bases, Bounds); + } + if (ClTemporalSafety) { + auto Keys = createDummyMetadata(ValTy, MConstantIntZero); + auto Locks = createDummyLocks(ValTy); + associateAggregateKeyLock(Val, Keys, Locks); + } +} + +SmallVector +SoftBoundCETSPass::unpackMetadataArray(ArrayRef MetadataVals, + Instruction *InsertAt) { + SmallVector UnpackedMetadata; + for (auto *MetadataVal : MetadataVals) { + switch (MetadataVal->getType()->getTypeID()) { + case Type::FixedVectorTyID: { + auto VectorMetadata = extractVectorValues(MetadataVal, InsertAt); + UnpackedMetadata.append(VectorMetadata); + break; + } + default: + UnpackedMetadata.push_back(MetadataVal); + } + } + return UnpackedMetadata; +} + +TinyPtrVector +SoftBoundCETSPass::packMetadataArray(ArrayRef SingleMetadataVals, + Type *Ty, Instruction *InsertAt) { + TinyPtrVector PackedMetadata; + auto MetadataOrder = getMetadataOrder(Ty); + size_t MetadataValsIdx = 0; + + for (auto &MetadataType : MetadataOrder) { + if (std::get<0>(MetadataType) == 0) { + auto *SingleMetadatum = SingleMetadataVals[MetadataValsIdx]; + PackedMetadata.push_back(SingleMetadatum); + MetadataValsIdx++; + } else if (std::get<0>(MetadataType) == 1) { + auto VectorSize = std::get<1>(MetadataType); + auto *MetadataVector = createMetadataVector( + SingleMetadataVals.slice(MetadataValsIdx, VectorSize), InsertAt); + PackedMetadata.push_back(MetadataVector); + MetadataValsIdx += VectorSize; + } + } + return PackedMetadata; +} + +// NOTE: summary for constants +// just like getConstantExprBaseBound recursively +// 1. handle ConstantData +// a) ConstantPointerNull -> invalid metadata by createDummyMetadata +// b) Undef -> invalid metadata by createDummyMetadata if it contains pointers +// c) ConstantAggregateZero -> invalid metadata by createDummyMetadata if it +// contains pointers +// 2. handle ConstantAggregate +// a) ConstantArray -> call getConstantExprBaseBound on one element, push times +// Numelement on metadata array b) ConstantStruct -> call +// getConstantExprBaseBound on each subelement c) ConstantVector -> if it +// contains pointers, create metadata vector with invalid metadata +// 3. Globals +// a) Function -> create base=pointer, bound=pointer+DL.getPointerSize +// b) Global Variable -> get underlying type -> get typesizeinbits + +template +TinyPtrVector SoftBoundCETSPass::createConstantBases(Constant *Const) { + TinyPtrVector Bases; + + if (MValueBaseMap.count(Const)) { + auto ConstBases = MValueBaseMap[Const]; + for (auto *Base : ConstBases) { + Bases.push_back(dyn_cast(Base)); + } + return Bases; + } + + if (isa(Const)) { + Bases.push_back(MVoidNullPtr); + return Bases; + } + if (isa(Const)) { + auto *BaseCast = ConstantExpr::getBitCast(Const, MVoidPtrTy); + Bases.push_back(BaseCast); + return Bases; + } + if (auto *Global = dyn_cast(Const)) { + auto *ConstCast = ConstantExpr::getBitCast(Const, MVoidPtrTy); + Bases.push_back(ConstCast); + return Bases; + } + if (auto *Undefined = dyn_cast(Const)) { + return createDummyMetadata(Undefined->getType(), MVoidNullPtr); + } + if (auto *CAggZero = dyn_cast(Const)) { + return createDummyMetadata(CAggZero->getType(), MVoidNullPtr); + } + if (ConstantExpr *Expr = dyn_cast(Const)) { + + // ignore all types that do not contain pointers + if(!isTypeWithPointers(Expr->getType())) + return Bases; + + + switch (Expr->getOpcode()) { + case Instruction::GetElementPtr: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantBases(Op); + } + + case BitCastInst::BitCast: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantBases(Op); + } + + case Instruction::IntToPtr: { + LLVM_DEBUG(errs() << "Unhandled IntToPtr operation: " << *Const << '\n'); + Bases.push_back(MVoidNullPtr); + return Bases; + } + + case Instruction::ExtractElement: { + auto *VBases = dyn_cast(getAssociatedBase(Expr->getOperand(0))); + auto *Idx = Expr->getOperand(1); + auto *EBase = ConstantExpr::getExtractElement(VBases, Idx); + Bases.push_back(EBase); + return Bases; + } + + case Instruction::InsertElement: { + auto *VBases = dyn_cast(getAssociatedBase(Expr->getOperand(0))); + auto *ElementBase = Expr->getOperand(1); + auto *Idx = Expr->getOperand(2); + auto *NewBases = ConstantExpr::getInsertElement(VBases, ElementBase, Idx); + Bases.push_back(NewBases); + return Bases; + } + + case Instruction::ExtractValue: { + Value *AggOp = Expr->getOperand(0); + auto VBases = getAssociatedBases(AggOp); + + size_t StartIdx = + flattenAggregateIndices(AggOp->getType(), Expr->getIndices()); + size_t MetadataCount = countMetadata(Expr->getType()); + + for (size_t J = StartIdx; J < StartIdx + MetadataCount; ++J) { + Value *Base = VBases[J]; + Bases.push_back(dyn_cast(Base)); + } + + return Bases; + } + + case Instruction::InsertValue: { + Constant *AggOp = Expr->getOperand(0); + Constant *ValOp = Expr->getOperand(1); + + auto AggBases = getAssociatedBases(AggOp); + for (auto *Base: AggBases) { + Bases.push_back(dyn_cast(Base)); + } + + if (!isTypeWithPointers(ValOp->getType())) + return Bases; + + auto ValBases = getAssociatedBases(ValOp); + size_t Idx = flattenAggregateIndices(AggOp->getType(), Expr->getIndices()); + + for (unsigned J = 0; J < ValBases.size(); ++J) { + static_cast>(Bases)[Idx + J] = dyn_cast(ValBases[J]); + } + + return Bases; + } + } + } + if (auto *CAgg = dyn_cast(Const)) { + if (auto *CArray = dyn_cast(CAgg)) { + for (size_t i = 0; i < CArray->getType()->getNumElements(); i++) { + auto CBases = + createConstantBases(CArray->getAggregateElement((unsigned)i)); + for (auto *Base : CBases) { + Bases.push_back(Base); + } + } + return Bases; + } + + if (auto *CVector = dyn_cast(CAgg)) { + auto *VectorTy = CVector->getType(); + if (isVectorWithPointers(VectorTy)) { + TinyPtrVector VecBases; + for (size_t i = 0; i < VectorTy->getNumElements(); i++) { + auto ElBases = + createConstantBases(CVector->getAggregateElement(i)); + VecBases.push_back(ElBases.front()); + } + auto *VecBase = ConstantVector::get(VecBases); + Bases.push_back(VecBase); + } + return Bases; + } + + if (auto *CStruct = dyn_cast(CAgg)) { + for (size_t i = 0; i < CStruct->getType()->getNumElements(); i++) { + auto ElBases = createConstantBases(CStruct->getAggregateElement(i)); + for (auto *Base : ElBases) { + Bases.push_back(Base); + } + } + return Bases; + } + } + + return Bases; +} + +template +TinyPtrVector SoftBoundCETSPass::createConstantBounds(Constant *Const, + bool isGlobal) { + TinyPtrVector Bounds; + + if (MValueBoundMap.count(Const)) { + auto ConstBounds = MValueBoundMap[Const]; + for (auto *Bound : ConstBounds) { + Bounds.push_back(dyn_cast(Bound)); + } + return Bounds; + } + + if (isa(Const)) { + if (isGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + Bounds.push_back(MInfiniteBoundPtr); + else + Bounds.push_back(MVoidNullPtr); + return Bounds; + } + if (isa(Const)) { + auto *BoundCast = ConstantExpr::getBitCast(Const, MVoidPtrTy); + Bounds.push_back(BoundCast); + return Bounds; + } + if (auto *Global = dyn_cast(Const)) { + Type *GlobalTy = Global->getValueType(); + + Constant *GEPIdx = + ConstantInt::get(Type::getInt32Ty(GlobalTy->getContext()), 1); + Constant *Bound = ConstantExpr::getGetElementPtr(GlobalTy, Const, GEPIdx); + auto *BoundCast = ConstantExpr::getBitCast(Bound, MVoidPtrTy); + Bounds.push_back(BoundCast); + return Bounds; + } + if (auto *Undefined = dyn_cast(Const)) { + if (isGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + return createDummyMetadata(Undefined->getType(), MInfiniteBoundPtr); + return createDummyMetadata(Undefined->getType(), MVoidNullPtr); + } + if (auto *CAggZero = dyn_cast(Const)) { + if (isGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + return createDummyMetadata(CAggZero->getType(), MInfiniteBoundPtr); + return createDummyMetadata(CAggZero->getType(), MVoidNullPtr); + } + if (ConstantExpr *Expr = dyn_cast(Const)) { + + // ignore all types that do not contain pointers + if(!isTypeWithPointers(Expr->getType())) + return Bounds; + + switch (Expr->getOpcode()) { + case Instruction::GetElementPtr: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantBounds(Op); + } + + case BitCastInst::BitCast: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantBounds(Op); + } + + case Instruction::IntToPtr: { + LLVM_DEBUG(errs() << "Unhandled IntToPtr operation: " << *Const << '\n'); + if (ClAssociateIntToPtrCastsWithOmnivalidMetadata) + Bounds.push_back(MInfiniteBoundPtr); + else + Bounds.push_back(MVoidNullPtr); + return Bounds; + } + + case Instruction::ExtractElement: { + auto *VBounds = + dyn_cast(getAssociatedBound(Expr->getOperand(0))); + auto *Idx = Expr->getOperand(1); + auto *EBound = ConstantExpr::getExtractElement(VBounds, Idx); + Bounds.push_back(EBound); + return Bounds; + } + + case Instruction::InsertElement: { + auto *VBounds = + dyn_cast(getAssociatedBound(Expr->getOperand(0))); + auto *ElementBound = Expr->getOperand(1); + auto *Idx = Expr->getOperand(2); + auto *NewBounds = + ConstantExpr::getInsertElement(VBounds, ElementBound, Idx); + Bounds.push_back(NewBounds); + return Bounds; + } + + case Instruction::ExtractValue: { + Value *AggOp = Expr->getOperand(0); + auto VBounds = getAssociatedBounds(AggOp); + + size_t StartIdx = + flattenAggregateIndices(AggOp->getType(), Expr->getIndices()); + size_t MetadataCount = countMetadata(Expr->getType()); + + for (size_t J = StartIdx; J < StartIdx + MetadataCount; ++J) { + Value *Bound = VBounds[J]; + Bounds.push_back(dyn_cast(Bound)); + } + + return Bounds; + } + + case Instruction::InsertValue: { + Constant *AggOp = Expr->getOperand(0); + Constant *ValOp = Expr->getOperand(1); + + auto AggBounds = getAssociatedBounds(AggOp); + for (auto *Bound : AggBounds) { + Bounds.push_back(dyn_cast(Bound)); + } + + if (!isTypeWithPointers(ValOp->getType())) + return Bounds; + + auto ValBounds = getAssociatedBounds(ValOp); + size_t Idx = + flattenAggregateIndices(AggOp->getType(), Expr->getIndices()); + + for (unsigned J = 0; J < ValBounds.size(); ++J) { + static_cast>(Bounds)[Idx + J] = + dyn_cast(ValBounds[J]); + } + + return Bounds; + } + } + } + if (auto *CAgg = dyn_cast(Const)) { + if (auto *CArray = dyn_cast(CAgg)) { + for (size_t i = 0; i < CArray->getType()->getNumElements(); i++) { + auto CBounds = + createConstantBounds(CArray->getAggregateElement((unsigned)i)); + for (auto *Bound : CBounds) { + Bounds.push_back(Bound); + } + } + return Bounds; + } + + if (auto *CVector = dyn_cast(CAgg)) { + auto *VectorTy = CVector->getType(); + if (isVectorWithPointers(VectorTy)) { + TinyPtrVector VecBounds; + for (size_t i = 0; i < VectorTy->getNumElements(); i++) { + auto ElBounds = + createConstantBounds(CVector->getAggregateElement(i)); + VecBounds.push_back(ElBounds.front()); + } + auto *VecBound = ConstantVector::get(VecBounds); + Bounds.push_back(VecBound); + } + return Bounds; + } + + if (auto *CStruct = dyn_cast(CAgg)) { + for (size_t i = 0; i < CStruct->getType()->getNumElements(); i++) { + auto ElBounds = + createConstantBounds(CStruct->getAggregateElement(i)); + for (auto *Bound : ElBounds) { + Bounds.push_back(Bound); + } + } + return Bounds; + } + } + + return Bounds; +} + +template +TinyPtrVector SoftBoundCETSPass::createConstantKeys(Constant *Const, + bool IsGlobal) { + TinyPtrVector Keys; + + if (MValueKeyMap.count(Const)) { + auto ConstKeys = MValueKeyMap[Const]; + for (auto *Key : ConstKeys) { + Keys.push_back(dyn_cast(Key)); + } + return Keys; + } + + if (isa(Const)) { + if (IsGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + Keys.push_back(MConstantIntOne); + else + Keys.push_back(MConstantIntZero); + return Keys; + } + if (isa(Const)) { + Keys.push_back(MConstantIntOne); + return Keys; + } + if (auto *Global = dyn_cast(Const)) { + Keys.push_back(MConstantIntOne); + return Keys; + } + if (auto *Undefined = dyn_cast(Const)) { + if (IsGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + return createDummyMetadata(Undefined->getType(), MConstantIntOne); + return createDummyMetadata(Undefined->getType(), MConstantIntZero); + } + if (auto *CAggZero = dyn_cast(Const)) { + if (IsGlobal && ClAssociateZeroInitializedGlobalsWithOmnivalidMetadata) + return createDummyMetadata(CAggZero->getType(), MConstantIntOne); + return createDummyMetadata(CAggZero->getType(), MConstantIntZero); + } + if (ConstantExpr *Expr = dyn_cast(Const)) { + switch (Expr->getOpcode()) { + case Instruction::GetElementPtr: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantKeys(Op); + } + + case BitCastInst::BitCast: { + Constant *Op = cast(Expr->getOperand(0)); + return createConstantKeys(Op); + } + + case Instruction::IntToPtr: { + LLVM_DEBUG(errs() << "Unhandled IntToPtr operation: " << *Const << '\n'); + Keys.push_back(MConstantIntOne); + return Keys; + } + } + } + if (auto *CAgg = dyn_cast(Const)) { + if (auto *CArray = dyn_cast(CAgg)) { + for (size_t i = 0; i < CArray->getType()->getNumElements(); i++) { + auto CKeys = + createConstantKeys(CArray->getAggregateElement((unsigned)i)); + for (auto *Key : CKeys) { + Keys.push_back(Key); + } + } + return Keys; + } + + if (auto *CVector = dyn_cast(CAgg)) { + auto *VectorTy = CVector->getType(); + if (isVectorWithPointers(VectorTy)) { + TinyPtrVector VecKeys; + for (size_t i = 0; i < VectorTy->getNumElements(); i++) { + auto ElKeys = + createConstantKeys(CVector->getAggregateElement(i)); + VecKeys.push_back(ElKeys.front()); + } + auto *VecKey = ConstantVector::get(VecKeys); + Keys.push_back(VecKey); + } + return Keys; + } + + if (auto *CStruct = dyn_cast(CAgg)) { + for (size_t i = 0; i < CStruct->getType()->getNumElements(); i++) { + auto ElKeys = createConstantKeys(CStruct->getAggregateElement(i)); + for (auto *Key : ElKeys) { + Keys.push_back(Key); + } + } + return Keys; + } + } + + return Keys; +} + +/* + * This method computes the metadata for all pointers in a constant. + * All generated metadata values will again be constants. + * For global variables, the bound is GEP(global, 1). + * No narrowing is performed for GEP constant expressions. + */ +void SoftBoundCETSPass::getConstantExprBaseBoundArray( + Constant *Const, TinyPtrVector &Bases, + TinyPtrVector &Bounds) { + size_t NumPtrs = countPointersInType(Const->getType()); + + if (NumPtrs == 0) + return; + + if (isa(Const) || isa(Const) || + isa(Const)) { + for (unsigned i = 0; i < NumPtrs; ++i) { + Bases.push_back(MVoidNullPtr); + Bounds.push_back(MVoidNullPtr); + } + return; + } + + if (GlobalVariable *Global = dyn_cast(Const)) { + Type *GlobalTy = Global->getValueType(); + Constant *GEPIdx = + ConstantInt::get(Type::getInt32Ty(GlobalTy->getContext()), 1); + Constant *Bound = ConstantExpr::getGetElementPtr(GlobalTy, Const, GEPIdx); + + Bases.push_back(Const); + Bounds.push_back(Bound); + return; + } + + if (isa(Const)) { + for (unsigned i = 0; i < Const->getNumOperands(); ++i) { + Constant *El = cast(Const->getOperand(i)); + getConstantExprBaseBoundArray(El, Bases, Bounds); + } + return; + } + + if (ConstantExpr *Expr = dyn_cast(Const)) { + switch (Expr->getOpcode()) { + case Instruction::GetElementPtr: { + Constant *Op = cast(Expr->getOperand(0)); + getConstantExprBaseBoundArray(Op, Bases, Bounds); + return; + } + + case BitCastInst::BitCast: { + Constant *Op = cast(Expr->getOperand(0)); + getConstantExprBaseBoundArray(Op, Bases, Bounds); + return; + } + + case Instruction::IntToPtr: { + LLVM_DEBUG(errs() << "Unhandled IntToPtr operation: " << *Const << '\n'); + Bases.push_back(MVoidNullPtr); + Bounds.push_back(MInfiniteBoundPtr); + return; + } + } + } + + LLVM_DEBUG(errs() << "Unhandled constant: " << *Const << '\n'); +} +void SoftBoundCETSPass::identifyOriginalInst(Function *func) { + for (Function::iterator bb_begin = func->begin(), bb_end = func->end(); + bb_begin != bb_end; ++bb_begin) { + + for (BasicBlock::iterator i_begin = bb_begin->begin(), + i_end = bb_begin->end(); + i_begin != i_end; ++i_begin) { + + Value *insn = dyn_cast(i_begin); + if (!MPresentInOriginal.count(insn)) { + MPresentInOriginal[insn] = 1; + } else { + assert(0 && "present in original map already has the insn?"); + } + + // if(isa(insn->getType())) { + // if(!m_is_pointer.count(insn)){ + // m_is_pointer[insn] = 1; + // } + // } + } /* BasicBlock ends */ + } /* Function ends */ +} + +bool SoftBoundCETSPass::runOnModule(Module &M) { + if (!ClSpatialSafety && !ClTemporalSafety) + return false; + + LLVM_DEBUG(dbgs() << "\n=====================================================" + "====================\nSoftBoundCETS Pass:\n"); + + DL = &M.getDataLayout(); + // TLI = &getAnalysis().getTLI(); + + BuilderTy TheBuilder(M.getContext(), TargetFolder(*DL)); + + Builder = &TheBuilder; + C = &M.getContext(); + this->M = &M; + + const std::vector BlacklistPaths = {BlacklistFile.str().str()}; + std::string Error; + Blacklist = + SpecialCaseList::create(BlacklistPaths, *vfs::getRealFileSystem(), Error); + if (!Blacklist) { + LLVM_DEBUG(errs() << "could not create blacklist: " << Error << "\n"); + } + + if (DL->getPointerSize() == 8) { + m_is_64_bit = true; + } else { + m_is_64_bit = false; + } + initializeSoftBoundVariables(M); + initializeInitFunctions(M); + initializeDereferenceCheckHandlers(M); + initializeMetadataHandlers(M); + insertGlobalCtor(M); + transformAndRedirectMain(M); + identifyFuncToTrans(M); + addMetadataToGlobals(M); + + for (Function &F : M.functions()) { + LLVM_DEBUG( + dbgs() << "\n=====================================================" + "====================\n"); + + if (!checkIfFunctionOfInterest(&F)) { + LLVM_DEBUG(dbgs() << "Not instrumenting function: " << F.getName() + << "\n"); + continue; + } + + LLVM_DEBUG(dbgs() << "Instrumenting function: " << F.getName() << "\n"); + InstrumentedFunctions.insert(&F); + + // + // Iterating over the instructions in the function to identify IR + // instructions in the original program In this pass, the pointers + // in the original program are also identified + // + + identifyOriginalInst(&F); + + // + // Iterate over all basic block and then each insn within a basic + // block We make two passes over the IR for base and bound + // propagation and one pass for dereference checks + // + + gatherBaseBoundPass1(F); + gatherBaseBoundPass2(F); + addDereferenceChecks(&F); + + llvm::StringRef PrintFns = ClPrintInstrumentedFunctions; + if (ClPrintAllInstrumentedFunctions || PrintFns.contains(F.getName())) + LLVM_DEBUG(dbgs() << "\nInstrumented Function: " << F << "\n"); + } + + renameFunctions(M); + LLVM_DEBUG(dbgs() << "Done with SoftBoundCETSPass\n"); + + return true; +} diff --git a/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.h b/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.h new file mode 100644 index 000000000000..97d6e5aee6da --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/SoftBoundCETS.h @@ -0,0 +1,539 @@ +//=== SoftBound/SoftBoundCETSPass.h - Definitions for the SoftBound/CETS --*- +// C++ -*===// +// Copyright (c) 2014 Santosh Nagarakatte, Milo M. K. Martin. All rights +// reserved. +// +// Developed by: Santosh Nagarakatte, +// Department of Computer Science, +// Rutgers University +// http://www.cs.rutgers.edu/~santosh.nagarakatte/softbound/ +// +// in collaboration with +// Milo Martin, Jianzhou Zhao, Steve Zdancewic +// University of Pennsylvania +// +// +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal with the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. + +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. + +// 3. Neither the names of Santosh Nagarakatte, Milo M. K. Martin, +// Jianzhou Zhao, Steve Zdancewic, University of Pennsylvania, +// Rutgers University, nor the names of its contributors may be +// used to endorse or promote products derived from this Software +// without specific prior written permission. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// WITH THE SOFTWARE. + +//===---------------------------------------------------------------------===// + +#ifndef SOFTBOUNDCETSPASS_H +#define SOFTBOUNDCETSPASS_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Operator.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Pass.h" + +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" + +#include "llvm-c/Target.h" +#include "llvm-c/TargetMachine.h" + +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/CallGraph.h" +#include "llvm/Analysis/LoopInfo.h" +#include "llvm/IR/AbstractCallSite.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Dominators.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include "llvm/Analysis/TargetFolder.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Support/SpecialCaseList.h" + +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" + +#include + +using namespace llvm; + +#define DEBUG_TYPE "softboundcets" + +typedef IRBuilder BuilderTy; + +struct SoftBoundCETSMetadata { + Value *Base; + Value *Bound; + Value *Key; + Value *Lock; +}; + +class SoftBoundCETSPass : public ModulePass { + +private: + const DataLayout *DL; + // const TargetLibraryInfo *TLI; + LLVMContext *C; + Module *M; + BuilderTy *Builder; + SmallString<64> BlacklistFile; + std::unique_ptr Blacklist; + + FunctionCallee IntrospectMetadataFn; + FunctionCallee CopyMetadataFn; + FunctionCallee AllocateShadowStackFn; + FunctionCallee DeallocateShadowStackFn; + FunctionCallee StoreMetadataShadowStackFn; + FunctionCallee LoadMetadataPtrShadowStackFn; + FunctionCallee LoadBaseShadowStackFn; + FunctionCallee LoadBoundShadowStackFn; + FunctionCallee LoadKeyShadowStackFn; + FunctionCallee LoadLockShadowStackFn; + + FunctionCallee AllocateStackLockAndKeyFn; + FunctionCallee DeallocateStackLockAndKeyFn; + + FunctionCallee SpatialLoadDereferenceCheckFn; + FunctionCallee SpatialStoreDereferenceCheckFn; + FunctionCallee TemporalLoadDereferenceCheckFn; + FunctionCallee TemporalStoreDereferenceCheckFn; + + FunctionCallee CallDereferenceCheckFn; + FunctionCallee MemcpyDereferenceCheckFn; + FunctionCallee MemsetDereferenceCheckFn; + + FunctionCallee GetGlobalLockFn; + FunctionCallee StoreMetadataFn; + FunctionCallee StoreVectorMetadataFn; + + FunctionCallee LoadMetadataPtrFn; + FunctionCallee LoadMetadataBaseFn; + FunctionCallee LoadMetadataBoundFn; + FunctionCallee LoadMetadataLockFn; + FunctionCallee LoadMetadataKeyFn; + + FunctionCallee LoadVectorMetadataBaseFn; + FunctionCallee LoadVectorMetadataBoundFn; + FunctionCallee LoadVectorMetadataLockFn; + FunctionCallee LoadVectorMetadataKeyFn; + FunctionCallee LoadVectorMetadataPtrFn; + + FunctionCallee MaskedLoadVectorMetadataBaseFn; + FunctionCallee MaskedLoadVectorMetadataBoundFn; + FunctionCallee MaskedLoadVectorMetadataLockFn; + FunctionCallee MaskedLoadVectorMetadataKeyFn; + FunctionCallee LoadMaskedVectorMetadataPtrFn; + + /* void pointer type, used many times in the Softboundcets pass */ + PointerType *MVoidPtrTy; + Type *MSizetTy; + PointerType *MLockPtrTy; + + /* constant null pointer which is the base and bound for most + * non-pointers + */ + ConstantPointerNull *MVoidNullPtr; + ConstantPointerNull *MSizetNullPtr; + Type *MKeyTy; + Type *MArgNoTy; + + Constant *MConstantIntOne; + Constant *MConstantIntZero; + + Constant *MGlobalLockOne; + std::set MGlobalLockOnes; + + /* Infinite bound where bound cannot be inferred in VarArg + * functions + */ + Constant *MInfiniteBoundPtr; + + /* Dominance Tree and Dominance Frontier for avoiding load + * dereference checks + */ + + DominatorTree *m_dominator_tree; + + /* Book-keeping structures for identifying original instructions in + * the program, pointers and their corresponding base and bound + */ + // std::map m_is_pointer; + std::map> MValueBaseMap; + std::map> MValueBoundMap; + + /* key associated with pointer */ + std::map> + MValueKeyMap; /* address of the location to load the key from */ + std::map> MValueLockMap; + + std::map MFaultingBlock; + + std::map MPresentInOriginal; + + /* Map of all functions for which Softboundcets Transformation must + * be invoked + */ + StringMap m_func_softboundcets_transform; + + /* Map of all functions that need to be transformed as they have as + * they either hava pointer arguments or pointer return type and are + * defined in the module + */ + StringMap m_func_to_transform; + + /* Map of all functions defined by Softboundcets */ + static StringMap MFunctionHasSoftboundCETSDefinition; + static StringSet<> MIgnorableLLVMIntrinsics; + + static StringMap MFunctionWrappersAvailable; + + /* Map of all functions transformed */ + StringMap m_func_transformed; + + /* Boolean indicating whether bitcode generated is for 64bit or + 32bit */ + bool m_is_64_bit; + + SmallSet InstrumentedFunctions; + + /* Main functions implementing the structure of the Softboundcets + pass + */ + bool runOnModule(Module &) override; + + void initializeInitFunctions(Module &M); + void initializeDereferenceCheckHandlers(Module &M); + void initializeMetadataHandlers(Module &M); + void initializeSoftBoundVariables(Module &M); + void insertGlobalCtor(Module &); + void identifyOriginalInst(Function *); + bool isAllocaPresent(Function *); + void gatherBaseBoundPass1(Function &F); + void gatherBaseBoundPass2(Function &F); + void addDereferenceChecks(Function *); + bool checkIfFunctionOfInterest(Function *); + bool isFunctionNotToInstrument(const StringRef &str); + bool isIgnorableLLVMIntrinsic(const StringRef &str); + bool isExternalDefinitelyInstrumentedFunction(const StringRef &str); + std::string transformFunctionName(const std::string &str); + void runForEachFunctionIndirectCallPass(Function &); + void indirectCallInstPass(Module &); + bool checkStructTypeWithGEP(BasicBlock *, std::map &, Value *, + BasicBlock::iterator); + + /* Specific LLVM instruction handlers in the bitcode */ + void handleAlloca(AllocaInst *AI, Value *Key, Value *Lock, Value *); + + void insertPointerMetadataLoad(Value *, SoftBoundCETSMetadata &Metadata, + Instruction *InsertAt); + + void insertVectorMetadataLoad(Value *LoadSrc, SoftBoundCETSMetadata &Metadata, + Instruction *InsertAt, size_t NumElements); + void handleLoad(LoadInst *); + void handleVectorStore(StoreInst *); + void handleVectorLoad(LoadInst *); + + void handlePointerLoad(LoadInst *Load); + void handleAggregateLoad(LoadInst *); + void handleAggregateStore(StoreInst *); + void handleStore(StoreInst *); + void handleGEP(GetElementPtrInst *); + void handleShuffleVector(ShuffleVectorInst *); + void handleMaskedVectorLoad(CallBase *); + + bool isVaargGep(GetElementPtrInst *); + void varargAssociatePointerLoads(Instruction *); + + void handleBitCast(BitCastInst *); + void handlePHIPass1(PHINode *); + void handlePHIPass2(PHINode *); + void handleCall(CallBase *); + void handleMemcpy(CallBase *); + void handleIndirectCall(CallInst *); + void handleExtractValue(ExtractValueInst *); + void handleExtractElement(ExtractElementInst *); + void handleInsertElement(InsertElementInst *); + void handleSelect(SelectInst *); + void handleIntToPtr(IntToPtrInst *); + void identifyFuncToTrans(Module &); + + void handleInsertValue(InsertValueInst *); + + void transformFunctions(Module &); + bool transformIndividualFunction(Module &); + bool hasPtrArgRetType(Function *); + void iterateOverSuccessors(Function &); + void transformExternalFunctions(Module &); + bool transformIndividualExternalFunctions(Module &); + void transformAndRedirectMain(Module &); + void renameFunctions(Module &); + void renameFunctionName(Function *, Module &, bool); + bool checkAndShrinkBounds(GetElementPtrInst *, Value *); + + // bool isByValDerived(Value*); + + bool checkBitcastShrinksBounds(Instruction *); + bool isStructOperand(Value *); + void addSpatialChecks(Instruction *, std::map &); + void addTemporalChecks(Instruction *, std::map &, + std::map &); + + bool optimizeTemporalChecks(Instruction *, std::map &, + std::map &); + + bool bbTemporalCheckElimination(Instruction *, std::map &); + + bool funcTemporalCheckElimination(Instruction *, std::map &); + + bool optimizeConstsGlobalAndStackVariableChecks(Instruction *); + bool checkLoadStoreSourceIsGEP(Instruction *, Value *); + void addMemcopyMemsetCheck(CallBase *, Function *); + bool isMemcopyFunction(Function *); + + void getFunctionKeyLock(Function *, Value *&, Value *&, Value *&); + void freeFunctionKeyLock(Function *, Value *&, Value *&, Value *&); + Value *getPointerLoadStore(Instruction *); + void propagateMetadata(Value *, Instruction *); + + void getFunctionKeyLock(Function &, Value *&, Value *&, Value *&); + void addMemoryAllocationCall(Function *, Value *&, Value *&, Instruction *); + + enum { SBCETS_BITCAST, SBCETS_GEP }; + /* Auxillary base and propagation functions */ + + void addMetadataToGlobals(Module &); + void addMetadataToGlobal(GlobalVariable &); + size_t flattenAggregateIndices(Type *, ArrayRef); + void generateAggregateGEPs(Value *pointer, Type *pointee_type, + Instruction *insert_at, + SmallVector &gep); + + template TinyPtrVector createConstantBases(Constant *Const); + template + TinyPtrVector createConstantBounds(Constant *Const, + bool IsGlobal = false); + template + TinyPtrVector createConstantKeys(Constant *Const, bool IsGlobal = false); + void getConstantExprBaseBoundArray(Constant *constant, + TinyPtrVector &base_array, + TinyPtrVector &bound_array); + + Instruction *getGlobalsInitializerInsertPoint(Module &); + void dissociateBaseBound(Value *); + void dissociateKeyLock(Value *); + + /* Explicit Map manipulation functions */ + + /* Single function that adds base/bound/key to the pointer map, + * first argument - pointer operand + * second argument - associated base + * third argument - associated bound + * fourth argument - associated key + * fifth argument - associated lock + */ + // void associateBaseBoundKeyLock(Value*, Value*, Value*, Value*, Value*); + /* XMM mode functions for base/bound and key/lock extraction */ + // void associateXMMBaseBoundKeyLock(Value*, Value*, Value*); + + void associateBaseBound(Value *, Value *, Value *); + void associateKeyLock(Value *, Value *, Value *); + void associateAggregateBase(Value *Val, TinyPtrVector &Bases); + void associateAggregateBound(Value *Val, TinyPtrVector &Bounds); + void associateAggregateBaseBound(Value *Val, TinyPtrVector &Bases, + TinyPtrVector &Bounds); + void associateAggregateKeyLock(Value *Val, TinyPtrVector &Keys, + TinyPtrVector &Locks); + void associateMetadata(Value *, Value *, Value *, Value *, Value *); + void associateMetadata(Value *Val, const SoftBoundCETSMetadata &Metadata); + void associateAggregateMetadata(Value *, TinyPtrVector &, + TinyPtrVector &, + TinyPtrVector &, + TinyPtrVector &); + + enum MetadataType { Base, Bound, Key, Lock }; + bool isValidMetadata(Value *, MetadataType); + + Value *createMetadataVector(ArrayRef SingleMetadataVals, + Instruction *InsertAt); + SmallVector extractVectorValues(Value *MetadataVector, + Instruction *InsertAt); + SmallVector extractVectorBases(Value *Val, Instruction *InsertAt); + SmallVector extractVectorBounds(Value *Val, + Instruction *InsertAt); + SmallVector extractVectorKeys(Value *Val, Instruction *InsertAt); + SmallVector extractVectorLocks(Value *Val, Instruction *InsertAt); + + /* Returns the base associated with the pointer value */ + Value *getAssociatedBase(Value *); + ArrayRef getAssociatedBases(Value *); + + /* Returns the bound associated with the pointer value */ + Value *getAssociatedBound(Value *); + ArrayRef getAssociatedBounds(Value *); + + Value *getAssociatedKey(Value *); + ArrayRef getAssociatedKeys(Value *); + + Value *getAssociatedLock(Value *); + ArrayRef getAssociatedLocks(Value *); + + SmallVector unpackMetadataArray(ArrayRef, Instruction *); + TinyPtrVector packMetadataArray(ArrayRef, Type *Ty, + Instruction *); + + SmallVector, 8> getMetadataOrder(Type *Ty); + + template + TinyPtrVector createDummyMetadata(Type *Ty, Constant *Metadatum); + TinyPtrVector createDummyLocks(Type *Ty); + void associateOmnivalidMetadata(Value *Val); + void associateInvalidMetadata(Value *Val); + + bool checkBaseBoundMetadataPresent(Value *); + bool checkMetadataPresent(Value *); + + bool checkKeyLockMetadataPresent(Value *); + + /* Function to add a call to m_store_base_bound_func */ + void insertPointerMetadataStore(Value *, Value *, Value *, Value *, Value *, + Instruction *); + void insertVectorMetadataStore(Value *, Value *, Value *, Value *, Value *, + Instruction *); + + void addStoreXMMBaseBoundFunc(Value *, Value *, Value *, Instruction *); + + void setFunctionPtrBaseBound(Value *, Instruction *); + + void replaceAllInMap(std::map &, Value *, Value *); + + void castAddToPhiNode(PHINode *, Value *, BasicBlock *, + std::map &, Value *); + + void getConstantExprBaseBound(Constant *, Value *&, Value *&); + + Value *castAndReplaceAllUses(Value *, Value *, Instruction *); + + bool checkIfNonCallUseOfFunction(Function *); + + /* Other helper functions */ + + Value *introduceGEPWithLoad(Value *, int, Instruction *); + Value *storeShadowStackBaseForFunctionArgs(Instruction *, int); + Value *storeShadowStackBoundForFunctionArgs(Instruction *, int); + Value *storeShadowStackKeyForFunctionArgs(Instruction *, int); + Value *storeShadowStackLockForFunctionArgs(Instruction *, int); + + Value *retrieveShadowStackBaseForFunctionArgs(Instruction *, int); + Value *retrieveShadowStackBoundForFunctionArgs(Instruction *, int); + Value *retrieveShadowStackKeyForFunctionArgs(Instruction *, int); + Value *retrieveShadowStackLockForFunctionArgs(Instruction *, int); + + Value *introduceGlobalLockFunction(Instruction *); + void introspectMetadata(Function *, Value *, Instruction *, int); + size_t introduceShadowStackLoads(Value *, Instruction *, int); + void introduceShadowStackAllocation(CallBase *, int); + size_t introduceShadowStackStores(Value *, Instruction *, int); + void introduceShadowStackDeallocation(CallBase *, Instruction *); + size_t getNumPointerArgs(const CallBase *CB); + + void checkIfRetTypePtr(Function *, bool &); + Instruction *getReturnInst(Function *, int); + + // + // Method: getNextInstruction + // + // Description: + // This method returns the next instruction after the input instruction. + // + + Instruction *getNextInstruction(Instruction *I) { + if (I->isTerminator()) + return I; + return I->getNextNode(); + } + + const Type *getStructType(const Type *); + ConstantInt *getSizeOfType(Type *); + + Value *castToVoidPtr(Value *Ptr, IRBuilder<> &IRB); + Value *castToVoidPtr(Value *, Instruction *); + bool checkGEPOfInterestSB(GetElementPtrInst *); + void handleReturnInst(ReturnInst *); + +public: + static char ID; + + /* INITIALIZE_PASS(SoftBoundCETSPass, "softboundcetspass", */ + /* "SoftBound CETS for memory safety", false, false) */ + + SoftBoundCETSPass(StringRef BlacklistFile = "") + : ModulePass(ID), BlacklistFile(BlacklistFile) {} + + llvm::StringRef getPassName() const override { return "SoftBoundCETSPass"; } + + void getAnalysisUsage(AnalysisUsage &au) const override { + au.addRequired(); + au.addRequired(); + au.addRequired(); + } +}; + +#endif diff --git a/llvm/lib/Transforms/SoftBoundCETS/Utils.cpp b/llvm/lib/Transforms/SoftBoundCETS/Utils.cpp new file mode 100644 index 000000000000..ded265ba98c4 --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/Utils.cpp @@ -0,0 +1,128 @@ +#include "Utils.h" +#include "llvm/Support/ConvertUTF.h" + +size_t countPointersInType(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: + return 1; + case Type::StructTyID: { + size_t Sum = 0; + for (Type::subtype_iterator I = Ty->subtype_begin(), E = Ty->subtype_end(); + I != E; ++I) { + Sum += countPointersInType(*I); + } + return Sum; + } + case Type::ArrayTyID: { + ArrayType *ArrayTy = cast(Ty); + return countPointersInType(ArrayTy->getElementType()) * + ArrayTy->getNumElements(); + } + case Type::FixedVectorTyID: { + FixedVectorType *FVTy = cast(Ty); + return countPointersInType(FVTy->getElementType()) * FVTy->getNumElements(); + } + // TODO[orthen]: enable this too with ElementCount, isScalar, getValue + case Type::ScalableVectorTyID: { + assert(0 && "Counting pointers for scalable vectors not yet handled."); + } + default: + return 0; + } +} + +bool isTypeWithPointers(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: + return true; + case Type::StructTyID: { + for (Type::subtype_iterator I = Ty->subtype_begin(), E = Ty->subtype_end(); + I != E; ++I) { + if (isTypeWithPointers(*I)) + return true; + } + return false; + } + case Type::ArrayTyID: { + ArrayType *ArrayTy = cast(Ty); + return isTypeWithPointers(ArrayTy->getElementType()); + } + case Type::FixedVectorTyID: { + FixedVectorType *FVTy = cast(Ty); + return isTypeWithPointers(FVTy->getElementType()); + } + case Type::ScalableVectorTyID: { + assert(0 && "Counting pointers for scalable vectors not yet handled."); + } + default: + return false; + } +} + +Type *getContainedByValType(Argument *FnArg) { + PointerType *PointerTy = dyn_cast(FnArg->getType()); + assert(PointerTy && "byval attribute for argument that is not a pointer"); + return PointerTy->getContainedType(0); +} + +bool isVectorWithPointers(Type *Ty) { + auto *VectorTy = dyn_cast(Ty); + if (VectorTy && VectorTy->getElementType()->isPointerTy()) { + return true; + } + return false; +} + +// Count how much metadata (consisting of Base, Bound, Key, Lock value pointers) +// we need during compile time. +// Pointers need 1 metadata, vectors also (metadata of vectors is stored in +// other vectors). Aggregate types need the sum of metadata of their subtypes. +size_t countMetadata(Type *Ty) { + switch (Ty->getTypeID()) { + case Type::PointerTyID: + return 1; + case Type::StructTyID: { + size_t Sum = 0; + for (Type::subtype_iterator I = Ty->subtype_begin(), E = Ty->subtype_end(); + I != E; ++I) { + Sum += countMetadata(*I); + } + return Sum; + } + case Type::ArrayTyID: { + ArrayType *ArrayTy = cast(Ty); + return countMetadata(ArrayTy->getElementType()) * ArrayTy->getNumElements(); + } + case Type::FixedVectorTyID: { + return 1; + } + case Type::ScalableVectorTyID: { + assert(0 && "Counting metadata for scalable vectors not yet handled."); + } + default: + return 0; + } +} + +void collectVarargPointerLoads(Instruction *Root, SmallVector &LoadInsts, + std::set &Visited) { + if (Visited.count(Root)) + return; // Avoid cycles + Visited.insert(Root); + + for (auto *Usr : Root->users()) { + Instruction* I = dyn_cast(Usr); + + if (!I || isa(I)) + continue; + // Check if the user is a LoadInst and loads a pointer + if (auto *LI = dyn_cast(Usr)) { + if (LI->getType()->isPointerTy()) { + LoadInsts.push_back(LI); // Collect LoadInst + } + } + + // Continue the traversal for other users + collectVarargPointerLoads(I, LoadInsts, Visited); + } +} diff --git a/llvm/lib/Transforms/SoftBoundCETS/Utils.h b/llvm/lib/Transforms/SoftBoundCETS/Utils.h new file mode 100644 index 000000000000..bda884a65ca1 --- /dev/null +++ b/llvm/lib/Transforms/SoftBoundCETS/Utils.h @@ -0,0 +1,22 @@ +#include "llvm/IR/Argument.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Type.h" +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "softboundcets" + +bool isTypeWithPointers(Type *Ty); +size_t countPointersInType(Type *Ty); +bool isVectorWithPointers(Type *Ty); +size_t countMetadata(Type *Ty); + +// get Type byval pointer points to (as byval arg usually(always?) is a pointer +// to another type) +Type *getContainedByValType(Argument *FnArg); +void collectVarargPointerLoads(Instruction *Root, SmallVector &LoadInsts, + std::set &Visited);