mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
x86: Build check for embedded endbr64 instructions
An interesting corner case occurs when the byte sequence making up endbr64 ends up on a non-instruction boundary. Such embedded instructions mark legal indirect branch targets as far as the CPU is concerned, which aren't legal as far as the logic is concerned. When CET-IBT is active, check for embedded byte sequences. Example failures look like: check-endbr.sh xen-syms Fail: Found 2 embedded endbr64 instructions 0xffff82d040325677: test_endbr64 at /local/xen.git/xen/arch/x86/x86_64/entry.S:28 0xffff82d040352da6: init_done at /local/xen.git/xen/arch/x86/setup.c:675 Signed-off-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
5d59421815
commit
4d037425dc
3 files changed
+93
No files matched your search
@@ -68,6 +68,7 @@ provided by your OS distributor:
|
||||
In addition to the above there are a number of optional build
|
||||
prerequisites. Omitting these will cause the related features to be
|
||||
disabled at compile time:
|
||||
* Binary-search capable grep (if building Xen with CET support)
|
||||
* Development install of Ocaml (e.g. ocaml-nox and
|
||||
ocaml-findlib). Required to build ocaml components which
|
||||
includes the alternative ocaml xenstored.
|
||||
|
||||
@@ -142,6 +142,9 @@ $(TARGET)-syms: $(BASEDIR)/prelink.o $(obj)/xen.lds
|
||||
| $(BASEDIR)/tools/symbols --all-symbols --xensyms --sysv --sort \
|
||||
>$(@D)/$(@F).map
|
||||
rm -f $(@D)/.$(@F).[0-9]* $(@D)/..$(@F).[0-9]*
|
||||
ifeq ($(CONFIG_XEN_IBT),y)
|
||||
$(SHELL) $(BASEDIR)/tools/check-endbr.sh $@
|
||||
endif
|
||||
|
||||
$(obj)/note.o: $(TARGET)-syms
|
||||
$(OBJCOPY) -O binary --only-section=.note.gnu.build-id $< $@.bin
|
||||
@@ -212,6 +215,9 @@ endif
|
||||
$(NM) -pa --format=sysv $(@D)/$(@F) \
|
||||
| $(BASEDIR)/tools/symbols --all-symbols --xensyms --sysv --sort >$(@D)/$(@F).map
|
||||
rm -f $(@D)/.$(@F).[0-9]* $(@D)/..$(@F).[0-9]*
|
||||
ifeq ($(CONFIG_XEN_IBT),y)
|
||||
$(SHELL) $(BASEDIR)/tools/check-endbr.sh $@
|
||||
endif
|
||||
else
|
||||
$(TARGET).efi: FORCE
|
||||
rm -f $@
|
||||
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Usage ./$0 xen-syms
|
||||
#
|
||||
set -e
|
||||
|
||||
# Pretty-print parameters a little for message
|
||||
MSG_PFX="${0##*/} ${1##*/}"
|
||||
|
||||
OBJCOPY="${OBJCOPY:-objcopy}"
|
||||
OBJDUMP="${OBJDUMP:-objdump}"
|
||||
ADDR2LINE="${ADDR2LINE:-addr2line}"
|
||||
|
||||
D=$(mktemp -d)
|
||||
trap "rm -rf $D" EXIT
|
||||
|
||||
TEXT_BIN=$D/xen-syms.text
|
||||
VALID=$D/valid-addrs
|
||||
ALL=$D/all-addrs
|
||||
BAD=$D/bad-addrs
|
||||
|
||||
# Check that grep can do binary searches. Some, e.g. busybox, can't. Leave a
|
||||
# warning but don't fail the build.
|
||||
echo "X" | grep -aob "X" -q 2>/dev/null ||
|
||||
{ echo "$MSG_PFX Warning: grep can't do binary searches" >&2; exit 0; }
|
||||
|
||||
#
|
||||
# First, look for all the valid endbr64 instructions.
|
||||
# A worst-case disassembly, viewed through cat -A, may look like:
|
||||
#
|
||||
# ffff82d040337bd4 <endbr64>:$
|
||||
# ffff82d040337bd4:^If3 0f 1e fa ^Iendbr64 $
|
||||
# ffff82d040337bd8:^Ieb fe ^Ijmp ffff82d040337bd8 <endbr64+0x4>$
|
||||
# ffff82d040337bda:^Ib8 f3 0f 1e fa ^Imov $0xfa1e0ff3,%eax$
|
||||
#
|
||||
# Want to grab the address of endbr64 instructions only, ignoring function
|
||||
# names/jump labels/etc, so look for 'endbr64' preceded by a tab and with any
|
||||
# number of trailing spaces before the end of the line.
|
||||
#
|
||||
${OBJDUMP} -j .text $1 -d -w | grep ' endbr64 *$' | cut -f 1 -d ':' > $VALID &
|
||||
|
||||
#
|
||||
# Second, look for any endbr64 byte sequence
|
||||
# This has a couple of complications:
|
||||
#
|
||||
# 1) Grep binary search isn't VMA aware. Copy .text out as binary, causing
|
||||
# the grep offset to be from the start of .text.
|
||||
#
|
||||
# 2) dash's printf doesn't understand hex escapes, hence the use of octal.
|
||||
#
|
||||
# 3) AWK can't add 64bit integers, because internally all numbers are doubles.
|
||||
# When the upper bits are set, the exponents worth of precision is lost in
|
||||
# the lower bits, rounding integers to the nearest 4k.
|
||||
#
|
||||
# Instead, use the fact that Xen's .text is within a 1G aligned region, and
|
||||
# split the VMA in half so AWK's numeric addition is only working on 32 bit
|
||||
# numbers, which don't lose precision.
|
||||
#
|
||||
eval $(${OBJDUMP} -j .text $1 -h |
|
||||
awk '$2 == ".text" {printf "vma_hi=%s\nvma_lo=%s\n", substr($4, 1, 8), substr($4, 9, 16)}')
|
||||
|
||||
${OBJCOPY} -j .text $1 -O binary $TEXT_BIN
|
||||
grep -aob "$(printf '\363\17\36\372')" $TEXT_BIN |
|
||||
awk -F':' '{printf "%s%x\n", "'$vma_hi'", int(0x'$vma_lo') + $1}' > $ALL
|
||||
|
||||
# Wait for $VALID to become complete
|
||||
wait
|
||||
|
||||
# Sanity check $VALID and $ALL, in case the string parsing bitrots
|
||||
val_sz=$(stat -c '%s' $VALID)
|
||||
all_sz=$(stat -c '%s' $ALL)
|
||||
[ "$val_sz" -eq 0 ] && { echo "$MSG_PFX Error: Empty valid-addrs" >&2; exit 1; }
|
||||
[ "$all_sz" -eq 0 ] && { echo "$MSG_PFX Error: Empty all-addrs" >&2; exit 1; }
|
||||
[ "$all_sz" -lt "$val_sz" ] && { echo "$MSG_PFX Error: More valid-addrs than all-addrs" >&2; exit 1; }
|
||||
|
||||
# $BAD = $ALL - $VALID
|
||||
sort $VALID $ALL | uniq -u > $BAD
|
||||
nr_bad=$(wc -l < $BAD)
|
||||
|
||||
# Success
|
||||
[ "$nr_bad" -eq 0 ] && exit 0
|
||||
|
||||
# Failure
|
||||
echo "$MSG_PFX Fail: Found ${nr_bad} embedded endbr64 instructions" >&2
|
||||
${ADDR2LINE} -afip -e $1 < $BAD >&2
|
||||
exit 1
|
||||