Menu

0xDEADBEEF: cosa significa e perché non è un indirizzo di memoria

6 min di lettura Manuel

0xDEADBEEF sembra un indirizzo di memoria e non lo è. Vale 3735928559 in decimale, occupa quattro byte, si legge in un hex dump come una costante e non mappa quasi certamente nessuna pagina accessibile. È un valore sentinella: un numero scelto perché non dovrebbe comparire mai, in modo che se lo vedi qualcosa non sta funzionando. In questo è un parente stretto del canary token che abbiamo descritto in Canary token: la trappola che parla al posto tuo: non protegge un asset, rende visibile un errore.

Le quattro regole per scegliere un valore così

Prima della storia, la tecnica. Un buon valore "veleno" per il debug deve fallire in modo rumoroso. I requisiti sono quasi aritmetici:

  • Non deve essere un indirizzo valido. Se i bit alti sono posti, servono milioni di allocazioni prima che l'indirizzo diventi valido in un processo qualsiasi.

  • Non deve essere allineato. Su x86 l'allineamento è 4 byte, quindi un valore divisibile per 4 sarebbe plausibile come puntatore. 0xDEADBEEF non lo è.

  • Deve avere tutti i byte diversi. Se usato come stack canary serve a capire quanto è stato sovrascritto: con un byte solo, non distingui la sovrascrittura da un singolo byte errato.

  • Non deve essere 0x00 o 0xFF. Sono i valori più probabili dopo un singolo byte scribagliato, e servono a distinguere un valore "non inizializzato" da un valore "azzerato per scelta".

0xDEADBEEF soddisfa tutti e quattro. Per questo l'hanno usato sistemi operativi, allocatori di memoria e compilatori, ognuno per ragioni un po' diverse.

Non è un indirizzo: la differenza che conta

Questo è il punto su cui vale la pena essere precisi, perché la confusione è frequente. 0xDEADBEEF non è un indirizzo di memoria nel senso di un puntatore valido: non c'è nessun processo in cui quel numero corrisponda a una pagina mappata e leggibile. È un valore usato dentro la memoria, che代表a "qui non c'è niente di utilizzabile".

La differenza pratica è già tutta nella parola. In un crash dump del 1980, stampato su carta, gli operatori IBM cercavano un "eyecatcher": un valore a tutta parola che saltava all'occhio in mezzo a dati omotenei, permettendo di posizionarsi nell'offset giusto del buffer senza leggerlo. Da lì l'uso come pattern di riempimento per memoria debolmente inizializzata, e l'espressione "your program is DEADBEEF" nel gergo informatico, con il significato di *andato, abortito, svuotato*.

Da dove viene: ambiente IBM, anni '70-'80

L'origine non è documentata con certezza, e va detto: chi lo usa oggi ha spesso letto più di quanto la storia consenta di affermare. Quello che risulta solido:

  • La pratica risale agli anni '70, quando in alcuni ambienti IBM si usava 0xDEADBEEF per indicare un valore non valido, riempiendo la memoria liberata dopo una deallocazione o come surrogato di NULL.

  • È documentato e diffuso soprattutto nelle macchine midrange IBM, inclusa la famiglia RS/6000, dove compare in dump di sistema.

  • È presente in implementazioni di VMS su DEC VAX, nei Mac classici, in OPENSTEP e nel Commodore Amiga.

  • Su Solaris marca la memoria kernel liberata:
#define KMEM_FREE_PATTERN  0xdeadbeefdeadbeefULL

Quel dettaglio è interessante per due motivi. Primo, il valore è ripetuto due volte per riempirere un intero blocco da 64 bit allineato, mantenendo l'allineamento. Secondo, in un'epoca in cui dumps di sistema erano carta, il pattern rendeva immediatamente riconoscibile un'area "morta" in mezzo a dati utili.

Perché è sparito, e perché non del tutto

Con i sistemi a memoria paginata moderni l'uso principale è progressivamente sparito. Le ragioni sono tecniche ed esatte:

  • Non serve più inizializzare. Ogni kernel azzera le pagine quando le mappa in uno spazio di indirizzamento. Azzerare con 0xDEADBEEF costerebbe fault su tutte le pagine toccate, per un vantaggio che non si paga più.

  • Cache e costo. Scrivere un pattern non-zero alloca memoria fisica che un processo potrebbe non usare mai, e fa perdere cache.

Ma il concatto è rimasto nei compilatori e nel debug: malloc e free di molte librerie moderni us ancora valori sentinella per distinguere memoria inizializzata da memoria liberata, e la scena debugger/analisi forense continua a usare pattern di questo tipo per riconososcere aree "avvelenate" nelle immagini di memoria.

Vale la pena accennare alle varianti, perché mostrano che la convenzione è più ampia di un solo valore:

ValoreUso tipico
0xDEADBEEFmemoria liberata, non inizializzata
0xBBADBEEFusato da iOS nelle crash report
0xCAFEBABEmagic number dei file .class Java
0xDEADBABEcostante di controllo nel Jikes JVM
0xBAADF00Dvalore non valido, altro nome della stessa idea

Questi pattern hanno un nome collettivo: hexspeak, valori esadecimali che formano parole inglesi leggibili.

Perché oggi è una cattiva idea nel codice di produzione

Il valore è splendido nel debug e sconsigliato altrove, e il motivo è preciso. Molti debugger e runtime hanno già riservato quel valore per i propri scopi: se lo usi nel tuo codice, in un ambiente che alloca con pattern sentinella, il tuo valore è indistinguibile dalla memoria libera. Nel caso migliore i dump diventano meno utili, nel peggiore ricevi warning fuorvianti dal debugger.

Il punto più interessante è un altro: se un valore magico compare in tutto il codice, diventa una bomba a orologeria. Chi legge if (p != 0xDEADBEEF) ventimila righe più avanti deve ricordarsi cosa significa, e nessun contesto nel codice lo aiuta. Esiste una funzione già pronta che fa lo stesso lavoro, e ha pure un nome.

Scrivere il numero a mano ha anche un costo di review: 0xDAEDBEEF e 0xDEADBEFF sono errori che passano in una code review e che il compilatore segnala solo al momento dell'errore a runtime, nel caso migliore.

In sintesi

0xDEADBEEF non è un indirizzo: è un valore che non è un indirizzo, scelto per essere memorabile e per fallire rumorosamente. È nato nell'era dei dump su carta degli anni '70-'80, ha attraversato VMS, Mac, Amiga e Solaris, e sopravvive oggi nel debug dei compilatori.

La cosa che vale la pena portare a casa non è il numero, è il principio: un valore sentinella vale quanto la facilità con cui lo riconosci quando qualcosa è andato storto. Lo stesso ragionamento vale per un honeytoken, per un canary in un test, per un campo obbligatorio che nessuno verifica. Non protegge nulla da solo: rende rende il silenzio un errore invece di una normalità.