Är egentligen ingen magi alls, pseudoassembler ser ut ungefär så här
Vid anropet av bar() konstaterar kompilatorn att det man tar adressen av är fyra bytes långt. Vad som händer då är att MPX associerar en övre och en undre gräns på den buffert man når via pekaren (instruktion bndmk) som skickas som argument till bar(). Rent konkret lägger MPX till extra register (som även kan spilla över till RAM om allt för många buffertar är aktiva samtidigt), registren är bnd0 till bnd3.
I foo() har sedan kompilatorn lagt ut en MPX check på varje effektiv adress man kommer skriva till (bndcl och bndcu för att testa nedre och övre gräns). Är det som leder till den trevliga egenskapen att man hamnar på en brytpunkt INNAN skrivningen faktiskt sker när man kör i debugger.
Detta gör också det förväntande
void bar(uint32_t *d)
{
foo ((uint8_t *) d + 1);
}
I detta läge är det i stället skrivningarna b[3] = 0xd; samt b[4] = 0xde; som skriver en respektive två bytes utanför. b[-1] = 0xad; är i detta fall korrekt och genererar inte heller någon varning från MPX.
D.v.s. det som i praktiken spåras är början och slutet på den buffert man når via en pekare, pekaren behöver inte peka just på första byte.
Den klurige inser direkt att MPX absolut inte kan plocka ALLA tänkbara fall. Ett specifikt fall som både är ett hål men också en finess ihop med MPX är C99 arrayer som deklareras med längd noll. Semantiskt betyder det ju detta
struct my_struct
{
int a;
char b[0];
};
att något av typen struct my_struct innehåller medlemmen a följt av noll eller flera chars. Här är det ju omöjligt för kompilatorn att berätta för MPX var den bakre gränsen går. Man kan dock fortfarande upptäcka överskrivningar framför något som pekar på struct my_struct.
Jag ser främst MPX som ett hjälpmedel vid utveckling, framförallt då det mesta jag jobbar med körs på ARM och inte x86. Fungerar ju ändå lysande i t.ex. unit-tester, de testar ju endast att logiken är rätt, inte att det fungerar på ett specifikt system.
Är ändå fullt möjligt att använda MPX även i produktionskod. Det är en viss kostnad, men den är relativt liten och det är en kostnad alla som kör t.ex. Java/C# alltid betalar utan att det sätter allt för stora käppar i hjulen. I sann "x86-hacky" anda så är kodningen av MPX instruktioner gjorda så att modeller som inte stödjer MPX kommer tolka alla bndxx som varianter på nop (No OPeration). Så en binär med MPX-stöd går att köra på alla x86 CPUer!
Är ett hack, men TSX gör samma sak (modeller utan TSX uppför sig som att transaktionen alltid misslyckas så man måste gå "slow-path" som i detta fall är att göra på normalt sett med lås) och om det inte hade gått att köra binären på äldre modeller hade aldrig en sak som TSX stoppats in i Linux standardbibliotek för multitrådning (pthreads).
En defekt likt Heartbleed ska i praktiken vara omöjligt om man använder MPX, så finns garanterat områden där det är värt att ta en liten extrakostnad.