VC ++ wcscpy_s случайным образом утверждает, что "буфер слишком мал" - PullRequest
0 голосов
/ 09 ноября 2019

Вы можете видеть, что параметр, отправляемый в функцию, не превышает размер буфера из приведенного ниже кода.

Эта проблема возникла случайным образом и произошла только в отладочной сборке.

#include <thread>
#include <sstream>

#define BUF_SZ 32

int main()
{
  wchar_t src[BUF_SZ]{};
  bool running = true;
  std::thread th([&] {
    for (double g = 0; g < 100000; g += .1)
    {
      std::wstringstream ws;
      ws << g;
      wcscpy_s(src, BUF_SZ, ws.str().c_str());
    }
    running = false;
  });

  wchar_t dst[BUF_SZ]{};
  while (running)
    wcscpy_s(dst, src);  // assert on "Buffer is too small" randomly

  th.join();
  return 0;
}

Ответы [ 2 ]

1 голос
/ 13 ноября 2019

Благодаря г-ну Стиву Вишновски из команды MSFT VC ++, здесь приведено полное объяснение проблемы.

Wcscpy_s не работает атомарно на буферах и будет работать правильно только если буферы не работаютизменить содержимое во время выполнения wcscpy_s.

Еще одна вещь, на которую следует обратить внимание, это то, что в режиме отладки функция wcscpy_s заполнит оставшуюся часть буфера отладочной меткой (0xFE), чтобы указать, что данные там сейчаснедопустимо предполагать, что это содержимое, чтобы обнаружить потенциальные ошибки времени выполнения.

Ошибка возникает каждый раз по-разному, но давайте предположим, что эта ошибка происходит, когда вызывается src = 1269.9 и вызывается wcscpy_s (dst, src). Фактическое содержимое src: «1 2 6 9. 9 null 0xfe 0xfe ...». wcscpy_s копирует 1269.9, но когда он собирается прочитать нулевое значение, другие wcscpy_s просто записали новое значение в src, и теперь оно выглядит так: «1 2 7 0 null 0xfe 0xfe ...». Вместо чтения нулевого значения, соответствующего предыдущему src, он читает 0xfe, поэтому думает, что это настоящий символ. Так как нулевой терминатор отсутствует до тех пор, пока мы не достигнем конца буфера, среда выполнения Debug утверждает, что буфер был слишком мал для ввода.

В сборке Release отладочные метки 0xFE не помещаются вбуфер, поэтому он в конечном итоге найдет нулевой символ. Вы также можете отключить отметки отладки, вызвав _CrtSetDebugFillThreshold: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/crtsetdebugfillthreshold?view=vs-2019.

Обратите внимание, что отметки отладки фактически улавливают проблему реальной корректности. Эта проблема «буфер изменен во время wcscpy_s» может возникнуть при любом значении. Например, если src = 1269.9, wcscpy_s может скопировать 126, но затем, когда он собирается прочитать 9, src обновляется до 1270, и значение, которое в конечном итоге будет в dest, будет равно «1260».

0 голосов
/ 11 ноября 2019

Поскольку при копировании строки необходимо копировать символы "src" и завершающий нулевой символ, вам необходимо предоставить буфер, который как минимум на один символ больше размера "src".

Я предлагаю вамМожно попробовать использовать:

    wcscpy_s(dst, sizeof src+1, src); 
...