Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Мне интересно попробовать плагин на:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Красивая 64-битная ошибка на языке Си

Красивая 64-битная ошибка на языке Си

19 Ноя 2009

В языке Си можно использовать функции без их предварительного объявления. Обращаю внимание, что речь идет именно о языке Си, а не Си++. Конечно, данная возможность опасна. Рассмотрим связанный с ней интересный пример 64-битной ошибки.

Ниже приведен корректный код, который выделяет и использует три массива размером 1 гигабайт каждый:

#include <stdlib.h>
void test()
{
  const size_t Gbyte = 1024 * 1024 * 1024;
  size_t i;
  char *Pointers[3];
  // Allocate
  for (i = 0; i != 3; ++i)
    Pointers[i] = (char *)malloc(Gbyte);
  // Use
  for (i = 0; i != 3; ++i)
    Pointers[i][0] = 1;
  // Free
  for (i = 0; i != 3; ++i)
    free(Pointers[i]);
}

Данный код корректно выделит память, запишет в первый элемент каждого массива по единице и освободит занятую память. Код совершенно корректно работает на 64-битной системе.

Теперь удалим или закомментируем строчку "#include <stdlib.h>". Код по-прежнему будет собираться, но при запуске программы произойдет ее аварийное завершение. Поскольку заголовочный файл "stdlib.h" не подключен, компилятор языка Си считает, что функция malloc вернет тип int. Первые два выделения памяти, скорее всего, пройдут успешно. При третьем обращении функция malloc вернет адрес массива за пределами первых 2-х гигабайт. Поскольку компилятор считает, что результат работы функции имеет тип int, он неверно интерпретирует результат и сохраняет в массиве Pointers некорректное значение указателя.

Для лучшего пояснения приведу ассемблерный код, генерируемый компилятором Visual C++ для 64-битной Debug версии. Вначале рассмотрим корректный код, который будет сгенерирован, когда присутствует объявление функции malloc (подключен файл "stdlib.h"):

Pointers[i] = (char *)malloc(Gbyte);
mov   rcx,qword ptr [Gbyte]
call  qword ptr [__imp_malloc (14000A518h)]
mov    rcx,qword ptr [i]
mov    qword ptr Pointers[rcx*8],rax

Теперь рассмотрим вариант некорректного кода, когда отсутствует объявление функции malloc:

Pointers[i] = (char *)malloc(Gbyte);
mov    rcx,qword ptr [Gbyte]
call   malloc (1400011A6h)
cdqe
mov    rcx,qword ptr [i]
mov    qword ptr Pointers[rcx*8],rax

Обратите внимание на наличие инструкции CDQE (Convert doubleword to quadword). Компилятор посчитал, что результат содержится в регистре eax и расширил его до 64-битного значения, чтобы записать в массив Pointers. Соответственно старшие биты регистра rax будут потеряны. Если даже адрес выделенной памяти лежит в пределах первых четырех гигабайт, в случае, когда старший бит регистра eax равен 1 мы все равно получим некорректный результат. Например, адрес 0x81000000 превратится в 0xFFFFFFFF81000000.

К счастью данный вид ошибки легко обнаружить. Например, компилятор Visual C++ выдает два предупреждения, свидетельствующих о потенциальной проблеме:

warning C4013: 'malloc' undefined; assuming extern returning int

warning C4312: 'type cast' : conversion from 'int' to 'char *' of greater size

А анализатор PVS-Studio 3.40 выдает предупреждение "error V201: Explicit type conversion. Type casting to memsize.".

Популярные статьи по теме
Простая ошибка при кодировании - не значит нестрашная ошибка

Дата: 19 Апр 2017

Автор: Андрей Карпов

Популяризируя статический анализатор кода PVS-Studio, мы обычно пишем статьи для программистов. Однако, на некоторые вещи программисты смотрят одностороннее. Именно поэтому и существуют менеджеры про…
Как обнаружить переполнение 32-битных переменных в длинных циклах в 64-битной программе?

Дата: 22 Мар 2016

Автор: Андрей Карпов

Одна из проблем, с которой сталкиваются разработчики 64-битных приложений, это переполнение 32-битных переменных в очень длинных циклах. С этой задачей хорошо справляется анализатор кода PVS-Studio (…
64-битный код в 2015 году: что нового в диагностике возможных проблем?

Дата: 21 Май 2015

Автор: Сергей Васильев

64-битные ошибки достаточно тяжело обнаружить, так как они сродни бомбе замедленного действия: могут дать о себе знать далеко не сразу. Статический анализатор PVS-Studio облегчает задачу поиска и исп…
C++11 и 64-битные ошибки

Дата: 29 Апр 2014

Автор: Андрей Карпов

64-битные компьютеры давно и успешно используются. Большинство приложений стали 64-битными. Это позволяет им использовать больший объем памяти, а также получить прирост производительности за счёт арх…
Отличие %p от %x

Дата: 05 Апр 2013

Автор: Андрей Карпов

В функциях семейства printf существуют спецификаторы типа "%p" и "%x".

Комментарии (0)

Следующие комментарии
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно. Хотите узнать подробнее?
Принять