4 ms·
Why is the x86 undefined instruction called ud2? Why 2?
- zero-sharp 25d ago[flagged]
- Sharlin 25d agoDid you read the friendly article?
- Neywiny 25d agoI'm not much of an x86 person but on other architectures you can raise software interrupts/exceptions. Does x86 not have this or did those facilities not cover enough use cases?
- 349ru3h4f03 25d agox86 has... INT Ib INT1 INT3 INTO BOUND
- userbinator 23d agoNote that INT1 was originally called ICEBP before Intel finally documented it publicly (very recently).
- rep_lodsb 22d agoYes, and it's not quite the same as a normal "INT 01h". It causes a debug exception, which may enter ICE mode if it is enabled (undocumented bit in DR7, or PMCR on Pentium), otherwise it invokes interrupt 1, but without checking the privilege level on the IDT entry, or the interrupt redirection bitmap in V86 mode. https://www.rcollins.org/secrets/opcodes/ICEBP.html https://www.rcollins.org/secrets/opcodes/ICEBP.html IIRC, older versions of the Linux kernel had a security bug because they didn't expect this to happen. ICE mode was sort of a precursor to SMM, but both also coexisted for a time with slightly different behaviour. It was introduced in the 286, where instead of ICEBP there was "STOREALL" (opcode 0F04). F1 on that processor was a prefix instead, with the same function as UMOV on 386+. If you use them together - something Intel probably didn't intend - you can dump the internal CPU state to memory on a regular non-bond-out chip. https://rep-lodsb.mataroa.blog/blog/intel-286-secrets-ice-mode-and-f1-0f-04/ https://rep-lodsb.mataroa.blog/blog/intel-286-secrets-ice-mo...
- js8 25d agoIt's basically a convention. The alternative is to raise interrupts of course, but that might be application specific, or use other invalid instructions than the designated one, but they might work differently on other processor types.
- sweetjuly 25d agoThere's a bit of convention and practicality The only thing you really need is that your "fatal error" instruction and "syscall" instruction can be reasonably discriminated without needing to set registers at the call site. Needing to set register to identify a fatal error is not great for code size, especially in languages that generate a lot of them (memory safe languages, mostly). Though, yes, convention does play a role. On ARMv8 you get both SVC <imm> and BRK <imm>. SVC and BRK raise different exception codes (which satisfies the "easy to distinguish requirement) but in principle you could just use BRK with a well-known immediate and eliminate the need for SVC since BRK's immediate is reported in the exception status register. And, anyways, if you have an SVC instruction and a BRK instruction, you may as well use the SVC instruction for syscalls since it's right there.
- pm215 25d agoARMv8 also gives you a a UDF imm, for a guaranteed undefined insn with an immediate payload. The reason to want a true UDF imm with an immediate comes down to it being pretty solidly guaranteed that it's going to turn into your language/OS equivalent of a SIGILL insn. In theory an OS could by convention allocate some subset of BRK space for arbitrary userspace purposes, but in practice none did, so trying to use BRK gets you dumped into a debugger, or doesn't have consistent behaviour. It's nice for userspace to have something that doesn't need active OS support.
- mitxela 25d agoX86 has int3 for this purpose. Interrupt 3 is designated as debug breakpoint and a single-byte instruction raises it (or you can use int 3, two bytes)
- omoikane 25d agoMaybe because if the code wants to call the invalid opcode interrupt handler (INT6), it needs extra code to populate the flags and registers expected by that handler, whereas actually triggering an invalid opcode exception will get all those parameters populated automatically.
- thayne 25d agoAnd this is code that will (hopefully) almost never run, so you don't want it to take up much space in you your program, and especially cache lines.
- mitxela 25d agoIt doesn't have to be the invalid opcode handler that you call. Prior to SYSCALL they picked one to be the system call handler. Windows 95 used illegal instructions because it was benchmarked to be the fastest fault.
- adrian_b 25d agoAlready since Intel 8086, x86 has the instruction "INT vector_number", whose purpose is to allow software to invoke directly any of the many kinds of exception handlers or hardware interrupt handlers that are specified by the ISA or implemented by the hardware designer, which are normally invoked when various conditions arise, as determined by software execution or by I/O events. So you can invoke the handler of the invalid instruction exception with the INT instruction, but as another poster mentioned, the INT instruction alone is not enough for this, but you need to setup the stack in such a way so that it will contain the information expected by the exception handler, which requires multiple instructions. This kind of invocation may be acceptable when you write a test program for the invalid instruction exception handler, but it is not acceptable when you want to initialize some guard memory with values that will trigger the exception, to signal that your program has attempted to execute instructions from an area that should not be executable. Setting a memory area as non-executable through the access rights has only page granularity, so it is not useful when a page must contain both some executable code and some non-executable data. If Intel had not defined an official opcode that is guaranteed to remain unused forever, to be able to reliably trigger the invalid instruction exception, the workaround would have been for the user to reserve one of the 256 interrupt vectors for the invocation through software of the invalid instruction exception. For that vector, a simple handler could have been used, which would have setup the stack in the right way, before jumping to the invalid instruction handler. But this workaround would have had the disadvantage that any chosen interrupt vector could have conflicted with some choice made by the hardware designers of some computers, so it would have been required for it to be a configurable parameter of the operating system kernel, and also of the user applications that need it, like compilers, unless it would have been standardized by some organization. Just reserving an opcode at Intel and AMD was simpler, with no other requirements for standardization or changes in the existing software.
- rep_lodsb 25d ago#UD has the same stack frame as a software interrupt, there's no error code pushed. But most likely, executing INT 06 from ring 3 will generate a protection fault instead, since the gate descriptor would be set up to not be reachable from that privilege level. (exceptions that do push an error code couldn't be emulated at all using INT, since the error code is the last thing pushed by the CPU, after flags and return address)
- fweimer 25d agoI expect that UD2 stops instruction fetching (beyond the current block) and conversion to µops. A software interrupt or supervisor call should probably do neither because most of the time, these instructions eventually return and continue executing the next instruction.
- rep_lodsb 25d agoAn interrupt or SYSCALL instruction could do anything, which includes remapping or overwriting the memory location it returns to. So no, these instructions can't be prefetched in any case.
- fsckboy 25d ago>you can raise software interrupts/exceptions exception handling requires that some unrelated region of memory is initialized and intact and ready to do the right thing, whatever that is, and that region is outside the scope of your control, it belongs to the operating system or the the embedded ROM, and it may not have been laid out to take care of your case. assembly/machine code is operating at a lower layer: "I don't know what larger thing I'm a part of, but I know I need to stop."
- rep_lodsb 25d agoIf you don't know anything about the environment the code runs in, you can't rely on what action the undefined opcode handler will take either. (same for any other exception) Some operating systems used them for syscalls.
- fsckboy 24d ago1. you can use the opcode, it's guaranteed. if it does something else, that's not on you. 2. you might know everything about the environments and that's how you know that different environments use different interrupt tables, but you want a code snippet that will not rely on that, so you use this guaranteed opcode.
- rep_lodsb 22d agoUsually, you do know something about the environment that your code runs in. But in the case that you don't, causing an exception (whether "undefined opcode" or anything else) can't be guaranteed to terminate the process, because some operating systems actually did use it for other purposes. The assembly language equivalent of nasal demons :) https://devblogs.microsoft.com/oldnewthing/20041215-00/?p=37003 https://devblogs.microsoft.com/oldnewthing/20041215-00/?p=37...
- asveikau 25d agoIt's common to use int3 for some of the scenarios mentioned in the article. (Like non-reachable code) This instruction is often used to trigger a break in the debugger.
- 349ru3h4f03 25d agoNowadays UD0 UD1 UD2 are in the SDM and APM. We also got UDB (D6), the one-byte variant that arrived with x86-64 for 64-bit mode. And we have always had UDW (FF FF), aka group #5 (1st FF) with a modrm byte of mod=11b r/m=111b (/7) reg=111b (2nd FF) -- that one matters for memory with all bits set to 1, or for buses terminated to all 1 when no device claims an access.
- Bluestein 25d ago[flagged]
- rramadass 25d agoCouldn't find UDW in the intel docs - https://intel.github.io/SDM/sdm.html https://intel.github.io/SDM/sdm.html But found it in wikipedia which is well structured and has detailed notes/comments on all instructions - https://en.wikipedia.org/wiki/List_of_x86_instructions https://en.wikipedia.org/wiki/List_of_x86_instructions There is also UD2A and UD2B which just seem to be synonyms for UD2 and UD1 respectively? Google tells me that they are legacy tool specific mnemonics used in older GNU GCC/Binutils.
- userbinator 25d agoI've always remembered FF FF as "??? DI", because that's what DEBUG's disassembler would render it as.
- Bluestein 24d agoSorry y'all. In a thread about encodings whose only spec is that they mean nothing (UD0/UD1/UD2, FF /7), "349ru3h4f03" is the most thematically correct username possible, it really does "check out" dang it. It even looks like hex right up until it #UDs on the r, u, and h. Invalid encoding; faults on interpretation. Tough crowd tonite.-
- dataflow 25d agoIs this just his speculation? Or is there evidence for it?
- dspillett 25d agoWhat the instruction does is well documented, and the history of other invalid instructions being used for the same purpose in the past I'm guessing there is also well known, though a quick search doesn't turn up any official Intel documentation on the matter. Given who this is and the overall quality of his output over the years, I'm willing to trust it isn't pure guesswork - and anyway, I'd trust his guesswork over many other people's absolute facts.
- alt227 25d agoThis is a first hand account of somebody very knowledgeable and respected in the industry at the time of the events. I would say he is the evidence.
- userbinator 25d agoHe has been shown to be wrong in several cases, with the evidence presented from others, so I would say he's knowledgeable but not 100%.
- qbane 25d agoIt's like why the first (hard) drive letter is C.
- cyanydeez 25d agoA: drive is 3.5; B: drive is 5.25; C: drive is hard disk
- alightsoul 25d agoYeah it's a relic from when computers booted off floppy and hard disks were rare and expensive
- compiler-guy 25d agoEven further back into time, before 3.5" disks, both A: and B: were 5.25" disks. And, although my memory is hazy, 3.5's were commonly slotted into B: at first, because no one had 3.5" boot disks until the drives became somewhat common.
- gumby 25d agoYou are forgetting 8” floppies. IBM used only the soft sector ones (one hole punched close to the spindle to mark sector 0) but there were also hard sector ones (a ring of 32 holes close to the spindle hole). There was a lot of experimentation back in those days. The Wikipedia’s page on floppy disks is surprisingly long!
- compiler-guy 25d agoTrust me, I will never forget 8" floppies. But in the context of drive lettering, although CP/M supported 8" floppies, MS-DOS never did. MS-DOS adopted the naming scheme, but not the boot style.
- kjs3 25d agoMS-DOS 1.25 supported 8" disks. I think 2.0 still did.
- ordu 25d ago> It’s called ud2 because the 0F FF variant was retroactively named ud0, and the 0F B9 variant was retroactively named ud1, leaving ud2 as the recommended undefined opcode. It was a surprise for me as a reader. When I came to this sentence I assumed that 0f ff would become #1 and 0fb9 -- #2. But no, Intel counts from zero, so there is a third ud.
- hacker_homie 25d agoThus finally the 0F FF believers were rewarded by being give the honor of op code UD0 making it the one and true original invalid opcode permanently disgracing the 0F B9 adherents with the shame of UD1.
- FartyMcFarter 25d agoThat will show'em.
- randomblock1 25d ago0 undoubtedly comes first, but we all know 1 == true...
- gtirloni 25d agoI recently had to debug builds that failed randomly and a ud2 from V8 was there waiting for me.
- boramalper 25d ago> Better to stick with ud2. Its behavior is consistent and architecturally guaranteed. Ah, finally an undefined instruction whose behaviour is consistent and architecturally guaranteed!
- randomblock1 25d agoSchrodinger's instruction: simultaneously defined and undefined
- tombert 25d agoTangential, but I almost never read assembly [1], but I do read Java bytecode pretty frequently, primarily because doing that can sometimes be a good substitute for benchmarking [2], which I do not enjoy. The thing that never seems to stop tripping me up is the different “dup” codes that compile. At some point I really need to properly learn the difference between dup_x2 and dup2_x1 and dup2_x2. [1] not out of like an ethical objection, just my career has involved almost no reverse engineering and it’s also never been a path I have been super interested in to pursue on my own. [2] e.g. if two competing chunks of code emit the same bytecode, you don’t need to pull out JMH. My go to example for this is using if statements vs switches, which will usually emit the same code so performance arguments are moot.
- mitxela 25d agoJava has bytecode instructions for switches. You're saying the compiler doesn't use them?
- tombert 24d agoI'm saying that if statements that can be converted to switches usually are converted to switches. Something like: if (x == 5) { doSomething() } else if (x == 6) { doSomethingElse() } This can be trivially converted into a switch and generally is. Run a release build and check me if you'd like.
- arkj 25d agoHyrum’s Law reaching all the way down to the instruction decoder. And for those who care the ud2 opcode is 0F 0B
- userbinator 25d agoThough, for whatever reason, the instruction internally decoded as if it took two parameters, a register destination and a register-or-memory source. Because the rest of the 0F Fx line also has a ModRM. Ditto for 0F Bx. So if your 0F FF is at the end of a page, and the next page is not present, you sometimes got an invalid opcode exception and you sometimes got an access violation. This reminds me of some related information on instruction length and decoding of "undefined" instructions I have filed away from a long time ago; sadly this stuff is disappearing from the Internet, but both the Archive and I still remember: https://web.archive.org/web/20160721202526/http://pferrie.host22.com/misc/lowlevel2.htm https://web.archive.org/web/20160721202526/http://pferrie.ho... Such information is very important for emulation accuracy and some security contexts.
- JdeBP 23d agoIt's sad that this is probably going to become canon, now, Raymon Chen being as influential as xe is, when what actually happened was that in 1998 H. Peter Anvin saw UD2 in Intel's doco, and that there was a second opcode that was just described by Intel as merely undefined, so gave it the mnemonic UD1 in the 0.98p3-hpa version of NASM because 'calling it UD1 seemed to make sense'. * https://groups.google.com/g/comp.lang.asm.x86/c/ErG5TJEDjiw/m/DZ4uuYzjjZ0J https://groups.google.com/g/comp.lang.asm.x86/c/ErG5TJEDjiw/... * https://github.com/netwide-assembler/nasm/blob/nasm-0.98.x/CHANGES#L827 https://github.com/netwide-assembler/nasm/blob/nasm-0.98.x/C...