De ud2-instructie is een architectonisch ongedefinieerde x86-instructie die gegarandeerd een 'invalid opcode'-exceptie veroorzaakt. Compilers gebruiken deze instructie om onbereikbare code te markeren, waardoor een programma direct crasht in plaats van onvoorspelbaar gedrag te vertonen.
Historische context: Voor de introductie van ud2 gebruikten ontwikkelaars byte-reeksen zoals 0F FF (later ud0) en 0F B9 (later ud1) om crashes te forceren. Vanwege Hyrum's Law — het fenomeen waarbij gebruikers gaan vertrouwen op elk waarneembaar gedrag van een systeem — moest Intel een officiële standaard (ud2) introduceren toen wijzigingen in nieuwe processors deze oude methoden onbetrouwbaar maakten.
Technisch voordeel: In tegenstelling tot ud0 en ud1 heeft ud2 geen parameters en bestaat het uit precies twee bytes. Dit voorkomt ambiguïteit tijdens het decoderen; bij de oudere instructies kon een 'access violation' optreden in plaats van een 'invalid opcode' als de instructie overging in een niet-aanwezige geheugenpagina.
Waarom wordt de ongedefinieerde x86-instructie ud2 genoemd? Waarom 2?
Als je kijkt naar de output van een x86-compiler (of als je, net als ik, naar een crash kijkt die is veroorzaakt door software die een API probeerde te omzeilen), kom je mogelijk de instructie ud2 tegen. Hoe zit dat precies?
Wat is de ud2-instructie?
De ud2-instructie is een architectonisch ongedefinieerde instructie die gegarandeerd een "invalid opcode"-exceptie (ongeldige opcode) veroorzaakt. Sommige compilers genereren deze instructie om "onbereikbare" code te markeren. Op die manier wordt gegarandeerd dat het programma crasht als de executie op een of andere manier toch dit punt bereikt, in plaats van dat er willekeurige instructies worden uitgevoerd.
Een voorbeeld hiervan is een functie die is gemarkeerd met [[noreturn]]. Mocht deze functie onverwacht toch terugkeren, dan plaatst de compiler een ud2 na de aanroep, zodat het programma crasht in plaats van door te lopen naar de volgende functie.
De oorsprong van de naam
Waarom is deze instructie ud2 genoemd in plaats van simpelweg ud? Was er een ud1? En wat was er mis met ud1 waardoor er een ud2 nodig was?
Ik denk dat ik kan reconstrueren wat er is gebeurd.
Oorspronkelijk was er op x86 geen architectonisch ongedefinieerde instructie. Mensen die een "invalid opcode"-exceptie wilden forceren, zochten daarom naar een byte-reeks die betrouwbaar deze exceptie opriep bij uitvoering.
Er werd ontdekt dat de reeks 0F FF leidde tot een "invalid opcode"-exceptie. Om welke reden dan ook werd de instructie intern gedecodeerd alsof deze twee parameters nam: een register-bestemming en een register- of geheugenbron. Deze parameters worden echter niet daadwerkelijk gebruikt, omdat de exceptie wordt gegenereerd voordat er iets anders kan gebeuren.
Tegelijkertijd ontdekte iemand anders dat de reeks 0F B9 dezelfde eigenschappen had. Hierdoor ontstonden er twee kampen: de aanhangers van 0F FF en de aanhangers van 0F B9. Er was geen echte strijd tussen de twee, aangezien beide technieken werkten en de een de ander niet in de weg zat.
Hyrum's Law en de officiële standaard
Toen Intel werkte aan hun volgende processor, werden er mogelijk wijzigingen doorgevoerd waardoor 0F FF niet langer een "invalid opcode"-exceptie veroorzaakte. Misschien probeerden ze een nieuwe instructie te introduceren die 0F FF gebruikte, of misschien was de instructie nog steeds ongedefinieerd, maar voerde deze een willekeurige operatie uit in plaats van een exceptie te genereren.
Wanneer ze software op deze nieuwe processor draaiden, merkten ze dat sommige programma's stopten met werken. Na een moeizaam onderzoek ontdekten ze dat deze programma's vertrouwden op het feit dat 0F FF een ongeldige opcode was.
In feite liepen ze tegen Hyrum's Law aan: met een voldoende groot aantal gebruikers zal iedereen op alle waarneembare gedragingen gaan vertrouwen. (Zie ook XKCD).
Een soortgelijke ontdekking werd gedaan bij 0F B9.
Omdat Intel nu besefte dat ontwikkelaars een betrouwbare manier nodig hadden om een "invalid opcode"-exceptie te triggeren, besloten ze dit officieel te maken. Ze creëerden een daadwerkelijk ondersteunde, permanent ongeldige instructie en noemden deze ud2.
De naam ud2 is gekozen omdat de 0F FF-variant met terugwerkende kracht werd benoemd tot ud0 en de 0F B9-variant tot ud1. Dit liet ud2 over als de aanbevolen ongedefinieerde opcode.
Technische voordelen van ud2
Een voordeel van ud2 is dat het een instructie van twee bytes is zonder parameters. Je hoeft dus niet om te gaan met willekeurige, gedecodeerde maar ongebruikte bronnen en bestemmingen.
Het probleem met de parameters van ud0 en ud1
Men zou kunnen vragen waarom die ongebruikte parameters van ud0 en ud1 überhaupt uitmaken. Kunnen we niet gewoon zeggen dat ud0 and ud1 ook ongeldige opcodes van twee bytes zijn? Er is immers een derde byte (of meer, als de geheugenoperand een offset of geschaalde index heeft), maar de processor gebruikt deze niet.
Dit is echter wel van belang, want hoewel de processor de parameters niet gebruikt, decodeert hij ze nog wel. Als de decodering van de instructie overgaat in een pagina die niet aanwezig is (not-present page), krijg je geen "invalid opcode"-exceptie, maar een access violation.
Sommige oudere processors genereerden de "invalid opcode"-instructie zodra ze 0F FF decodeerden, zonder te controleren of de rest van de instructie correct werd gedecodeerd. Als je 0F FF dus aan het einde van een pagina stond en de volgende pagina niet aanwezig was, kreeg je soms een "invalid opcode"-exceptie en soms een access violation.
Het is daarom beter om vast te houden aan ud2. Het gedrag is consistent en architectonisch gegarandeerd.
Waarom wordt de ongedefinieerde x86-instructie ud2 genoemd? Waarom 2?
Als je kijkt naar de output van een x86-compiler (of als je, net als ik, naar een crash kijkt die is veroorzaakt door software die een API probeerde te omzeilen), kom je mogelijk de instructie ud2 tegen. Hoe zit dat precies?
Wat is de ud2-instructie?
De ud2-instructie is een architectonisch ongedefinieerde instructie die gegarandeerd een "invalid opcode"-exceptie (ongeldige opcode) veroorzaakt. Sommige compilers genereren deze instructie om "onbereikbare" code te markeren. Op die manier wordt gegarandeerd dat het programma crasht als de executie op een of andere manier toch dit punt bereikt, in plaats van dat er willekeurige instructies worden uitgevoerd.
Een voorbeeld hiervan is een functie die is gemarkeerd met [[noreturn]]. Mocht deze functie onverwacht toch terugkeren, dan plaatst de compiler een ud2 na de aanroep, zodat het programma crasht in plaats van door te lopen naar de volgende functie.
De oorsprong van de naam
Waarom is deze instructie ud2 genoemd in plaats van simpelweg ud? Was er een ud1? En wat was er mis met ud1 waardoor er een ud2 nodig was?
Ik denk dat ik kan reconstrueren wat er is gebeurd.
Oorspronkelijk was er op x86 geen architectonisch ongedefinieerde instructie. Mensen die een "invalid opcode"-exceptie wilden forceren, zochten daarom naar een byte-reeks die betrouwbaar deze exceptie opriep bij uitvoering.
Er werd ontdekt dat de reeks 0F FF leidde tot een "invalid opcode"-exceptie. Om welke reden dan ook werd de instructie intern gedecodeerd alsof deze twee parameters nam: een register-bestemming en een register- of geheugenbron. Deze parameters worden echter niet daadwerkelijk gebruikt, omdat de exceptie wordt gegenereerd voordat er iets anders kan gebeuren.
Tegelijkertijd ontdekte iemand anders dat de reeks 0F B9 dezelfde eigenschappen had. Hierdoor ontstonden er twee kampen: de aanhangers van 0F FF en de aanhangers van 0F B9. Er was geen echte strijd tussen de twee, aangezien beide technieken werkten en de een de ander niet in de weg zat.
Hyrum's Law en de officiële standaard
Toen Intel werkte aan hun volgende processor, werden er mogelijk wijzigingen doorgevoerd waardoor 0F FF niet langer een "invalid opcode"-exceptie veroorzaakte. Misschien probeerden ze een nieuwe instructie te introduceren die 0F FF gebruikte, of misschien was de instructie nog steeds ongedefinieerd, maar voerde deze een willekeurige operatie uit in plaats van een exceptie te genereren.
Wanneer ze software op deze nieuwe processor draaiden, merkten ze dat sommige programma's stopten met werken. Na een moeizaam onderzoek ontdekten ze dat deze programma's vertrouwden op het feit dat 0F FF een ongeldige opcode was.
In feite liepen ze tegen Hyrum's Law aan: met een voldoende groot aantal gebruikers zal iedereen op alle waarneembare gedragingen gaan vertrouwen. (Zie ook XKCD).
Een soortgelijke ontdekking werd gedaan bij 0F B9.
Omdat Intel nu besefte dat ontwikkelaars een betrouwbare manier nodig hadden om een "invalid opcode"-exceptie te triggeren, besloten ze dit officieel te maken. Ze creëerden een daadwerkelijk ondersteunde, permanent ongeldige instructie en noemden deze ud2.
De naam ud2 is gekozen omdat de 0F FF-variant met terugwerkende kracht werd benoemd tot ud0 en de 0F B9-variant tot ud1. Dit liet ud2 over als de aanbevolen ongedefinieerde opcode.
Technische voordelen van ud2
Een voordeel van ud2 is dat het een instructie van twee bytes is zonder parameters. Je hoeft dus niet om te gaan met willekeurige, gedecodeerde maar ongebruikte bronnen en bestemmingen.
Het probleem met de parameters van ud0 en ud1
Men zou kunnen vragen waarom die ongebruikte parameters van ud0 en ud1 überhaupt uitmaken. Kunnen we niet gewoon zeggen dat ud0 and ud1 ook ongeldige opcodes van twee bytes zijn? Er is immers een derde byte (of meer, als de geheugenoperand een offset of geschaalde index heeft), maar de processor gebruikt deze niet.
Dit is echter wel van belang, want hoewel de processor de parameters niet gebruikt, decodeert hij ze nog wel. Als de decodering van de instructie overgaat in een pagina die niet aanwezig is (not-present page), krijg je geen "invalid opcode"-exceptie, maar een access violation.
Sommige oudere processors genereerden de "invalid opcode"-instructie zodra ze 0F FF decodeerden, zonder te controleren of de rest van de instructie correct werd gedecodeerd. Als je 0F FF dus aan het einde van een pagina stond en de volgende pagina niet aanwezig was, kreeg je soms een "invalid opcode"-exceptie en soms een access violation.
Het is daarom beter om vast te houden aan ud2. Het gedrag is consistent en architectonisch gegarandeerd.