Skip to main content
  1. Posts/

Branch Target Identification (BTI)

·689 words·4 mins· loading · loading ·
Rafael Sadowski
Rafael Sadowski
Shut up and hack

OpenBSD 7.4 added enforcement of indirect branch targets (BTI on arm64, IBT on Intel amd64), unless a linker flag (-Wl,-z,nobtcfi) requests no enforcement. IBT support, meaning Tiger Lake (11th Gen) on Intel CPUs.

  • Security improvements: o Enable indirect branch tracking (IBT) on amd64 and branch target identification (BTI) on arm64 in both the kernel and in userland. On hardware that supports this feature, it helps enforcing control flow integrity by making sure malicious code cannot jump into the middle of a function.

This means that it is always switched ON on OpenBSD.

How can you imagine IBT

One step back. How can you imagine IBT?

Let’s imagine you’re playing a video game where you have to choose different paths to reach the end of a level. Think of the CPU as playing its own game, but instead of levels, it has instructions to follow. Sometimes, it reaches a point where it has to make a choice, like you choosing which path to take in your game. This is similar to reaching a crossroad and deciding whether to go left or right.

“Branch Target Identification” (BTI) and “Indirect Branch Tracking” (IBT) is like giving the computer a map that highlights only the correct paths and warns it about the wrong ones. So, every time it reaches a crossroad (or a point where it needs to make a decision), BTI helps it pick the safe and correct path. This way, it avoids making mistakes or being tricked into going down a dangerous path added by an attacker.

IBT in open source landscape

If we look at the third party packages on OpenBSD, we can see over 50 times that USE_NOBTCFI is set to Yes.

Here is a list from May. 15, 2024. I think there will be many more, as more and more OpenBSD developers are on the road with new notebooks.

  • benchmarks/wrk
  • cad/kicad
  • devel/jdk/
  • devel/zeal
  • editors/libreoffice
  • editors/neovim
  • emulators/citra
  • emulators/dolphin
  • emulators/higan
  • emulators/mednafen
  • emulators/ppsspp
  • emulators/qemu
  • emulators/retroarch
  • games/0ad/base
  • games/godot
  • games/love
  • games/luasteam
  • games/openarena
  • games/openmw
  • games/solarus/solarus
  • games/taisei
  • games/tome4
  • graphics/ffmpeg
  • lang/dmd
  • lang/erlang/25
  • lang/erlang/26
  • lang/ghc
  • lang/go
  • lang/gprolog
  • lang/hashlink
  • lang/luajit
  • lang/mono
  • lang/ocaml
  • lang/python
  • lang/racket-minimal
  • lang/sbcl
  • mail/rspamd
  • math/labplot
  • multimedia/mpv
  • multimedia/upplay
  • net/hexchat
  • net/snort
  • textproc/goldendict-ng
  • textproc/wkhtmltopdf
  • www/luakit
  • www/mozilla/mozilla
  • www/webkitgtk4
  • x11/gnome/gjs
  • x11/kde-applications/cantor
  • x11/mplayer
  • x11/qt5/qtwebengine
  • x11/qt5/qtwebkit
  • x11/qt6/qtwebengine
  • x11/vlc
  • x11/gnustep (Only aarch64)

I think we can overlook games from a security perspective. Looking further down the list, there are significant applications like web engines, multimedia tools, and emulators.

I’ve noticed that many of these are related to Lua, likely due to LuaJIT itself.

Removing these from consideration, it’s evident that several major programming languages such as Python, Java, Go, Erlang, Haskell, .NET (Mono), and OCaml do not support IBI.

So what at first sounds like 50+ ports is actually much, much more. I am curious to see how this will develop in the upstream projects.

On a positive note, the OpenBSD team has already patched many essential libraries. Some examples include:

  • devel/ffcall
  • devel/gmp
  • games/allegro
  • graphics/ffmpeg
  • graphics/imlib2
  • lang/deno
  • lang/node
  • lang/rust
  • mail/rspamd
  • multimedia/aom
  • multimedia/dav1d
  • multimedia/gstreamer1/plugins-good
  • multimedia/libass
  • multimedia/libdv
  • multimedia/libvpx
  • multimedia/openh264
  • multimedia/x264
  • multimedia/x265
  • security/libgcrypt
  • www/chromium
  • www/iridium
  • www/ungoogled-chromium

How to find indirect branch tracking issues

If your application crashes with a SIGILL, using ktrace(1) can be helpful. You can find an example here:

$ ktrace /usr/local/bin/konqueror
$ kdump -f ktrace.out > konqueror.kdump
$ grep ILL_BTCFI konqueror.kdump
24296 konqueror PSIG  SIGILL SIG_DFL code=ILL_BTCFI addr=0x48feec0f980 trapno=21

Assuming you have identified an application crash caused by ILL_BTCFI, you will need to locate the sections of code with indirect jumps and calls and implement the endbr64 command as a crucial mitigation measure. endbr64, which stands for “ENDBRanch 64-bit”, is a security-enhancing instruction within the x86-64 architecture, utilized by processors adhering to the AMD64 or Intel 64 instruction sets, which support 64-bit processing.

This command is specifically designed to mark valid targets for indirect jumps and calls—areas frequently targeted in speculative execution attacks such as “Spectre” and “Meltdown”. These attacks take advantage of the processor’s ability to perform speculative execution of instructions to enhance performance, a process that can inadvertently lead to unauthorized data access.

By integrating endbr64 instructions before the points where indirect jumps or calls occur, you can provide the processor with clear indicators of legitimate targets.