Digamos que eu tenha uma matriz de caracteres C char buf[15]
. A variável Say int set_me = 0
tem seus dados armazenados em um local de memória diretamente depois char buf[15]
. Se eu transbordasse buf
com string "aaabbbcccdddeee\xef\xbe\xad\xde"
, o set_me
tipo de dados mudaria de um número inteiro para uma matriz de caracteres?
8
Respostas:
Não.
O "tipo de dados" de uma variável é relevante apenas no código fonte (e mesmo assim apenas em alguns idiomas). Diz ao compilador como tratar a variável.
Esses tipos de dados de alto nível não existem como tal no código compilado (nativo). Eles podem afetar as instruções que um compilador gera, mas as próprias instruções não se importam se os dados representam um caractere ou um número.
Variáveis não existem no hardware. No hardware, você tem locais de memória e as instruções que os operam.
Uma variável pode ser vista como uma visualização dos dados em um local de memória - se você olhar de soslaio e olhar para a mesma memória de maneira um pouco diferente (uma variável diferente com um tipo diferente referente à mesma localização), o mesmo valor binário pode ter um significado diferente .
Por exemplo, o byte 0x41 pode ser interpretado como o caractere codificado em UTF-8
A
. Também pode ser interpretado como o número inteiro de byte único65
. Também pode ser interpretado como um byte em um número inteiro de vários bytes ou número de ponto flutuante, ou um byte em uma codificação de caracteres de vários bytes. Poderia ser o bitset0b1000001
. Tudo a partir do mesmo byte no mesmo local da memória. Na linguagem C, você pode ver esse efeito transmitindo para esses diferentes tipos.Quando você tem um "buffer overflow", está fazendo algo fora dos limites esperados pelo seu compilador ou idioma. Mas, no que diz respeito ao hardware 1 , você está gravando bytes (únicos ou múltiplos) em um local de memória. Um local de memória não possui um "tipo". De fato, o hardware nem sabe que qualquer conjunto específico de bytes cria uma matriz ou buffer no seu código.
Sempre que você acessar o local da memória em seu código, as instruções serão executadas conforme definido originalmente. por exemplo, se eles esperavam um número lá, eles agiriam em quaisquer bytes de dados como se fossem um número.
Para usar seu exemplo, assumindo que você
int
é um número inteiro assinado de 4 bytes (32 bits):Você pode ver que o
int
local da memória agora contém0xEFBEADDE
, assumindo um sistema big-endian 2 . Este é o int assinado de 32 bits-272716322
. Agora, se você interpretar a mesma memória que um int (uint
) não assinado , seria4022250974
. Para exatamente os mesmos dados na memória, o significado depende inteiramente de como você os visualiza.1 Existem alguns mecanismos que impedem que você grave em regiões protegidas da memória e travam o programa se você tentar fazê-lo.
2 x86 é na verdade little-endian, o que significa que você interpreta os bytes que compõem um valor maior ao contrário. Assim, no x86, você teria
0xDEADBEEF
, dando assinado-559038737
ou não3735928559
.fonte
0xdeadbeef
, em uma arquitetura x86, ocuparia menos espaço na memória que sua contraparte decimal3735928559
?0xDEADBEEF
é armazenada na memória como0x30 0x78 0x44 0x45 0x41 0x44 0x42 0x45 0x45 0x46
.0xEFBEADDE
. Talvez reformule isso um pouco. Caso contrário, essa é uma resposta excelente - eu particularmente gosto da analogia "ver" e da idéia "fechar os olhos" :)Do ponto de vista C, a resposta seria "Quem sabe? É um comportamento indefinido".
Tipos são um conceito C, não hardware. Mas as regras C não se aplicam se o seu programa tiver Comportamento indefinido, que é o significado literal de Comportamento indefinido no padrão C. E os estouros de buffer são uma forma disso.
Inicialmente, escrevi "as regras C não se aplicam mais", mas, na verdade, o comportamento indefinido é retroativo. As regras C não se aplicam a um programa que terá um comportamento indefinido no futuro.
fonte