19 ноября 2020 г.

Добавление EurekaLog в программу вызывает Integer Overflow?

К нам обратился человек, который пожаловался на то, что его приложение работало нормально, пока он не добавил в него EurekaLog. После включения в проекте EurekaLog стало появляться исключение Integer Overflow. Исключение происходило внутри функции _UStrCatN (функция конкатенации нескольких строк в RTL).

Функция _UStrCatN написана на ассемблере, но если вникнуть в смысл проверок, то получится что-то такое:
  DestLen := {... вычисляется длина результирующей строки ...};      
  if DestLen < 0 then 
    _IntOver;
где _IntOver - это функция RTL, которая и вызывает исключение Integer Overflow.

Что происходит? Как длина строки может быть отрицательной? Это баг в EurekaLog?

Указанная проверка внутри _UStrCatN должна ограничить строки 2 Гб памяти: если результат сложения всех строк будет больше 2 Гб, то произойдёт переполнение, и длина станет отрицательной. Таким образом, Integer Overflow при сложении строк может возникать, если результат слишком большой.

Но при чём тут тогда EurekaLog? И как проверка может срабатывать, если мы складываем небольшие строки? (клиент подтвердил это логом)

Такое "ложно-положительное" срабатывание возможно, если вы проводите операцию с уже удалённой строкой.

Посмотрите на такой код:
var
  Marker: String;
  
  function ReadLine: String;
  begin
    // ...
    Marker := { ... };
    // ...
  end;
  
begin
  // ...
  Data := Data + Marker + ReadLine;
  // ...
end;
Видите ли вы проблему в этом коде?

Чтобы понять проблему, нужно знать как выполняется строка "Data := Data + Marker + ReadLine;". На псевдо-коде это выглядит как-то так:
Param0 := Pointer(Data);
Param1 := Pointer(Marker);
Param2 := Pointer(ReadLine);
_UStrCatN(Data, [Param0, Param1, Param2]);
Иными словами, оператор последовательно сохраняет указатели на аргументы, прежде чем вызвать функцию.

Вот вам и проблема: оператор сохраняет указатель на строку Marker, но строка Marker меняется внутри функции ReadLine. Это означает, что сохранённый указатель будет указывать на старую строку. Таким образом, на вход функции _UStrCatN попадёт уже удалённая строка.

Заметьте, что без EurekaLog в проекте этот баг не является "проблемой". Действительно, удалённая память просто помечается как свободная, но её содержимое не очищается. Это значит, что _UStrCatN успешно проведёт конкатенацию с уже удалённой строкой. И результат операции, скорее всего, будет корректным. Т.е. баг в коде есть, но его совершенно не видно, поскольку программа функционирует полностью правильно.

Ситуация меняется в корне, если в проект добавляется EurekaLog (или любой другой инструмент для отладки проблем с памятью). По умолчанию в EurekaLog включены проверки памяти. Это означает, что удалённая память будет очищена. Как правило, это делается шаблоном вроде DEADBEEF. Заметьте, что Integer представление DEADBEEF - отрицательно (равно -559038737). Т.е. прибавление к этому числу длин нескольких небольших строк также даст отрицательное число.

Иными словами, если в проект добавлена EurekaLog, то операция с уже удалённой строкой больше не будет успешной. Ранее скрытый баг теперь виден.

Комментариев нет :

Отправка комментария

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.