Скажем, у меня есть массив символов C char buf[15]
. Скажем, переменная int set_me = 0
свои данные в ячейке памяти сразу после char buf[15]
. Если бы я переполнил buf
строкой "aaabbbcccdddeee\xef\xbe\xad\xde"
, изменился бы тип данных set_me
с целого на массив символов?
2 ответа
Нет.
"Тип данных" переменной имеет значение только в исходном коде (и даже тогда только в некоторых языках). Он говорит компилятору, как обращаться с переменной.
Эти высокоуровневые типы данных не существуют как таковые в скомпилированном (нативном) коде. Они могут влиять на то, какие инструкции генерирует компилятор, но самим инструкциям все равно, представляют ли данные символ или число.
Переменные не существуют в аппаратном обеспечении. В оборудовании у вас есть области памяти и инструкции, которые работают с ними.
Переменная может рассматриваться как представление данных в ячейке памяти - если вы косите и смотрите на одну и ту же память немного по-разному (другая переменная с другим типом ссылается на одну и ту же ячейку), то одно и то же двоичное значение может иметь другое значение ,
Так , например, байты 0x41 могут быть истолкованы как UTF-8 кодировкой символов A
Это также можно интерпретировать как однобайтовое целое число 65
. Его также можно интерпретировать как один байт в многобайтовом целом числе или числе с плавающей запятой или один байт в многобайтовой кодировке символов. Это может быть 0b1000001
. Все из одного байта в одной и той же ячейке памяти. В языке C вы можете увидеть этот эффект, приведя к этим различным типам.
Когда у вас "переполнение буфера", вы делаете что-то за пределами того, что может ожидать ваш компилятор или язык. Но что касается аппаратного обеспечения 1, вы записываете байты (одиночные или множественные) в область памяти. В ячейке памяти нет "типа". Фактически, аппаратное обеспечение даже не знает, что какой-то конкретный набор байтов создает массив или буфер в вашем коде.
Где бы вы в следующий раз не обращались к этой ячейке памяти в вашем коде, инструкции будут выполняться в соответствии с первоначальным определением. например, если они ожидали число там, они будут действовать на любые байты данных, как если бы они были числом.
Чтобы использовать ваш пример, предположим, что ваше int
является 4-байтовым (32-битным) целым числом со знаком:
+-------------+--------------------------------------------+-----------+
| 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|
+-------------+--------------------------------------------------------+
Вы можете видеть, что область памяти int
теперь содержит 0xEFBEADDE
, предполагая систему с прямым порядком байтов 2. Это подписанный 32-битный int -272716322
. Теперь, если вы интерпретируете ту же память, что и unsigned int (uint
), вместо этого будет 4022250974
. Для точно таких же данных в памяти значение полностью зависит от того, как вы их просматриваете.
1 Существуют некоторые механизмы, которые препятствуют записи в защищенные области памяти и могут привести к сбою программы, если вы попытаетесь это сделать.
2 x86 на самом деле является прямым порядком байтов, что означает, что вы интерпретируете байты, составляющие большее значение в обратном направлении. Так что на x86 вы бы вместо того, чтобы иметь 0xDEADBEEF
давая подписал -559038737
или без знака 3735928559
С точки зрения C ответом будет «Кто знает? Это неопределенное поведение ".
Типы - это концепция C, а не аппаратная часть. Но правила C не применяются, если ваша программа имеет неопределенное поведение, то есть буквальное значение Undefined Behavior в стандарте C. И переполнение буфера является одной из форм этого.
Первоначально я написал "правила C больше не применяются", но на самом деле неопределенное поведение имеет обратную силу. Правила C не применяются к программе, которая будет иметь неопределенное поведение в будущем.