O estouro de buffer altera o tipo de dados da variável que está substituindo? [fechadas]

8

Digamos que eu tenha uma matriz de caracteres C char buf[15]. A variável Say int set_me = 0tem seus dados armazenados em um local de memória diretamente depois char buf[15]. Se eu transbordasse bufcom string "aaabbbcccdddeee\xef\xbe\xad\xde", o set_metipo de dados mudaria de um número inteiro para uma matriz de caracteres?

Darien Springer
fonte
3
Depende de quem está interpretando os dados. finalmente tudo é binário. Então, a maneira como você interpretá-lo, ele pode ser um valor inteiro válido ou causar um erro de elenco
Ganesh R.

Respostas:

33

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 único 65. 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 bitset 0b1000001. 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):

+-------------+--------------------------------------------+-----------+
| Source code |                  char[15]                  |    int    |
+-------------+--------------------------------------------------------+
| Memory      |61|61|61|62|62|62|63|63|63|64|64|64|65|65|65|EF|BE|AD|DE|
+-------------+--------------------------------------------------------+

Você pode ver que o intlocal da memória agora contém 0xEFBEADDE, 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 , seria 4022250974. 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 -559038737ou não 3735928559.

Prumo
fonte
Assim 0xdeadbeef, em uma arquitetura x86, ocuparia menos espaço na memória que sua contraparte decimal 3735928559?
Darien Springer 04/04
2
@DarienSpringer Ambos ocupam 4 bytes de memória - são a mesma sequência de 4 bytes. Eles são idênticos na memória. Você pode considerar tudo isso como base 2 (binário) na memória, se desejar. Então, quando você as exibe (converte em uma string para saída), pode escolher uma base para exibir - o hex é a base 16 e o ​​decimal é a base 10. As representações da string são armazenadas em um local de memória diferente e podem usar quantidades diferentes de memória (como cada caractere é um byte separado). A cadeia 0xDEADBEEF é armazenada na memória como 0x30 0x78 0x44 0x45 0x41 0x44 0x42 0x45 0x45 0x46.
Bob
5
@DarienSpringer Dito de outra forma, um número é o mesmo número, não importa em que base esteja. Hex é uma maneira conveniente (compacta) de visualizar binários. Fisicamente, é binário. Os seres humanos gostam de decimal, então, mais frequentemente, exibimos números como decimais. Mas até chegarmos à etapa de exibição, todas as operações numéricas (somar, subtrair, multiplicar etc.) funcionam nos mesmos dados binários da memória.
Bob
1
"Você pode ver que a localização da memória do int agora é 0xEFBEADDE" Nitpick: Eu sei que você não pretendia isso, mas parece que você está dizendo que o int está localizado no local da memória 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" :)
Lightness Races in Orbit
@LightnessRacesinOrbit Good point. Editado.
Bob
2

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.

MSalters
fonte