Live data from Hacker News

Mandatory enforcement of indirect branch targets

undeadly.org

51–60 of 134 posts

Re: Mandatory enforcement of indirect branch targets

#51
post #43

Earlier quoted context omitted.

> Why should every function start with endbr64 command? Aren't functions usually called directly? They're usually called directly, but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required. > Also, is it required to insert endbr64 command after function calls (for return address)? No, IBT is only for jmp and call. SS is the equ…

> but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required Then why not just have the compiler break down every non-static function into two blocks: a static function that contains all the logic, and a non-static function that just contains an IBT and a direct jump to the static function? (Or, better yet, place the non-static…

[deleted]

Re: Mandatory enforcement of indirect branch targets

#52
post #2

For anybody unfamiliar with this, as I was, this appears to refer to Intel's Indirect Branch Tracking feature[1] (and the equivalent on ARM, BTI). The idea is that an indirect branch can only pass control to a location that starts with an "end branch" instruction. An indirect branch is one that jumps to a location whose value is loaded or computed from either a register or memory address: think calling a function poi…

Interesting. Seems like enforcement on Intel CPUs is supported since Tiger Lake (so ~2020). Windows has basically the same feature implemented in software since 2015, called Control Flow Guard [1]. I wonder what the story there is, and if Windows has any plans to (get everyone to) switch to the hardware version once those CPUs have sufficient market share. 1: https://learn.microsoft.com/en-us/windows/win32/secbp/cont…

Windows also recently implemented a far better version of this called Extended Flow Guard (XFG) that not only checks whether the location is a valid destination, but also whether it's a valid destination for that specific source.

For example, for any virtual function call or function pointer call, the destination must have a correct tag with the hash of the arguments. It's much more secure, and also faster, since loading the tag from memory can be merged with loading the actual code after it.

I wish this was the one implemented in hardware..

Re: Mandatory enforcement of indirect branch targets

#53
post #8
post #4

Earlier quoted context omitted.

The fun fact being that older CPUs decode ENDBR64 as a slightly weird NOP (with no architectural effects), but it'll fault on original Pentiums: https://stackoverflow.com/questions/56120231/how-do-old-cpus...

Various architectures do other interesting things with NOPs, IIRC one convention on PowerPC had something vaguely related to debugging or tracing (I can't remember the details or find any references right now).

https://www.ibm.com/docs/en/aix/7.3?topic=h-hpmstat-command:

“random_samp_ele_crit=name

Specifies the random criteria for selecting the instructions for sampling. Valid values for this option are as follows:

ALL_INSTR

All instructions are eligible. This value is the default setting.

LOAD_STORE

The operation is routed to the Load Store Unit (LSU); for example, load, store.

PROB_NOP

Sample only special no-operation instructions, which are called Probe NOP events.

[…]”

Re: Mandatory enforcement of indirect branch targets

#54
post #21

Theo had to get his digs in against Linux in that announcement. Why not just focus on what OpenBSD is doing, and maybe contrast it to what Linux does without the speculation that they will still be doing the same thing in 20 years. He's unquestionably brilliant, but I've had a few encounters with him on the mailing lists and he is so quick to take offense where none was meant and drop into name-calling and insults. I…

It's an important comparison of the mechanisms, even in 2023, you can still find binaries on modern Linux distributions with executable stacks due to the fail-open design, 20 years later. The fact that Linux hasn't learned the right lessons in 20 years, and has chosen to "double down" in respect to IBT/BTI, does not inspire confidence that they will ever fix it. I'd say his 20 year estimate was in fact being pretty g…

> It's an important comparison of the mechanisms, even in 2023, you can still find binaries on modern Linux distributions with executable stacks due to the fail-open design, 20 years later.

Unfortunately, for C code using GCC’s nested functions extension (or for languages that want to be ABI-compatible with C and support nested functions, like that paragon of advanced features called Pascal /s ), there’s no other compilation strategy in current ABIs. The patches to switch C (and not just Ada) to function descriptors[1] with an ABI break have been sitting on the GCC mailing list since approximately forever[2], but it doesn’t seem like there’s been any progress.

[1] The strategy is basically to compile (*fp)() not as

  call *%rax
but as (untested)

     test $1, %rax
     jz 1f
     mov 8(%rax), %r10
     mov (%rax), %rax
  1: call *%rax
thus essentially inlining the (currently stack-allocated) closure calling thunk at all indirect call sites. It is ABI-compatible on x86 and x86-64 with all code that does not involve nested functions, place functions at odd addresses, or tag function pointers itself (and I think with all arm64 and riscv code, although arm32’s usage of the low pointer bit for Thumb interworking is bound to make this trickier).

[2] https://gcc.gnu.org/legacy-ml/gcc-patches/2019-01/msg00735.h...

Re: Mandatory enforcement of indirect branch targets

#55
post #43

Earlier quoted context omitted.

> Why should every function start with endbr64 command? Aren't functions usually called directly? They're usually called directly, but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required. > Also, is it required to insert endbr64 command after function calls (for return address)? No, IBT is only for jmp and call. SS is the equ…

> but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required Then why not just have the compiler break down every non-static function into two blocks: a static function that contains all the logic, and a non-static function that just contains an IBT and a direct jump to the static function? (Or, better yet, place the non-static…

What is the overhead of executing the IBT NOP?

Re: Mandatory enforcement of indirect branch targets

#56
post #14

I still run OpenBSD where I can, especially where security is more important. Yes, it's still missing A LOT of functionally compared to other UNIX-like systems, but security bases tend to be well covered.

I find OpenBSD's hardware support especially lacking. It doesn't really work that well on at least 3 devices where I tried it on (all Dell laptops from various generations, 3-10 years old), whereas Linux runs perfectly out-of-the-box on all three. Which is sad, as I kinda like the *BSD approach to things

Not my experience at all, it works very well with a new Acer laptop I own: the graphics work (Intel Xe - 12th gen processor), audio, touchpad, keyboard (and special keyboard keys like brightness), wifi... All I had to do is to download the firmware with fw_update, nothing more.

Also I was pleasantly surprised to hear they support Apple M1/M2 Macs. Asahi Linux gets a lot of press around here but I had no idea OpenBSD supported it.

Re: Mandatory enforcement of indirect branch targets

#57
post #14

I still run OpenBSD where I can, especially where security is more important. Yes, it's still missing A LOT of functionally compared to other UNIX-like systems, but security bases tend to be well covered.

I don't really buy their approach to security honestly. Trying to fix all bugs is great, but they provide little to prevent unknown bugs bing exploited (pledge is nice for software that opts in to use it, but otherwise not so much). I'd love to see them implement something like AppArmor with their approach, it would probably be amazing. I actually think NetBSD is a pretty interesting alternative, it has some nice sec…

> they provide little to prevent unknown bugs bing exploited

They provide plenty of mitigations (https://www.openbsd.org/innovations.html). In fact OP's article is for preventing unknown bugs from being exploited.

Re: Mandatory enforcement of indirect branch targets

#58
post #4
post #2

For anybody unfamiliar with this, as I was, this appears to refer to Intel's Indirect Branch Tracking feature[1] (and the equivalent on ARM, BTI). The idea is that an indirect branch can only pass control to a location that starts with an "end branch" instruction. An indirect branch is one that jumps to a location whose value is loaded or computed from either a register or memory address: think calling a function poi…

The fun fact being that older CPUs decode ENDBR64 as a slightly weird NOP (with no architectural effects), but it'll fault on original Pentiums: https://stackoverflow.com/questions/56120231/how-do-old-cpus...

There's a good question in the comments there that I still don't see the answer to. How does this work if there's an interrupt between the branch and the endbranch? Does the OS need to save/restore the "branchness" bit?

Re: Mandatory enforcement of indirect branch targets

#59
post #43

Earlier quoted context omitted.

> Why should every function start with endbr64 command? Aren't functions usually called directly? They're usually called directly, but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required. > Also, is it required to insert endbr64 command after function calls (for return address)? No, IBT is only for jmp and call. SS is the equ…

> but unless the compiler can prove that they always are (e.g., if they're static and nothing in the same file takes the address), endbr64 is required Then why not just have the compiler break down every non-static function into two blocks: a static function that contains all the logic, and a non-static function that just contains an IBT and a direct jump to the static function? (Or, better yet, place the non-static…

That's absolutely doable, just... How much is predicted unconditional jump slower/faster than ENDBR64? What's the ratio of virtual/static calls in real-world programs? And while your last proposal ("foo: endbr64; foo_internal: ") evades those questions, it raises up questions about maintaining function alignment (16 bytes IIRC? Is this even necessary today?) and restructuring the compiler to distinguish the inner/external symbol addresses. Plus, of course, somebody has to actually sit down and write the code to implement that, as opposed to just adding "if (func->is_escaping) emit_endbr(...);" at the beginning of the code that emits the object code for a function body.

Re: Mandatory enforcement of indirect branch targets

#60
post #36

Earlier quoted context omitted.

Now, yes. Linus wasn't always so abrasive though. At some point he caught up to Theo.

Linus has been trying to calm down in recent years, in large part because he decided he no longer wanted to be lumped in with the crowd that endlessly complains about political correctness. https://www.bbc.com/news/technology-45664640

Yeah this is good stuff, and why I felt bad about making the comparison. Not saying Theo is in that camp, but Linus is trying to be less abrasive in general, and Theo is not.
Post reply on HN