Skip to content
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

cmd/asm,cmd/compile: add support for control flow integrity #66054

Open
4a6f656c opened this issue Mar 1, 2024 · 9 comments
Open

cmd/asm,cmd/compile: add support for control flow integrity #66054

4a6f656c opened this issue Mar 1, 2024 · 9 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@4a6f656c
Copy link
Contributor

4a6f656c commented Mar 1, 2024

Modern CPUs provide support for branch tracking control flow integrity (BTCFI) in the form of Intel's Indirect Branch Tracking (IBT) and AARCH64's Branch Tracking Identification (BTI). BTCFI helps to mitigate flow control based exploits, such as those achieved via Jump Oriented Programming (JOP).

Some operating systems (for example, OpenBSD 7.4 onwards) are starting to enforce IBT and BTI on hardware where it is available (Intel Tigerlake onwards, Apple's M2, etc). Go binaries need to be marked with PT_OPENBSD_NOBTCFI in order to allow them to execute. Other operating systems have varying levels of support (Linux enables IBT in the kernel, but not userland AIUI - https://lwn.net/Articles/889475/).

Go should ideally support BTCFI - in the IBT/BTI case it is effectively a matter of providing a "landing pad" instruction (endbr64 on amd64 and bti c on arm64), at function entry points and any other locations where control flow is expected to land. On machines that do not support these instructions (or have BTCFI enabled/enforced in userland), they are effectively a no-ops. This does potentially impact code where it is intentional to jump into a function at some offset - for example, duff devices (DUFFCOPY, DUFFZERO) and jump tables. This would also need landing pads at each potential entry point, or revisiting/disabling.

@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Mar 1, 2024
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/568435 mentions this issue: cmd/link,debug/elf: mark Go binaries with no branch target CFI on openbsd

@Jorropo
Copy link
Member

Jorropo commented Mar 1, 2024

How much good enough are you asking for this to be ? For comparison don't even enable ASLR by default. (and -buildmode=pie greatly increase build sizes)
Due to goroutine preamption my first guess would be that all instructions would need to be marked as valid jump destinations, making the value very dubious. Even if we only mark safe points and expected jump destinations that still a lot of places.

@mknyszek mknyszek added this to the Unplanned milestone Mar 4, 2024
@mknyszek mknyszek added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Mar 4, 2024
gopherbot pushed a commit that referenced this issue Mar 10, 2024
…nbsd

OpenBSD enables Indirect Branch Tracking (IBT) on amd64 and Branch Target
Identification (BTI) on arm64, where hardware permits. Since Go generated
binaries do not currently support IBT or BTI, temporarily mark them with
PT_OPENBSD_NOBTCFI which prevents branch target CFI from being enforced
on execution. This should be removed as soon asn IBT and BTI support are
available.

Fixes #66040
Updates #66054

Change-Id: I91ac05736e6942c54502bef4b8815eb8740d2d5e
Reviewed-on: https://go-review.googlesource.com/c/go/+/568435
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Josh Rickmar <jrick@zettaport.com>
Reviewed-by: Keith Randall <khr@golang.org>
Run-TryBot: Joel Sing <joel@sing.id.au>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
@bjorndm
Copy link

bjorndm commented Apr 16, 2024

BTCFU is a palliative for C and C++ which allow ROP due to their deficient design and allowance for undefined behavior.

If one doesn't use unsafe, then ROP should not be possible in Go. This might negatively affect the performance of Go programs and is another example of how non C programming languages have to pay "C taxes" as it were. This should certainly remain optional.

Furthermore I think it is crazy that now we have to introduce a "come from" assembly instruction just because of the critical design flaws of C. Rather, we should be working on getting rid of C everywhere, including in OS.

@4a6f656c
Copy link
Contributor Author

I have a branch that adds branch tracking control flow integrity (BTCFI) to Go for arm64 (in the form of BTI) and amd64 (in the form of IBT).

@4a6f656c
Copy link
Contributor Author

Due to goroutine preamption my first guess would be that all instructions would need to be marked as valid jump destinations, making the value very dubious. Even if we only mark safe points and expected jump destinations that still a lot of places.

BTCFI generally only tracks indirect branches. In the case of asynchronous preemption there is no change for amd64, as it resumes via RET which is pulling the address off the stack (and is permissible with IBT enforced). In the case of arm64, the current code is using JMP (R27) which is an indirect branch and is not permissible with BTI enforced - this is however easily fixed via the use of RET with a specific register.

@Jorropo
Copy link
Member

Jorropo commented Jun 22, 2024

@4a6f656c thx that answers I had and couldn't understand from your code.
I thought RET would be considered an indirect branch.
But then does that means IBT does not do anything against Return-Oriented-Programing ?

@Jorropo Jorropo closed this as completed Jun 22, 2024
@Jorropo Jorropo reopened this Jun 22, 2024
@4a6f656c
Copy link
Contributor Author

If one doesn't use unsafe, then ROP should not be possible in Go. This might negatively affect the performance of Go programs and is another example of how non C programming languages have to pay "C taxes" as it were. This should certainly remain optional.

This also implies that there are no bugs in Go's compiler or runtime, that C code is never called (hint, don't use Go on macOS) and that Go is never called from C code (because that means the C code cannot have branch tracking enforced, since the Go code does not support it).

Furthermore I think it is crazy that now we have to introduce a "come from" assembly instruction just because of the critical design flaws of C. Rather, we should be working on getting rid of C everywhere, including in OS.

It's not a "come from", rather a "it is permissible to indirect branch to here" instruction.

@Jorropo
Copy link
Member

Jorropo commented Jun 22, 2024

Add assembly routines to the list of things not to use to never have ROPs.
Which are used on literally all platforms by the runtime and some third packages.

@4a6f656c
Copy link
Contributor Author

4a6f656c commented Jun 22, 2024

@4a6f656c thx that answers I had and couldn't understand from your code. I thought RET would be considered an indirect branch. But then does that means IBT does not do anything against Return-Oriented-Programing ?

Right, it is forward-edge flow control rather than reverse-edge flow control (the original description should have said Jump-Oriented Programming, which I've fixed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants