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
The conditional branch-backward instruction it is is almost as bad as the series of NOPs, since it is still likely to redirect an attacker to functioning code. (If the attacker can clear the mi flag first, these are just NOPs!)
Hence yes, this is a broken exploit mitigation.
I intentionally also pointed you to a collection of several critiques of the whole idea, long-since made. (-:
There, all instructions are 32 bits and D4D4 is only 16 bits.
And why, in your own words, is it OK for the jump to be a conditional backwards jump?
Your first comment says "it's intentional that it works this way".
The article doesn't say it is.
> It's an exploit mitigation, in fact.
The article made that clear.
> It's not a bug; it's intentional that it works this way.
What is "this way"? Trap or jump? If you're saying a jump is supposed to count as a trap, it's a pretty bad one. It still allows a lot of jumps to the padding to continue and execute valuable code.
If the instructions are branching instead of trapping (as explained in the article) then it would be exploitable as a ROP gadget and it would be a bug.
[1] For those who don't realize: author is on a Cortex-M processor per the ISA Ref they cite. These devices support *only* thumb instructions. Although as of thumb2, the encoding is now variable-length and there are lots of not-at-all-orthogonal-with-big-ARM 32 bit variants too. It's... not really the same architecture at all, to be honest.
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.
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...
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.
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.
> [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.
Another argument is comments-as-noise, as I would call it. The more "unnecessary" comments you write, the more core developers (who write and read most of the code), will learn to ignore comments. Then, critical comments like "Be careful not to call this when XYZ isn't initialized yet, unless you don't mind ABC happening" are ignored, and ta-da! comments are now useless.
Commit messages are attached to specific changes. If I want to know why a line of code is the way it is, I can git blame it, and see which commit is to blame, together with issue numbers, authors, maybe reviewers, context, history, etc.
Should there be a comment briefly explaining this patch? Probably. But the commit message should add the other context.
If people already have the habit of ignoring comments that are right there in the code, I am not sure they would spend the extra effort to go after commit history. Also, some commits might have originated from private repositories where commit history is not accessible, and the most context we get out of "git blame" might be "code was imported on this date".
Sometimes it’s because the later developer doesn’t think the comment needs to be fixed - maybe they tried to fix a bug in John Doe’s approach, accidentally introduced a new bug, but thought they didn’t touch the clever algorithm.
Sometimes it’s because the comment isn’t proximate to the code it refers to. For example, in the “XYZ initializer” case, maybe XYZ is changed down the line to remove the ABC behaviour, but the comment stays because it is attached to some faraway usage of XYZ.
Notes in commit messages don’t fix either of these problems, obviously. But, on the other hand, they obviously refer to a specific point in time, unlike comments, which makes it easier to figure out if the notes are still relevant or not.
That the code gets changed in the future such that the comment is a lie[1]. This happens with shocking regularity. Comments sound like a great idea until you deal with them in a long term maintenance situation.
As a corrolary, they also increase the cost of maintenance because if you end up doing refactoring that makes the comments a lie, it's never acceptable in review to just remove them. People expect you to write the same kind of treatise that the original author did. And original authors are horrifyingly verbose. All those doxygen crumbs you're leaving only act to confuse and irritate the pour souls coming after you.
Code is code. It should explain itself. If it does not, comments should do the absolute minimum needed (c.f. citing the relevant section in the ISA reference by number in this case) to rectify that.
[1] c.f. Guy Steele: https://softwarequotes.com/quote/don-t-get-suckered-in-by-th...
> [W]e find this in ARM.cpp:
> trapInstr = {0xd4, 0xd4, 0xd4, 0xd4};
The only thing left to explain is that the trap instruction is used as padding, but you can’t tell from here if that’s obvious or not. Opening the actual code[1], we see that the occurrences of trapInstr are all along the lines of
> void ARM::writePlt( /* ... */ ) {
> /* ... */
> memcpy(buf + 12, trapInstr.data(), 4); // Pad to 16-byte boundary
which isn’t the absolute best, but seems clear enough (if of course you know what a PLT is, which you should if you’re writing a linker).
I do think this merits an explanation that we’re using (what’s intended to be) a trap because the traditional option of using a nop makes ASLR less effective. But then the commit message you’re quoting doesn’t mention that either.
[1] https://github.com/llvm/llvm-project/blob/b20c291baec94ba370...
In fact, as a former code auditor I can say that comments at times make bug finding harder -- they frame you up a certain way. I definitely preferred to audit without comments.
Anyway, there are definitely valid reasons. I think the commit log or dev notes files are generally preferable, especially when combined with good naming.
Probably more of a "stick it in the toolbox for automatic use" rather than building an exploit around it type of situation however.
(Unless someone stays up all night to find the bugs....)