Add CFGuard test
This commit is contained in:
1 parent
e9401c8bfe
commit
039ee72a68
2 files changed
+163
-1
No files matched your search
+45
-1
@@ -42,6 +42,20 @@ if ! $ANY_ARCH-w64-mingw32-gcc -E is-ucrt.c > /dev/null 2>&1; then
|
|||||||
fi
|
fi
|
||||||
rm -f is-ucrt.c
|
rm -f is-ucrt.c
|
||||||
|
|
||||||
|
if (echo "int main(){}" | $ANY_ARCH-w64-mingw32-clang -x c++ - -o has-cfguard-test.exe -mguard=cf); then
|
||||||
|
if llvm-readobj --coff-load-config has-cfguard-test.exe | grep -q 'CF_INSTRUMENTED (0x100)'; then
|
||||||
|
HAS_CFGUARD=1
|
||||||
|
elif [ -n "$HAS_CFGUARD" ]; then
|
||||||
|
echo "error: Toolchain doesn't seem to include Control Flow Guard support." 1>&2
|
||||||
|
rm -f has-cfguard-test.exe
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f has-cfguard-test.exe
|
||||||
|
elif [ -n "$HAS_CFGUARD" ]; then
|
||||||
|
echo "error: Toolchain doesn't seem to include Control Flow Guard support." 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
: ${TARGET_OSES:=${TOOLCHAIN_TARGET_OSES-$DEFAULT_OSES}}
|
: ${TARGET_OSES:=${TOOLCHAIN_TARGET_OSES-$DEFAULT_OSES}}
|
||||||
|
|
||||||
if [ -z "$RUN_X86" ]; then
|
if [ -z "$RUN_X86" ]; then
|
||||||
@@ -82,6 +96,9 @@ TESTS_OMP="hello-omp"
|
|||||||
TESTS_UWP="uwp-error"
|
TESTS_UWP="uwp-error"
|
||||||
TESTS_IDL="idltest"
|
TESTS_IDL="idltest"
|
||||||
TESTS_OTHER_TARGETS="hello"
|
TESTS_OTHER_TARGETS="hello"
|
||||||
|
if [ -n "$HAS_CFGUARD" ]; then
|
||||||
|
TESTS_CFGUARD="cfguard-test"
|
||||||
|
fi
|
||||||
for arch in $ARCHS; do
|
for arch in $ARCHS; do
|
||||||
case $arch in
|
case $arch in
|
||||||
i686|x86_64)
|
i686|x86_64)
|
||||||
@@ -165,6 +182,9 @@ for arch in $ARCHS; do
|
|||||||
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test.exe -fstack-protector-strong
|
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test.exe -fstack-protector-strong
|
||||||
FAILURE_TESTS="$FAILURE_TESTS $test"
|
FAILURE_TESTS="$FAILURE_TESTS $test"
|
||||||
done
|
done
|
||||||
|
for test in $TESTS_CFGUARD; do
|
||||||
|
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test.exe -mguard=cf
|
||||||
|
done
|
||||||
for test in $TESTS_FORTIFY; do
|
for test in $TESTS_FORTIFY; do
|
||||||
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test-fortify.exe -O2 -D_FORTIFY_SOURCE=2 -lssp
|
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test-fortify.exe -O2 -D_FORTIFY_SOURCE=2 -lssp
|
||||||
TESTS_EXTRA="$TESTS_EXTRA $test-fortify"
|
TESTS_EXTRA="$TESTS_EXTRA $test-fortify"
|
||||||
@@ -217,6 +237,14 @@ for arch in $ARCHS; do
|
|||||||
TESTS_EXTRA="$TESTS_EXTRA $test-asan"
|
TESTS_EXTRA="$TESTS_EXTRA $test-asan"
|
||||||
FAILURE_TESTS="$FAILURE_TESTS $test-asan"
|
FAILURE_TESTS="$FAILURE_TESTS $test-asan"
|
||||||
fi
|
fi
|
||||||
|
if [ -n "$HAS_CFGUARD" ]; then
|
||||||
|
# Smoke test ASAN with CFGuard to make sure it doesn't trip.
|
||||||
|
$arch-w64-mingw32-clang $test.c -o $TEST_DIR/$test-asan-cfguard.exe -fsanitize=address -g -gcodeview -Wl,-pdb,$TEST_DIR/$test-asan.pdb -mguard=cf
|
||||||
|
if [ -n "$NATIVE" ]; then
|
||||||
|
TESTS_EXTRA="$TESTS_EXTRA $test-asan-cfguard"
|
||||||
|
FAILURE_TESTS="$FAILURE_TESTS $test-asan-cfguard"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
for test in $TESTS_UBSAN; do
|
for test in $TESTS_UBSAN; do
|
||||||
case $arch in
|
case $arch in
|
||||||
@@ -287,7 +315,7 @@ for arch in $ARCHS; do
|
|||||||
echo $file trigger failed expectedly, returned $ret
|
echo $file trigger failed expectedly, returned $ret
|
||||||
|
|
||||||
case $test in
|
case $test in
|
||||||
stacksmash-asan)
|
stacksmash-asan|stacksmash-asan-cfguard)
|
||||||
grep -q stack-buffer-overflow $OUT
|
grep -q stack-buffer-overflow $OUT
|
||||||
grep -q "func.*stacksmash.c" $OUT
|
grep -q "func.*stacksmash.c" $OUT
|
||||||
;;
|
;;
|
||||||
@@ -331,6 +359,22 @@ for arch in $ARCHS; do
|
|||||||
fi
|
fi
|
||||||
i=$(($i+1))
|
i=$(($i+1))
|
||||||
done
|
done
|
||||||
|
for test in $TESTS_CFGUARD; do
|
||||||
|
file=$test.exe
|
||||||
|
OUT=cmdoutput
|
||||||
|
rm -f $OUT
|
||||||
|
if $RUN $test.exe check_enabled; then
|
||||||
|
$RUN $test.exe normal_icall
|
||||||
|
$RUN $test.exe invalid_icall_nocf || [ $? = 2 ]
|
||||||
|
# We want to check the exit code to be 0xc0000409
|
||||||
|
# (STATUS_STACK_BUFFER_OVERRUN aka fail fast exception).
|
||||||
|
# MSYS2 bash does not give us the full 32-bit exit code, so
|
||||||
|
# we have to rely on cmd.exe to perform the check.
|
||||||
|
# (This probably doesn't work on Wine, but Wine doesn't
|
||||||
|
# support CFG anyway, at least not for now...)
|
||||||
|
$RUN cmd //v:on //c "$test.exe invalid_icall & if !errorlevel! equ -1073740791 (exit 0) else (exit 1)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
cd ..
|
cd ..
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Alvin Wong
|
||||||
|
*
|
||||||
|
* This file is part of llvm-mingw.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define WINVER 0x0603
|
||||||
|
#define _WIN32_WINNT 0x0603
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
__attribute__ (( noinline ))
|
||||||
|
void nop_sled_target(void) {
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
__asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
|
||||||
|
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
// On x86_64 the stack frame has to be aligned to 16 bytes. Since we
|
||||||
|
// skipped the prologue we need to manually realign it to prevent
|
||||||
|
// alignment-related crashes when calling puts() or exit().
|
||||||
|
__asm__("and $~0xF, %rsp");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
puts("Pwned!!!");
|
||||||
|
|
||||||
|
// We skipped the function prologue with the indirect call. If we let
|
||||||
|
// the function return normally it will just crash with a segfault, so
|
||||||
|
// do an exit instead.
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( noinline ))
|
||||||
|
void normal_function(void) {
|
||||||
|
puts("Normal function called.");
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( noinline ))
|
||||||
|
void make_indirect_call(void (*fn_ptr)(void)) {
|
||||||
|
fn_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( noinline, guard(nocf) ))
|
||||||
|
void make_indirect_call_nocf(void (*fn_ptr)(void)) {
|
||||||
|
fn_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_cfguard_status(void) {
|
||||||
|
PROCESS_MITIGATION_CONTROL_FLOW_GUARD_POLICY policy;
|
||||||
|
BOOL result = GetProcessMitigationPolicy(GetCurrentProcess(),
|
||||||
|
ProcessControlFlowGuardPolicy,
|
||||||
|
&policy,
|
||||||
|
sizeof(policy));
|
||||||
|
if (!result)
|
||||||
|
return 0;
|
||||||
|
return policy.EnableControlFlowGuard;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
if (argc == 2) {
|
||||||
|
if (strcmp(argv[1], "check_enabled") == 0) {
|
||||||
|
if (check_cfguard_status()) {
|
||||||
|
puts("Control Flow Guard is enabled!");
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
puts("Control Flow Guard is _not_ enabled!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], "normal_icall") == 0) {
|
||||||
|
puts("Performing normal indirect call.");
|
||||||
|
make_indirect_call(normal_function);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], "invalid_icall") == 0) {
|
||||||
|
void *target = nop_sled_target;
|
||||||
|
target += 16;
|
||||||
|
puts("Performing invalid indirect call. If CFG is enabled this "
|
||||||
|
"should crash with exit code 0xc0000409 (-1073740791)...");
|
||||||
|
fflush(stdout);
|
||||||
|
make_indirect_call(target);
|
||||||
|
puts("Unexpectedly returned from indirect call!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (strcmp(argv[1], "invalid_icall_nocf") == 0) {
|
||||||
|
void *target = nop_sled_target;
|
||||||
|
target += 16;
|
||||||
|
puts("Performing invalid indirect call without CFG. You should "
|
||||||
|
"get an exit code 2...");
|
||||||
|
fflush(stdout);
|
||||||
|
make_indirect_call_nocf(target);
|
||||||
|
puts("Unexpectedly returned from indirect call!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%s ( check_enabled | normal_icall | invalid_icall | invalid_icall_nocf )\n", argv[0]);
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user