L'indirizzo 0000000C è un indirizzo speciale?


32

Durante la programmazione a volte le cose si rompono. Hai fatto un errore e il tuo programma tenta di leggere da un indirizzo errato.

Una cosa che mi colpisce che spesso quelle eccezioni sono simili:

Access violation at address 012D37BC in module 'myprog.exe'. Read of address 0000000C.

Ora vedo molti log degli errori e ciò che mi colpisce è il: 0000000C. È un indirizzo "speciale"? Vedo altre violazioni dell'accesso con letture errate ma gli indirizzi sembrano casuali, ma questo continua a tornare in situazioni totalmente diverse.


1
Ho anche notato che 0000000Cè molto più comune di 00000008, ma nessuna delle risposte sembra indirizzarlo affatto: /
Mooing Duck

2
Forse questo System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringDataè 12=0x0Cun motivo per cui questo offset è più comune.
Mark Hurd,

1
@MarkHurd Questo è spaventoso. Pensi davvero che ci siano così tante applicazioni non gestite che apposta leggono / scrivono stringhe .NET che questa sarebbe una delle principali fonti di violazioni dell'accesso?
Luaan,

Risposte:


57

00000000è un indirizzo speciale (il puntatore null). 0000000Cè proprio quello che ottieni quando aggiungi un offset di 12 al puntatore nullo, molto probabilmente perché qualcuno ha cercato di ottenere il zmembro di una struttura come quella sotto attraverso un puntatore che era effettivamente nullo.

struct Foo {
    int w, x, y; // or anything else that takes 12 bytes including padding
    // such as: uint64_t w; char x;
    // or: void *w; char padding[8];
    // all assuming an ordinary 32 bit x86 system
    int z;
}

29
O forse perché un piccolo valore integrale è stato erroneamente distaccato come se fosse un puntatore. I valori piccoli sono molto più comuni dei valori enormi, quindi questo tende a produrre indirizzi illegali come 0X0000000C piuttosto che, ad esempio, 0x43FCC893.
Kilian Foth,

3
Il motivo per cui ho posto questa domanda è perché 0000000C ritorna così spesso rispetto ad altri indirizzi. Perché l'offset 12 è una grandezza più comune dell'offset 4, 8 o 16?
Pieter B,

5
Dopo ulteriori accertamenti questa risposta è totalmente corretta. Nella mia fonte la proprietà "tag" delle classi è ampiamente usata (sia nel bene che nel male devo occuparmene.) La proprietà tag nel mio caso fa parte di una classe base di basso livello e viene sempre creata a quell'offset.
Pieter B,

1
Punto eccellente. Forse è stato coperto il caso del puntatore null, ma il puntatore null ++ è solo un indirizzo normale (e in questo caso non valido), quindi fallisce solo accedendo ad esso.
Neil,

8
@Leushenko Sì, la protezione della memoria di solito funziona su intere pagine e anche se fosse possibile catturare solo 0, è preferibile proteggere anche i seguenti indirizzi perché è probabile che accedano se si verifica l'aritmetica del puntatore con un puntatore nullo (come in Caso di OP).

11

In Windows è illegale dedurre l' intera prima e l'ultima pagina , in altre parole il primo o l'ultimo 64 KiB della memoria di processo (gli intervalli 0x00000000da 0x0000ffffe 0xffff0000verso 0xffffffffin un'applicazione a 32 bit).

Questo per intrappolare il comportamento indefinito di dereferenziare un puntatore o un indice null in un array null. E la dimensione della pagina è di 64 KiB, quindi Windows deve solo evitare che alla prima o all'ultima pagina venga assegnato un intervallo valido.

Ciò non protegge da puntatori non inizializzati che potrebbero avere qualsiasi valore (inclusi indirizzi validi).


7
Windows non può davvero farlo. La tabella delle pagine è una struttura definita e richiesta da x86 e le pagine piccole sono fisse a 4KB. È incastonato nella pietra (più precisamente, nel silicio). Il 64 KB è probabilmente per comodità.
ElderBug

12
Preferirei scrivere 64 KiB anziché 65 kB in questo caso, poiché la dimensione della potenza di due è rilevante.
Codici A Caos

4
La gamma di 64 KB è rimasta dalla versione Aplha di NT. E non è la dimensione della pagina, ma la granularità di allocazione. blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301

3
@CodesInChaos: Mentre le maiuscole "M", "G" e "T" sono ambigue, non vedo alcun motivo per deprecare l'uso di "k" per 10 ^ 3 e "K" per 2 ^ 10.
supercat

2
@MooingDuck Sì, è per questo che ho creato delle piccole pagine. La maggior parte delle CPU x64 supporta anche le pagine 1GiB. Per quanto ne so, Windows sempre pagine con pagine 4KB, a meno che non siano allocate con API speciali.
ElderBug

2

Per quanto riguarda il motivo per cui 0x0Csembra più comune di 0x08(è davvero? Non lo so; e in quali tipi di applicazioni?), Ciò potrebbe avere a che fare con i puntatori della tabella dei metodi virtuali. Questo è davvero più di un commento (ipotesi di massa selvaggia :), ma è un po 'più grande, quindi ecco qui ... Se hai una classe con metodi virtuali, i suoi campi verranno spostati 0x04. Ad esempio, una classe che eredita da un'altra classe virtuale potrebbe avere un layout di memoria come questo:

0x00 - VMT pointer for parent
0x04 - Field 1 in parent
0x08 - VMT pointer for child
0x0C - Field 1 in child

È uno scenario comune o addirittura vicino? Non ne sono sicuro. Tuttavia, si noti che in un'applicazione a 64 bit, questo potrebbe spostarsi ancora più interessante verso il 0x0Cvalore:

0x00 - VMT parent
0x08 - Field 1 parent
0x0C - VMT child
0x14 - Field 2 child

Quindi in realtà ci sono molti casi in cui le applicazioni potrebbero avere una significativa sovrapposizione negli offset del puntatore null. Potrebbe essere il primo campo in una classe figlio o il suo puntatore alla tabella dei metodi virtuali, necessario ogni volta che si chiama un metodo virtuale su un'istanza, quindi se si chiama un metodo virtuale su un nullpuntatore, si otterrà una violazione di accesso sul relativo Offset VMT. La prevalenza di questo particolare valore potrebbe quindi avere a che fare con alcune API comuni che forniscono una classe che ha un modello di eredità simile o, più probabilmente, un'interfaccia particolare (abbastanza possibile per alcune classi di applicazioni, come i giochi DirectX). Potrebbe essere possibile tenere traccia di una semplice causa comune come questa, ma tendo a sbarazzarmi di applicazioni che fanno il dereferenziamento nullo abbastanza rapidamente, quindi ...


1
Se guardi attraverso i commenti, puoi ridurre considerevolmente le ipotesi.
Deduplicatore,

@Deduplicator Bene, trovo l'idea che le stringhe .NET gestite vengano utilizzate in codice non sicuro con operazioni di puntatore manuale spaventose, e il pensiero che questa sarebbe la causa principale delle violazioni dell'accesso ancora di più. "Sì, questo è totalmente sicuro per la memoria, non ti preoccupare, abbiamo usato C #. Abbiamo semplicemente modificato manualmente la memoria da C ++, ma è sicura in C #."
Luaan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.