from Hacker News

D4D4

by csense on 8/17/25, 3:42 PM with 56 comments

  • by JdeBP on 8/21/25, 9:45 AM

    It's not exploitable. It's an exploit mitigation, in fact. It's not a bug; it's intentional that it works this way. And Nathan Michaels didn't think that if you want to find Theo de Raadt writing on some subject, better try OpenBSD discussion fora, not the LLVM mailing list. (-:

    This was put into OpenBSD back in 2017. It's not "trap instructions". It's "trapsleds". The idea is to trap the sleds (a.k.a. slides) of NOP instructions that linkers used to put in between compiled code for alignment, and which could be exploited with "return-oriented programming" where an attacker could cause execution to jump to any address in the padding range (possibly needing the inexactitude because of constraints upon byte substitutions) and slide along all of the NOPs to the target function.

    * https://undeadly.org/cgi?action=article;sid=20170622065629

    * https://isopenbsdsecu.re/mitigations/trapsled/

  • by Normal_gaussian on 8/21/25, 9:35 AM

    This is cool; and yes, fairly clearly a bug with the commit showing both the name (trap instruction) and INT3 (debug) being used for x86.

    I definitely wouldn't have got this far looking at this - I'd have quickly assumed it was a sentinel value being used for padding and moved on with my day. Good work.

  • by colanderman on 8/21/25, 11:27 AM

    Hex D4xxxxxx is indeed (almost) BRK... on ARM64 [1].

    Being ARM32, these should be BKPT (hex BExx). [2]

    [1] https://developer.arm.com/documentation/ddi0602/2025-06/Base...

    [2] https://developer.arm.com/documentation/ddi0597/2024-09/Base...

  • by nneonneo on 8/21/25, 11:32 PM

    I'm confused, because d4d4d4d4 doesn't look like a trap in 32-bit ARM either:

      0x0000000000000000:  D4 D4 D4 D4    ldrble sp, [r4], #0x4d4
    
    (from https://shell-storm.org/online/Online-Assembler-and-Disassem...)

    This is "load byte at [r4 + 0x4d4] into sp, then add 0x4d4 to r4, but only if the condition flags signal a comparison result of less-than-or-equal". It is unlikely to be a useful instruction, since it causes the stack pointer register SP to be less than 0x100 (if [r4+0x4d4] is even a valid address), but it sure as heck isn't a trap. And, if the condition flags are right, this is just a NOP instruction.

    As far as I can tell, 0xd4d4d4d4 is only invalid on AArch64, and only because it happens to not yet be a defined instruction. 0xd4 does in fact introduce an exception generation instruction, but 0xd4d4xxxx is invalid as it is an unallocated combination of bits. However, nothing prevents this from being a defined instruction in the future, which makes 0xd4d4d4d4 a really bad choice as it could turn out to be a valid instruction in the future that performs an unexpected operation.

    In all, 0xd4 looks like a terrible choice for a padding byte for any ARM architecture, so it's a real mystery why this specific choice was made.

  • by Arnavion on 8/21/25, 10:18 PM

    Only tangentially related, but RISC-V intentionally makes any instruction starting with 0x0000 an illegal instruction instead of a NOP, for exactly the reason to prevent NOP-sleds. The official NOPs are 0x0001 (compressed 16-bit instruction) and 0x00000013 (regular 32-bit instruction; instructions are LE so 0x13 is the first byte in memory), both equivalent to addi x0, x0, 0, though many other instructions can act as NOPs by virtue of writing to the zero register.
  • by skrebbel on 8/21/25, 9:43 AM

    Cool story! If I may rant off topic a bit though, it boggles my mind that people put stuff like this:

    > [This patch] fills holes in executable sections with 0xd4 (ARM) or 0xef (MIPS). These trap instructions were suggested by Theo de Raadt.

    into commit messages, but not in the code. What's the cost? What's the downside to having a 2 to 3 line comment above a constant in a C file? Why pretend like all this is super obvious when clearly it isn't?

    There seems to be some unwritten cultural rule, particularly in FOSS land, that you're supposed to write code as if you're an all-knowing oracle, as if everything is obvious, and so comments are for losers (as are descriptive variable names). I simply can't understand how and why that developed.

  • by EDEADLINK on 8/21/25, 2:30 PM

    Digging into this a bit the best mnenomic I have found for a trap on ARM is "udf #0" which works on all the arm's on godbolt (and with -mthumb).

    This saves us from selecting between BRK and BKPT.

    I have not found a single sequence of bytes that would work on thumb, armv7 and AARCH64.

  • by davedx on 8/21/25, 9:25 AM

    Sounds like this could cause some awful heisenbugs if the instruction was ever reached?
  • by rokkamokka on 8/21/25, 9:16 AM

    God I love blame for use cases like this
  • by thayne on 8/22/25, 12:16 AM

    Why do the compilation units need to be padded if the functions don't need to be aligned.
  • by nokeya on 8/21/25, 9:18 AM

    May this be exploited?
  • by rasz on 8/21/25, 9:20 AM

    TLDR: Linker is kicking up the 4d3d3d3.
  • by chaboud on 8/21/25, 9:32 AM

    Do you want obfuscated supply-chain state-actor vulnerabilities? Because this is how you get obfuscated supply-chain state-actor vulnerabilities!

    (Unless someone stays up all night to find the bugs....)

  • by k33n on 8/21/25, 10:30 AM

    I think it’s just an artifact of objdump and not even real.
  • by mprovost on 8/21/25, 6:25 PM

    This reminds me of coding with AI where slightly changing the prompt gives you different code and you don't really understand why but you use it anyway. In this case it's not even the compiler that's adding the incorrect instructions - it's the linker. Someday we'll just trust the AI to spit out working code the same way we assume that the C toolchain produces reasonable assembler. And when it doesn't it's remarkable enough to warrant a blog post and HN discussion.