Изменяет ли переполнение буфера тип данных переменной, которую он перезаписывает?

1763
Darien Springer

Скажем, у меня есть массив C символов char buf[15]. Скажем, переменная int set_me = 0хранит свои данные непосредственно в ячейке памяти char buf[15]. Если бы я переполнился bufстрокой "aaabbbcccdddeee\xef\xbe\xad\xde", set_meизменился бы тип данных с целого на массив символов?

8
Зависит от того, кто интерпретирует данные. наконец, все двоично. Таким образом, то, как вы его интерпретируете, может быть допустимым целочисленным значением или вызвать ошибку приведения Ganesh R. 5 лет назад 3

2 ответа на вопрос

33
Bob

Нет.

«Тип данных» переменной имеет значение только в исходном коде (и даже только в некоторых языках). Он говорит компилятору, как обращаться с переменной.

Эти высокоуровневые типы данных не существуют как таковые в скомпилированном (нативном) коде. Они могут влиять на то, какие инструкции генерирует компилятор, но самим инструкциям все равно, представляют ли данные символ или число.


Переменные не существуют в оборудовании. В оборудовании у вас есть ячейки памяти и инструкции, которые работают с ними.

Переменная может рассматриваться как представление данных в ячейке памяти - если вы косите и смотрите на одну и ту же память немного по-разному (другая переменная с другим типом ссылается на одну и ту же ячейку), одно и то же двоичное значение может иметь другое значение ,

Например, байт 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.

Таким образом, `0xdeadbeef` в архитектуре x86 будет занимать меньше места в памяти, чем его десятичный аналог,` 3735928559`? Darien Springer 5 лет назад 0
@DarienSpringer Оба имеют одинаковую комбинацию битов. dsstorefile1 5 лет назад 1
@DarienSpringer Оба занимают 4 байта памяти - это одинаковая 4-байтовая последовательность. Они идентичны по памяти. Если хотите, вы можете считать это основанием 2 (двоичным) в памяти. Затем, когда вы отображаете их (конвертируете в строку для вывода), вы можете выбрать базу для отображения - шестнадцатеричное число - это основание 16, а десятичное число - это базовое 10. Строковые представления хранятся в другом месте памяти и могут использовать разные суммы. памяти (так как каждый символ является отдельным байтом). * Строка * `0xDEADBEEF` сохраняется в памяти как` 0x30 0x78 0x44 0x45 0x41 0x44 0x42 0x45 0x45 0x46`. Bob 5 лет назад 2
@DarienSpringer Другими словами, число - это одно и то же число, независимо от того, в какой базе оно находится. Hex - удобный (компактный) способ просмотра двоичного файла. Физически это двоичный файл. Людям нравится десятичная дробь, поэтому мы чаще показываем числа как десятичные. Но пока мы не дошли до шага отображения, все числовые операции (сложение, вычитание, умножение и т. Д.) Работают с одними и теми же двоичными данными в памяти. Bob 5 лет назад 5
_ «Вы можете видеть, что область памяти int теперь 0xEFBEADDE» _ Nitpick: Я знаю, что вы этого не предполагали, но звучит так, как будто вы говорите, что int находится _at_ в области памяти `0xEFBEADDE`. Возможно, перефразирую это немного. В противном случае это превосходный ответ - мне особенно нравится аналогия «взгляда» и идея «щурки» :) Lightness Races in Orbit 5 лет назад 1
@LightnessRacesinOrbit Хороший вопрос. Ред. Bob 5 лет назад 0
2
MSalters

С точки зрения C ответом будет «Кто знает? Это неопределенное поведение».

Типы - это концепция C, а не аппаратная часть. Но правила C не применяются, если ваша программа имеет неопределенное поведение, то есть буквальное значение Undefined Behavior в стандарте C. И переполнение буфера является одной из форм этого.

Сначала я написал «правила C больше не применяются», но на самом деле неопределенное поведение имеет обратную силу. Правила C не применяются к программе, которая будет иметь неопределенное поведение в будущем.

Похожие вопросы