O endereço 0000000C é um endereço especial?


32

Na programação, às vezes as coisas quebram. Você cometeu um erro e seu programa tenta ler de um endereço errado.

Uma coisa que se destaca para mim é que muitas vezes essas exceções são como:

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

Agora vejo muitos logs de erros e o que mais me destaca é o: 0000000C. Este é um endereço "especial"? Vejo outras violações de acesso com leituras ruins, mas os endereços parecem aleatórios, mas esse continua voltando em situações totalmente diferentes.


1
Também notei que isso 0000000Cé muito mais comum do que isso 00000008, mas nenhuma das respostas parece abordar isso: /
Mooing Duck

2
Talvez System.Runtime.CompilerServices.RuntimeHelpers.OffsetToStringDataseja 12=0x0Cpor isso que esse deslocamento seja mais comum.
Mark Hurd

1
@MarkHurd Isso é assustador. Você realmente acha que existem tantos aplicativos não gerenciados que, de propósito, leem / gravam seqüências .NET que seriam uma fonte importante de violações de acesso?
Luaan 21/01

Respostas:


57

00000000é um endereço especial (o ponteiro nulo). 0000000Cé exatamente o que você obtém ao adicionar um deslocamento de 12 ao ponteiro nulo, provavelmente porque alguém tentou obter o zmembro de uma estrutura como a abaixo através de um ponteiro que era realmente nulo.

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
Ou talvez porque algum pequeno valor integral tenha sido erradamente desreferenciado como se fosse um ponteiro. Valores pequenos são muito mais comuns que valores enormes; portanto, isso tende a produzir endereços ilegais como 0X0000000C em vez de, por exemplo, 0x43FCC893.
precisa saber é o seguinte

3
O motivo pelo qual fiz essa pergunta é porque 0000000C volta com tanta frequência em comparação com outros endereços. Por que o deslocamento 12 é uma magnitude mais comum que o deslocamento 4, 8 ou 16?
Pieter B

5
Após uma investigação mais aprofundada, esta resposta está totalmente correta. Na minha fonte, a propriedade "tag" das classes é usada extensivamente (bom ou ruim, eu tenho que lidar com isso). A propriedade tag no meu caso faz parte de uma classe base de baixo nível e sempre foi criada nesse deslocamento.
Pieter B

1
Ponto excelente. Talvez o caso do ponteiro nulo tenha sido coberto, mas o ponteiro nulo ++ é apenas um endereço normal (e, neste caso, inválido), portanto, falha apenas ao acessá-lo.
Neil

8
@Leushenko Sim, a proteção de memória geralmente funciona em páginas inteiras e, mesmo que fosse possível capturar apenas 0, é preferível também proteger os endereços a seguir, porque é provável que eles sejam acessados ​​se houver aritmética de ponteiro com um ponteiro nulo (como em Caso do OP).

11

No Windows, é ilegal desreferenciar toda a primeira e a última página , ou seja, o primeiro ou o último 64 KiB da memória do processo (os intervalos 0x00000000de 0x0000ffffe 0xffff0000para 0xffffffffum aplicativo de 32 bits).

Isso serve para interceptar o comportamento indefinido de desreferenciar um ponteiro ou índice nulo em uma matriz nula. E o tamanho da página é de 64 KiB; portanto, o Windows precisa impedir que a primeira ou a última página recebam um intervalo válido.

Isso não protegerá contra ponteiros não inicializados que possam ter algum valor (incluindo endereços válidos).


7
O Windows não pode realmente fazer isso. A tabela de páginas é uma estrutura definida e exigida por x86, e páginas pequenas são fixadas em 4KB. Está gravado em pedra (mais precisamente, em silício). O 64KB é provavelmente por conveniência.
ElderBug

12
Prefiro escrever 64 KiB em vez de 65 kB neste caso, uma vez que o tamanho de duas potências é relevante.
CodesInChaos

4
O intervalo de 64 KB é uma sobra da versão Aplha do NT. E não é o tamanho da página, mas a granularidade da alocação. blogs.msdn.com/b/oldnewthing/archive/2003/10/08/55239.aspx
shf301

3
@CodesInChaos: Enquanto "M", "G" e "T" maiúsculas são ambíguas, não vejo razão para descontinuar o uso de "k" para 10 ^ 3 e "K" para 2 ^ 10.
Supercat

2
@MooingDuck Sim, é por isso que preciso de páginas pequenas. A maioria das CPUs x64 também suporta as páginas 1GiB. Tanto quanto eu sei, o Windows sempre páginas com páginas de 4KB, a menos que alocado com APIs especiais.
ElderBug

2

Quanto ao porquê 0x0Cparece mais comum do que 0x08(é realmente? Eu não sei; e em que tipos de aplicativos?), Isso pode ter a ver com ponteiros de tabela de método virtual. Isso é realmente mais um comentário (adivinhação em massa :), mas é um pouco maior, então aqui vai ... Se você tem uma classe com métodos virtuais, seus próprios campos serão alterados 0x04. Por exemplo, uma classe que herda de outra classe virtual pode ter um layout de memória como este:

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

Esse é um cenário comum, ou até próximo? Não tenho certeza. No entanto, observe que em um aplicativo de 64 bits, isso pode ser ainda mais interessante para o 0x0Cvalor:

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

Portanto, existem muitos casos em que os aplicativos podem ter sobreposição significativa nas compensações de ponteiro nulo. Pode ser o primeiro campo de uma classe filho ou seu ponteiro de tabela de método virtual - necessário sempre que você chama qualquer método virtual em uma instância; portanto, se você estiver chamando um método virtual em um nullponteiro, terá violação de acesso em seu Deslocamento da VMT. A prevalência desse valor específico pode ter algo a ver com alguma API comum que fornece uma classe que possui um padrão de herança semelhante ou, mais provavelmente, uma interface específica (possível para algumas classes de aplicativos, como jogos do DirectX). Pode ser possível rastrear uma causa comum simples como essa, mas eu costumo me livrar de aplicativos que fazem a desreferenciação nula muito rapidamente, então ...


1
Se você examinar os comentários, poderá reduzir consideravelmente as estimativas.
Deduplicator

@ Deduplicator Bem, acho que a idéia de que seqüências .NET gerenciadas são usadas em códigos inseguros, com operações manuais de ponteiro assustadoras, e o pensamento de que essa seria a principal causa de violações de acesso ainda mais. "Sim, isso é totalmente seguro para a memória, não se preocupe, usamos o C #. Nós apenas modificamos manualmente a memória do C ++, mas é seguro no C #."
Luaan
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.