From: Daniele Buono <dbuono@linux.vnet.ibm.com>
To: Paolo Bonzini <pbonzini@redhat.com>, qemu-devel@nongnu.org
Subject: Re: [PATCH 2/2] configure: add support for Control-Flow Integrity
Date: Thu, 2 Jul 2020 08:19:11 -0400 [thread overview]
Message-ID: <764bc77f-693d-66fb-20a0-4c77ba935eb5@linux.vnet.ibm.com> (raw)
In-Reply-To: <1074ec24-513c-f754-841c-447dfa5cb5aa@redhat.com>
On 7/2/2020 5:45 AM, Paolo Bonzini wrote:
> On 02/07/20 07:49, Daniele Buono wrote:
>> This patch adds a flag to enable/disable control flow integrity checks
>> on indirect function calls. This feature is only provided by LLVM/Clang
>> v3.9 or higher, and only allows indirect function calls to functions
>> with compatible signatures.
>>
>> We also add an option to enable a debugging version of cfi, with verbose
>> output in case of a CFI violation.
>>
>> CFI on indirect function calls does not support calls to functions in
>> shared libraries (since they were not known at compile time), and such
>> calls are forbidden. QEMU relies on dlopen/dlsym when using modules,
>> so we make modules incompatible with CFI.
>>
>> We introduce a blacklist file, to disable CFI checks in a limited number
>> of TCG functions.
>>
>> The feature relies on link-time optimization (lto), which requires the
>> use of the gold linker, and the LLVM versions of ar, ranlib and nm.
>> This patch take care of checking that all the compiler toolchain
>> dependencies are met.
>>
>> Signed-off-by: Daniele Buono <dbuono@linux.vnet.ibm.com>
>
> Can you split this option in two parts, --enable-lto to enable link-time
> optimization (perhaps also for GCC) and --enable-cfi which implies
> --enable-lto?
>
> This is because in the future we are considering switching to Meson,
> where LTO support is built-in; having a separate switch would make it
> easier to find the relevant code.
>
> Paolo
Sure, that shouldn't be a big deal.
Thanks,
Daniele
>
>> ---
>> cfi-blacklist.txt | 27 +++++++
>> configure | 177 ++++++++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 204 insertions(+)
>> create mode 100644 cfi-blacklist.txt
>>
>> diff --git a/cfi-blacklist.txt b/cfi-blacklist.txt
>> new file mode 100644
>> index 0000000000..bf804431a5
>> --- /dev/null
>> +++ b/cfi-blacklist.txt
>> @@ -0,0 +1,27 @@
>> +# List of functions that should not be compiled with Control-Flow Integrity
>> +
>> +[cfi-icall]
>> +# TCG creates binary blobs at runtime, with the transformed code.
>> +# When it's time to execute it, the code is called with an indirect function
>> +# call. Since such function did not exist at compile time, the runtime has no
>> +# way to verify its signature. Disable CFI checks in the function that calls
>> +# the binary blob
>> +fun:cpu_tb_exec
>> +
>> +# TCI (Tiny Compiler Interpreter) is an interpreter for TCG pseudo code.
>> +# One possible operation in the pseudo code is a call to binary code.
>> +# Therefore, disable CFI checks in the interpreter function
>> +fun:tcg_qemu_tb_exec
>> +
>> +# TCG Plugins Callback Functions. The mechanism rely on opening external
>> +# shared libraries at runtime and get pointers to functions in such libraries
>> +# Since these pointers are external to the QEMU binary, the runtime cannot
>> +# verify their signature. Disable CFI Checks in all the functions that use
>> +# such pointers.
>> +fun:plugin_vcpu_cb__simple
>> +fun:plugin_cb__simple
>> +fun:plugin_cb__udata
>> +fun:qemu_plugin_tb_trans_cb
>> +fun:qemu_plugin_vcpu_syscall
>> +fun:qemu_plugin_vcpu_syscall_ret
>> +fun:plugin_load
>> diff --git a/configure b/configure
>> index 4a22dcd563..86fb0f390c 100755
>> --- a/configure
>> +++ b/configure
>> @@ -27,6 +27,7 @@ fi
>> TMPB="qemu-conf"
>> TMPC="${TMPDIR1}/${TMPB}.c"
>> TMPO="${TMPDIR1}/${TMPB}.o"
>> +TMPA="${TMPDIR1}/lib${TMPB}.a"
>> TMPCXX="${TMPDIR1}/${TMPB}.cxx"
>> TMPE="${TMPDIR1}/${TMPB}.exe"
>> TMPMO="${TMPDIR1}/${TMPB}.mo"
>> @@ -134,6 +135,43 @@ compile_prog() {
>> do_cc $QEMU_CFLAGS $local_cflags -o $TMPE $TMPC $QEMU_LDFLAGS $local_ldflags
>> }
>>
>> +do_run() {
>> + # Run a generic program, capturing its output to the log.
>> + # First argument is binary to execute.
>> + local program="$1"
>> + shift
>> + echo $program $@ >> config.log
>> + $program $@ >> config.log 2>&1 || return $?
>> +}
>> +
>> +do_run_filter() {
>> + # Run a generic program, capturing its output to the log,
>> + # but also filtering the output with grep.
>> + # Returns the return value of grep.
>> + # First argument is the filter string.
>> + # Second argument is binary to execute.
>> + local filter="$1"
>> + shift
>> + local program="$1"
>> + shift
>> + echo $program $@ >> config.log
>> + $program $@ >> config.log 2>&1
>> + $program $@ 2>&1 | grep ${filter} >> /dev/null || return $?
>> +
>> +}
>> +
>> +create_library() {
>> + do_run "$ar" -rc${1} $TMPA $TMPO
>> +}
>> +
>> +create_index() {
>> + do_run "$ranlib" $TMPA
>> +}
>> +
>> +find_library_symbol() {
>> + do_run_filter ${1} "$nm" $TMPA
>> +}
>> +
>> # symbolically link $1 to $2. Portable version of "ln -sf".
>> symlink() {
>> rm -rf "$2"
>> @@ -306,6 +344,8 @@ libs_tools=""
>> audio_win_int=""
>> libs_qga=""
>> debug_info="yes"
>> +cfi="no"
>> +cfi_debug="no"
>> stack_protector=""
>> safe_stack=""
>> use_containers="yes"
>> @@ -1285,6 +1325,14 @@ for opt do
>> ;;
>> --disable-werror) werror="no"
>> ;;
>> + --enable-cfi) cfi="yes"
>> + ;;
>> + --disable-cfi) cfi="no"
>> + ;;
>> + --enable-cfi-debug) cfi_debug="yes"
>> + ;;
>> + --disable-cfi-debug) cfi_debug="no"
>> + ;;
>> --enable-stack-protector) stack_protector="yes"
>> ;;
>> --disable-stack-protector) stack_protector="no"
>> @@ -1838,6 +1886,10 @@ disabled with --disable-FEATURE, default is enabled if available:
>> module-upgrades try to load modules from alternate paths for upgrades
>> debug-tcg TCG debugging (default is disabled)
>> debug-info debugging information
>> + cfi Enable Control-Flow Integrity for indirect function calls.
>> + Depends on clang/llvm >= 3.9 and is incompatible with modules
>> + cfi-debug In case of a cfi violation, a message containing the line that
>> + triggered the error is written to stderr
>> sparse sparse checker
>> safe-stack SafeStack Stack Smash Protection. Depends on
>> clang/llvm >= 3.7 and requires coroutine backend ucontext.
>> @@ -5948,6 +6000,129 @@ if test "$plugins" = "yes" &&
>> "for this purpose. You can't build with --static."
>> fi
>>
>> +########################################
>> +# cfi (Control Flow Integrity)
>> +
>> +if test "$cfi" = "yes"; then
>> + # Compiler/Linker Flags that needs to be added for cfi:
>> + # -fsanitize=cfi-icall to enable control-flow integrity checks on
>> + # indirect function calls.
>> + # -fsanitize-cfi-icall-generalize-pointers to allow indirect function calls
>> + # with pointers of a different type (i.e. pass a void* to a
>> + # function that expects a char*). Used in some spots in QEMU,
>> + # with compile-time type checks done by macros
>> + # -fsanitize-blacklist, to disable CFI on specific functions.
>> + # required for some TCG functions that call runtime-created or
>> + # runtime-linked code. More details in cfi-blacklist.txt
>> + # -flto=thin to enable link-time optimization. This is required for the
>> + # implementation of CFI to work properly across object files
>> + # -fuse-ld=gold Since some of the objects are packed into static libraries,
>> + # which are not supported by the bfd linker.
>> + test_cflag="-fsanitize=cfi-icall -fsanitize-cfi-icall-generalize-pointers -flto=thin -fsanitize-blacklist=${source_path}/cfi-blacklist.txt"
>> + test_ldflag="-fsanitize=cfi-icall -flto=thin -fuse-ld=gold -fsanitize-blacklist=${source_path}/cfi-blacklist.txt"
>> +
>> + if test "$cfi_debug" = "yes"; then
>> + # Disable the default trap mechanism so that a error message is displayed
>> + # when a CFI violation happens. The code is still terminated after the
>> + # message
>> + test_cflag="${test_cflag} -fno-sanitize-trap=cfi-icall"
>> + test_ldflag="${test_ldflag} -fno-sanitize-trap=cfi-icall"
>> + fi
>> +
>> + # Check that cfi is supported.
>> + # Need to check for:
>> + # - Valid compiler, that supports cfi flags
>> + # - Valid ar, ranlib and nm, able to work with intermediate code (for lto)
>> + # - Incompatible configure options (plugins and modules) that use dlsym at
>> + # runtime (indirect function calls to shared libraries is not supported)
>> +
>> + #### Check for a valid *ar* for link-time optimization.
>> + # Test it by creating a static library and linking it
>> + # Compile an object first
>> + cat > $TMPC << EOF
>> +int fun(int val);
>> +
>> +int fun(int val) {
>> + return val;
>> +}
>> +EOF
>> + if ! compile_object "-Werror $test_cflag"; then
>> + error_exit "Control Flow Integrity is not supported by your compiler"
>> + fi
>> + # Create a library out of it
>> + if ! create_library "s" ; then
>> + error_exit "LTO is required for CFI, but is not supported by ar. This usually happens when using gnu ar. Try switching to LLVM ar"
>> + fi
>> + # Now create a binary using the library
>> + cat > $TMPC << EOF
>> +int fun(int val);
>> +
>> +int main(int argc, char *argv[]) {
>> + return fun(0);
>> +}
>> +EOF
>> + if ! compile_prog "-Werror $test_cflag" "$test_ldflag -L${TMPDIR1} -l${TMPB}"; then
>> + error_exit "LTO is required for CFI, but is not supported by ar. This usually happens when using gnu ar. Try switching to LLVM ar"
>> + fi
>> +
>> + #### Check for a valid *ranlib* for link-time optimization.
>> + # Test it by creating a static library without index, indexing and linking it
>> + cat > $TMPC << EOF
>> +int fun(int val);
>> +
>> +int fun(int val) {
>> + return val;
>> +}
>> +EOF
>> + if ! compile_object "-Werror $test_cflag"; then
>> + error_exit "Control Flow Integrity is not supported by your compiler"
>> + fi
>> + # Create a library explicity without an index
>> + if ! create_library "S" ; then
>> + error_exit "LTO is required for CFI, but is not supported by ar. This usually happens when using gnu ar. Try switching to LLVM ar"
>> + fi
>> + # Now run ranlib to index it
>> + if ! create_index ; then
>> + error_exit "LTO is required for CFI, but is not supported by ranlib. This usually happens when using gnu ranlib. Try switching to LLVM ranlib"
>> + fi
>> + # If ranlib worked, we can now use the library
>> + cat > $TMPC << EOF
>> +int fun(int val);
>> +
>> +int main(int argc, char *argv[]) {
>> + return fun(0);
>> +}
>> +EOF
>> + if ! compile_prog "-Werror $test_cflag" "$test_ldflag -L${TMPDIR1} -l${TMPB}"; then
>> + error_exit "LTO is required for CFI, but is not supported by ranlib. This usually happens when using gnu ranlib. Try switching to LLVM ranlib"
>> + fi
>> +
>> + #### Check for a valid *nm* for link-time optimization.
>> + # nm does not return an error code if the file is unsupported, just
>> + # print a warning text. So, check if *fun* is one of the symbols found by nm
>> + # in the previously created static library
>> + if ! find_library_symbol "fun" ; then
>> + error_exit "LTO is required for CFI, but is not supported by nm. This usually happens when using gnu nm. Try switching to LLVM nm"
>> + fi
>> +
>> + #### The toolchain supports CFI, let's check for incompatible options
>> +
>> + if test "$modules" = "yes"; then
>> + error_exit "Control Flow Integrity is not compatible with modules"
>> + fi
>> +
>> + #### All good, add the flags for CFI to our CFLAGS and LDFLAGS
>> + # Flag needed both at compilation and at linking
>> + QEMU_CFLAGS="$QEMU_CFLAGS $test_cflag"
>> + QEMU_LDFLAGS="$QEMU_LDFLAGS $test_ldflag"
>> +
>> +else
>> + if test "$cfi_debug" = "yes"; then
>> + error_exit "Cannot enable Control Flow Integrity debugging since CFI is not enabled"
>> + fi
>> +fi
>> +
>> +
>> ########################################
>> # See if __attribute__((alias)) is supported.
>> # This false for Xcode 9, but has been remedied for Xcode 10.
>> @@ -6856,6 +7031,8 @@ echo "gprof enabled $gprof"
>> echo "sparse enabled $sparse"
>> echo "strip binaries $strip_opt"
>> echo "profiler $profiler"
>> +echo "cfi $cfi"
>> +echo "cfi debug $cfi_debug"
>> echo "static build $static"
>> echo "safe stack $safe_stack"
>> if test "$darwin" = "yes" ; then
>>
>
next prev parent reply other threads:[~2020-07-02 12:20 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-07-02 5:49 [PATCH 0/2] Add support for Control-Flow Integrity Daniele Buono
2020-07-02 5:49 ` [PATCH 1/2] check-block: enable iotests with cfi-icall Daniele Buono
2020-07-02 5:49 ` [PATCH 2/2] configure: add support for Control-Flow Integrity Daniele Buono
2020-07-02 9:45 ` Paolo Bonzini
2020-07-02 12:19 ` Daniele Buono [this message]
2020-07-02 9:52 ` Daniel P. Berrangé
2020-07-02 12:50 ` Daniele Buono
2020-07-02 12:59 ` Paolo Bonzini
2020-07-02 13:38 ` Alexander Bulekov
2020-07-02 15:43 ` Daniele Buono
2020-08-10 19:01 ` Daniele Buono
2020-08-10 19:39 ` Paolo Bonzini
2020-08-10 21:33 ` Alexander Bulekov
2020-08-13 14:00 ` Daniele Buono
2020-07-02 13:12 ` Daniel P. Berrangé
2020-07-02 15:02 ` Daniele Buono
2020-07-16 21:57 ` Daniele Buono
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=764bc77f-693d-66fb-20a0-4c77ba935eb5@linux.vnet.ibm.com \
--to=dbuono@linux.vnet.ibm.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).