Skip to content

Implement a simple e2e test for PFP. #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: pfp
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,7 @@ endif()
if (WIN32)
set(ALL_ORC_SUPPORTED_ARCH ${X86_64})
endif()

if (OS_NAME MATCHES "Linux")
set(ALL_PFP_SUPPORTED_ARCH ${X86_64} ${ARM64})
endif()
4 changes: 4 additions & 0 deletions compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,9 @@ if(APPLE)
list_intersect(ORC_SUPPORTED_ARCH
ALL_ORC_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)
list_intersect(PFP_SUPPORTED_ARCH
ALL_PFP_SUPPORTED_ARCH
SANITIZER_COMMON_SUPPORTED_ARCH)

else()
# Architectures supported by compiler-rt libraries.
Expand Down Expand Up @@ -721,6 +724,7 @@ else()
filter_available_targets(GWP_ASAN_SUPPORTED_ARCH ${ALL_GWP_ASAN_SUPPORTED_ARCH})
filter_available_targets(NSAN_SUPPORTED_ARCH ${ALL_NSAN_SUPPORTED_ARCH})
filter_available_targets(ORC_SUPPORTED_ARCH ${ALL_ORC_SUPPORTED_ARCH})
filter_available_targets(PFP_SUPPORTED_ARCH ${ALL_PFP_SUPPORTED_ARCH})
endif()

if (MSVC)
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS)
# ShadowCallStack does not yet provide a runtime with compiler-rt, the tests
# include their own minimal runtime
add_subdirectory(shadowcallstack)
add_subdirectory(pfp)
endif()

# Now that we've traversed all the directories and know all the lit testsuites,
Expand Down
36 changes: 36 additions & 0 deletions compiler-rt/test/pfp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
set(PFP_TESTSUITES)
set(PFP_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS} unwind compiler-rt)
if(COMPILER_RT_HAS_LLD AND TARGET lld)
list(APPEND PFP_TEST_DEPS lld)
endif()

macro(add_pfp_testsuite arch thinlto)
set(PFP_TEST_TARGET_ARCH ${arch})
get_test_cc_for_arch(${arch} PFP_TEST_TARGET_CC PFP_TEST_TARGET_CFLAGS)

string(TOUPPER ${arch} CONFIG_NAME)

if (${thinlto})
set(PFP_TEST_USE_THINLTO ${thinlto})
set(CONFIG_NAME "thinlto-${CONFIG_NAME}")
list(APPEND PFP_TEST_DEPS LTO)
endif()
set(PFP_TEST_USE_THINLTO ${thinlto})

configure_lit_site_cfg(
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in
${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg.py)
list(APPEND PFP_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME})
endmacro()

set(PFP_TEST_ARCH ${PFP_SUPPORTED_ARCH})

foreach(arch ${PFP_TEST_ARCH})
add_pfp_testsuite(${arch} False)
add_pfp_testsuite(${arch} True)
endforeach()

add_lit_testsuite(check-pfp "Running the PointerFieldProtection tests"
${PFP_TESTSUITES}
DEPENDS ${PFP_TEST_DEPS})

37 changes: 37 additions & 0 deletions compiler-rt/test/pfp/lit.cfg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- Python -*-

import os

from lit.llvm import llvm_config
from lit.llvm.subst import ToolSubst, FindTool

# Setup config name.
config.name = "pfp" + config.name_suffix

# Default test suffixes.
config.suffixes = [".c", ".cpp"]

# Setup source root.
config.test_source_root = os.path.dirname(__file__)
# Setup default compiler flags used with -fsanitize=memory option.
clang_cflags = [config.target_cflags] + config.debug_info_flags
clang_cxxflags = config.cxx_mode_flags + clang_cflags
clang_pfp_tagged_common_cflags = clang_cflags + [
"-fexperimental-pointer-field-protection=tagged"
]


clang_pfp_cxxflags = config.cxx_mode_flags + clang_pfp_tagged_common_cflags
clang_pfp_cxxflags = clang_pfp_cxxflags + ["-fuse-ld=lld --rtlib=compiler-rt --unwindlib=libunwind -static-libgcc"]


def build_invocation(compile_flags, with_lto=False):
lto_flags = []
if with_lto and config.lto_supported:
lto_flags += config.lto_flags

return " " + " ".join([config.clang] + lto_flags + compile_flags) + " "


config.substitutions.append(("%clangxx ", build_invocation(clang_cxxflags)))
config.substitutions.append(("%clangxx_pfp ", build_invocation(clang_pfp_cxxflags, config.use_thinlto)))
16 changes: 16 additions & 0 deletions compiler-rt/test/pfp/lit.site.cfg.py.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@LIT_SITE_CFG_IN_HEADER@

# Tool-specific config options.
config.name_suffix = "@PFP_TEST_CONFIG_SUFFIX@"
config.target_cflags = "@PFP_TEST_TARGET_CFLAGS@"
config.target_arch = "@PFP_TEST_TARGET_ARCH@"
config.use_lld = True
config.use_thinlto = @PFP_TEST_USE_THINLTO@
config.libunwind_shared = "@LIBUNWIND_ENABLE_SHARED@"
config.libunwind_install_dir = "@LLVM_BINARY_DIR@/@LIBUNWIND_INSTALL_LIBRARY_DIR@"

# Load common config for all compiler-rt lit tests.
lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured")

# Load tool-specific config that would do the real work.
lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg.py")
43 changes: 43 additions & 0 deletions compiler-rt/test/pfp/use-after-free-fixed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %clangxx_pfp %s -o %t1
// RUN: %run %t1 2>&1
// RUN: %clangxx %s -o %t2
// RUN: %run %t2 2>&1

#include <iostream>

// Struct1.ptr and Struct2.ptr have different locks.
struct Struct1 {
int *ptr;
Struct1() : num(1), ptr(&num) {}

private:
int num;
};

struct Struct2 {
int *ptr;
Struct2() : num(2), ptr(&num) {}

private:
int num;
};

Struct1 *new_object1() {
Struct1 *ptr = new Struct1;
return ptr;
}

Struct2 *new_object2() {
Struct2 *ptr = new Struct2;
return ptr;
}

int main() {
Struct1 *obj1 = new_object1();
Struct2 *obj2 = new_object2();
std::cout << "Struct2: " << *(obj2->ptr) << "\n";
std::cout << "Struct1: " << *(obj1->ptr) << "\n";
delete obj1;
delete obj2;
return 0;
}
45 changes: 45 additions & 0 deletions compiler-rt/test/pfp/use-after-free.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// RUN: %clangxx_pfp %s -o %t1
// RUN: %expect_crash %run %t1 2>&1
// RUN: %clangxx %s -o %t2
// RUN: %run %t2 2>&1

#include <iostream>

// Struct1.ptr and Struct2.ptr have different locks.
struct Struct1 {
int *ptr;
Struct1() : num(1), ptr(&num) {}

private:
int num;
};

struct Struct2 {
int *ptr;
Struct2() : num(2), ptr(&num) {}

private:
int num;
};

Struct1 *new_object1() {
Struct1 *ptr = new Struct1;
return ptr;
}

Struct2 *new_object2() {
Struct2 *ptr = new Struct2;
return ptr;
}

int main() {
Struct1 *obj1 = new_object1();
delete obj1;
// obj1's memory will be reused.
Struct2 *obj2 = new_object2();
std::cout << "Struct2: " << *(obj2->ptr) << "\n";
// Uses a wrong lock. The Program should crash when "-fexperimental-pointer-field-protection=tagged".
std::cout << "Struct1: " << *(obj1->ptr) << "\n";
delete obj2;
return 0;
}