Why is the x86 undefined instruction called ud2? Why 2? - The Old New Thing
Skip to main content
Dev Blogs
AI
All .NET posts
.NET MAUI ASP.NET Core Blazor Entity Framework
C++ C# F# TypeScript
NuGet Servicing .NET Blog in Chinese
Microsoft for Developers Agent Framework Develop from the cloud Xcode ISE Developer TypeScript PowerShell Python Java Java Blog in Chinese Go Microsoft Edge Dev Microsoft 365 Developer Microsoft Entra Identity Developer Microsoft Entra PowerShell
Visual Studio Visual Studio Code Aspire
All things Azure Azure SDK Azure VM Runtime Team Microsoft Azure Azure Cosmos DB Azure DocumentDB Azure Data Studio Azure SQL DevOps DirectX Microsoft Foundry Power Platform
OData Unified Data Model (IDEAs)
Windows Command Line #ifdef Windows Inside MSIX MIDI and music React Native The Old New Thing Windows Developer
Dev BlogsThe Old New ThingWhy is the x86 undefined instruction called ud2? Why 2?
September 10th, 2026
4 reactions
Why is the x86 undefined instruction called ud2? Why 2?
Raymond Chen
Show more
If you look at x86 compiler output (or if, like me, you’re looking at a crash caused by some software that tried to detour an API), you may see an instruction ud2. What’s up with that? The ud2 instruction is an architecturally undefined instruction, guaranteed to raise an “invalid opcode” exception. Some compilers generate it to mark “unreachable” code, so that if execution somehow manages to reach it, you get a crash rather than executing random instructions. For example, if a function marked [[noreturn]] somehow returns, the compiler will put a ud2 after the call so that the program crashes instead of falling through to the next function. Anyway, why is this instruction called ud2 instead of just ud? Was there a ud1? What was so wrong about ud1 that we had to make a ud2? I think I can reconstruct what happened. Originally, there was no architecturally undefined instruction on x86. So people who wanted to force an invalid opcode exception went looking for some byte sequence that reliably raised the invalid opcode exception when executed. Somebody found that the 0F FF sequence led to an invalid opcode exception. Though, for whatever reason, the instruction internally decoded as if it took two parameters, a register destination and a register-or-memory source. The parameters aren’t actually used because the invalid opcode exception gets raised before anything else can happen. Meanwhile, somebody else found that the 0F B9 sequence also had the same properties. So you now had two factions, the 0F FF believers and the 0F B9 adherents. There really wasn’t much of a battle between them, because both techniques seemed to work, and it’s not like one was coming at the detriment of the other. Intel then worked on their next processor, and maybe they made some changes that resulted in 0F FF no longer raising an invalid opcode exception. Maybe they tried introducing a new instruction that uses 0F FF. Or maybe it was still undefined but just performed some random operation instead of raising the invalid opcode instruction. And when they started running software on their new processor, they found that some programs stopped working, and after laborious investigation, they discovered that the programs were relying on 0F FF being an invalid opcode. In other words, they ran into Hyrum’s Law: With a sufficient number of users, all observable behaviors will be depended upon by somebody. Obligatory XKCD. A similar discovery was made with 0F B9. Now that they realized that people wanted a reliable way to trigger an invalid opcode exception, the folks at Intel decided to make it official, and they created an actual supported permanently-invalid instruction and called it ud2. 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. One advantage of ud2 is that it is a two-byte instruction with no parameters, so you don’t have to deal with the random decoded-but-unused source and destinations. Bonus chatter: But why do we care about the unused parameters to ud0 and ud1? Can’t we just say that ud0 and ud1 are also two-byte invalid opcodes? I mean, sure, there’s a third byte, or possibly more if the memory operand has an offset or a scaled index, but the processor doesn’t use it. It matters, because even though the processor doesn’t use it, it still decodes it. And if the decoding of the instruction crosses into a not-present page, you don’t get an invalid opcode exception at all. You get an access violation. Bonus bonus chatter: Except that some older processors raised the invalid opcode instruction as soon as they decoded the 0F FF without checking whether the rest of the instruction decoded properly. 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. Better to stick with ud2. Its behavior is consistent and architecturally guaranteed.
4
7
0
Share on Facebook
Share on X
Share on Linkedin
CategoryOld New ThingTopicsOther Share
Author Raymond ChenRaymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.
7 comments Join the discussion. Leave a commentCancel replySign in
Code of Conduct
Sort by :
Newest
Newest Popular Oldest
John Musbach
September 12, 2026
0
Copy link to comment by John Musbach
Collapse comment by John Musbach
I think we really shouldn’t need to worry about assembly anymore as compilers are quite good at doing their thing these days. Of course if you want to be on a Microsoft core team that’s a different story but I wonder how many genz are assembly knowledgeable at all
Log in to Vote or Reply
Csaba Varga
7 minutes ago 0
Copy link to comment by Csaba Varga
Collapse comment by Csaba Varga
Worry about it? No, you don't need to do that. But you need to know the basics if you ever need to write performance-critical code. Even today's amazing compilers won't necessarily fix a badly chosen algorithm that contains unpredictable jumps or has poor cache utilization. They won't replace a linked list with a vector even if the latter would be faster and more memory-efficient. If you have at least a general idea about what your source code turns into, you can help the compiler make the best out of it. Most of us won't need to hand-write assembly code anymore, I'll...Read moreWorry about it? No, you don’t need to do that. But you need to know the basics if you ever need to write performance-critical code. Even today’s amazing compilers won’t necessarily fix a badly chosen algorithm that contains unpredictable jumps or has poor cache utilization. They won’t replace a linked list with a vector even if the latter would be faster and more memory-efficient. If you have at least a general idea about what your source code turns into, you can help the compiler make the best out of it. Most of us won’t need to hand-write assembly code anymore, I’ll grant you that. Being able to read it could be helpful in rare cases where you don’t have the source code available. It can also help you appreciate what your compiler does for you, when you play around on godbolt.org 😀 Read less
Log in to Vote or Reply
Shawn Van Ness
17 hours ago 0
Copy link to comment by Shawn Van Ness
Collapse comment by Shawn Van Ness
I’ve never really heard of UD2 and friends.. I’ve always seen and used 0xCC (INT 3) when I want my program to crash.. and break in the debugger, if present. How does the handling of UD2 compare to 0xCC?
Log in to Vote or Reply
liamgraham
September 12, 2026
0
Copy link to comment by liamgraham
Collapse comment by liamgraham
CC (INT 3) is a trap. Mostly used to signal precondition/contract breach UD2 is more a hard stop because “executing random instructions”, as Raymond put it, can cause a lot of bugs.
Log in to Vote or Reply
Henry Skoglund
2 days ago 0
Copy link to comment by Henry Skoglund
Collapse comment by Henry Skoglund
Reminds me of programming the 6502: if you had a bug and the processor jumped into unknown territory, if the unused RAM had been zeroed out, you might catch those bugs easier. because the 0x00 opcode is BRK (force break). For x86, I guess that UD2 instruction came pretty late to the party so all “handy” opcodes like 0x00 of 0xFF were already taken 🙁
Log in to Vote or Reply
Darren Izzard
1 day ago 0
Copy link to comment by Darren Izzard
Collapse comment by Darren Izzard
On the Acorn machines, the BRK vector caught such instructions and read the following bytes as an error message. (Well, technically, an error code, then the ASCII message.) This allowed language and service ROMs to report errors very simply, and also allowed errors to be caught from any ROM or application via a single interface. Thus, BASIC's ON ERROR could catch errors not just from BASIC, but also the file system, the network, some ROM that supported some new hardware you plugged in, whatever. And so could your wordprocessor, FORTH program, game, debugger software, etc. Of course, should your code hit...Read moreOn the Acorn machines, the BRK vector caught such instructions and read the following bytes as an error message. (Well, technically, an error code, then the ASCII message.) This allowed language and service ROMs to report errors very simply, and also allowed errors to be caught from any ROM or application via a single interface. Thus, BASIC’s ON ERROR could catch errors not just from BASIC, but also the file system, the network, some ROM that supported some new hardware you plugged in, whatever. And so could your wordprocessor, FORTH program, game, debugger software, etc. Of course, should your code hit a random 0x00 in memory, the screen would fill with garbage until the next 0x00 was hit. Since the character output routine managed all graphics and text output, it could also clear the screen, change screen mode, redefine characters, activate the printer, even suppress all output if the right bytes were read. So you could say, while making error handling much easier, it also made it much more confusing. Read less
Log in to Vote or Reply
Joshua Hudson
2 days ago 0
Copy link to comment by Joshua Hudson
Collapse comment by Joshua Hudson
0x00 is taken. it’s an ADD instruction all the way back on the 8086 0xFF is a prefix byte of a two byte instruction. However 0xFF 0xFF is not a valid instruction even today (as least as far as my disassembler is concerned).
Log in to Vote or Reply
Read next
September 1, 2026 Microspeak: Funded / unfunded
Raymond Chen
July 14, 2026 Microspeak: Double-click and drill down
Raymond Chen
Stay informed Get notified when new posts are published.
Email * Country/Region * Select...United StatesAfghanistanÅland IslandsAlbaniaAlgeriaAmerican SamoaAndorraAngolaAnguillaAntarcticaAntigua and BarbudaArgentinaArmeniaArubaAustraliaAustriaAzerbaijanBahamasBahrainBangladeshBarbadosBelarusBelgiumBelizeBeninBermudaBhutanBoliviaBonaireBosnia and HerzegovinaBotswanaBouvet IslandBrazilBritish Indian Ocean TerritoryBritish Virgin IslandsBruneiBulgariaBurkina FasoBurundiCabo VerdeCambodiaCameroonCanadaCayman IslandsCentral African RepublicChadChileChinaChristmas IslandCocos (Keeling) IslandsColombiaComorosCongoCongo (DRC)Cook IslandsCosta RicaCôte dIvoireCroatiaCuraçaoCyprusCzechiaDenmarkDjiboutiDominicaDominican RepublicEcuadorEgyptEl SalvadorEquatorial GuineaEritreaEstoniaEswatiniEthiopiaFalkland IslandsFaroe IslandsFijiFinlandFranceFrench GuianaFrench PolynesiaFrench Southern TerritoriesGabonGambiaGeorgiaGermanyGhanaGibraltarGreeceGreenlandGrenadaGuadeloupeGuamGuatemalaGuernseyGuineaGuinea-BissauGuyanaHaitiHeard Island and McDonald IslandsHondurasHong Kong SARHungaryIcelandIndiaIndonesiaIraqIrelandIsle of ManIsraelItalyJamaicaJan MayenJapanJerseyJordanKazakhstanKenyaKiribatiKoreaKosovoKuwaitKyrgyzstanLaosLatviaLebanonLesothoLiberiaLibyaLiechtensteinLithuaniaLuxembourgMacau SARMadagascarMalawiMalaysiaMaldivesMaliMaltaMarshall IslandsMartiniqueMauritaniaMauritiusMayotteMexicoMicronesiaMoldovaMonacoMongoliaMontenegroMontserratMoroccoMozambiqueMyanmarNamibiaNauruNepalNetherlandsNew CaledoniaNew ZealandNicaraguaNigerNigeriaNiueNorfolk IslandNorth MacedoniaNorthern Mariana IslandsNorwayOmanPakistanPalauPalestinian AuthorityPanamaPapua New GuineaParaguayPeruPhilippinesPitcairn IslandsPolandPortugalPuerto RicoQatarRéunionRomaniaRwandaSabaSaint BarthélemySaint Kitts and NevisSaint LuciaSaint MartinSaint Pierre and MiquelonSaint Vincent and the GrenadinesSamoaSan MarinoSão Tomé and PríncipeSaudi ArabiaSenegalSerbiaSeychellesSierra LeoneSingaporeSint EustatiusSint MaartenSlovakiaSloveniaSolomon IslandsSomaliaSouth AfricaSouth Georgia and South Sandwich IslandsSouth SudanSpainSri LankaSt HelenaAscensionTristan da CunhaSurinameSvalbardSwedenSwitzerlandTaiwanTajikistanTanzaniaThailandTimor-LesteTogoTokelauTongaTrinidad and TobagoTunisiaTurkeyTurkmenistanTurks and Caicos IslandsTuvaluU.S. Outlying IslandsU.S. Virgin IslandsUgandaUkraineUnited Arab EmiratesUnited KingdomUruguayUzbekistanVanuatuVatican CityVenezuelaVietnamWallis and FutunaYemenZambiaZimbabwe
I would like to receive the The Old New Thing Newsletter. Privacy Statement.
Subscribe
Follow this blog
Are you sure you wish to delete this comment?
OK Cancel
Sign in
Theme
Insert/edit link
Close
Enter the destination URL
URL
Link Text
Open link in a new tab
Or link to existing content
Search
No search term specified. Showing recent items.
Search or use up and down arrow keys to select an item.
Cancel
Code Block
×
Paste your code snippet
Ok Cancel
Surface Pro Surface Laptop Surface Laptop Ultra Surface RTX Spark Dev Box Copilot for organizations Copilot for personal use Explore Microsoft products Windows 11 apps
Account profile Download Center Microsoft Store support Returns Order tracking Certified Refurbished Microsoft Store Promise Flexible Payments
Microsoft in education Devices for education Microsoft Teams for Education Microsoft 365 Education How to buy for your school Educator training and development Deals for students and parents AI for education
Microsoft AI Microsoft Security Dynamics 365 Microsoft 365 Microsoft Power Platform Microsoft Teams Microsoft 365 Copilot Small Business
Azure Microsoft Developer Microsoft Learn Support for AI marketplace apps Microsoft Tech Community Microsoft Marketplace Software companies Visual Studio
Careers About Microsoft Company news Privacy at Microsoft Investors Diversity and inclusion Accessibility Sustainability
Your Privacy Choices Opt-Out Icon
Your Privacy Choices
Your Privacy Choices Opt-Out Icon
Your Privacy Choices
Consumer Health Privacy
Sitemap Contact Microsoft Privacy Manage cookies Terms of use Trademarks Safety & eco Recycling About our ads |
The x86 undefined instruction, ud2, arises from an evolution concerning how software can reliably trigger exceptions and the subsequent need for architectural consistency in instruction handling. Originally, there was no designated architecturally undefined instruction on x86. Developers seeking to force an invalid opcode exception searched for specific byte sequences that would reliably generate this error. This search led to the discovery of sequences like 0F FF and 0F B9, both of which, despite seemingly taking two parameters, functioned as invalid opcodes because the exception was raised before the parameters could be processed.
The naming convention for the undefined operations was established retroactively by Intel as better practice emerged. The 0F FF variant was eventually named ud0, and the 0F B9 variant was named ud1, leaving ud2 as the designated and recommended undefined opcode. This choice reflects a systematic approach to defining these states. A key advantage of ud2 is its simplicity: it is a two-byte instruction containing no parameters, thereby avoiding the complexity introduced by ud0 and ud1, which still contained unused source and destination parameters that the processor might decode. This complexity is problematic because if the instruction decoding crosses into a not-present page, the system might raise an access violation instead of the desired invalid opcode exception, depending on the specific processor implementation and historical context.
The rationale for preferring ud2 is based on ensuring consistent and architecturally guaranteed behavior. While the unused parameters in ud0 and ud1 allowed for ambiguous outcomes based on memory state, ud2 provides a clear, uncontroversial state. This distinction is crucial because the behavior of these instructions must be predictable, especially in systems where the instruction decoding process can interact with memory management, making the consistent behavior of ud2 superior to the variants derived from other sequences. Consequently, ud2 is the standard used by implementers, providing a reliable mechanism for handling unintended execution paths, particularly when compilers generate it to denote unreachable code, such as following a function marked as not returning. |