Я использую clang++ 10 на Ubuntu 20.04 LTS с -fsanitize-undefined-trap-on-error -fsanitize=address,undefined,nullability,implicit-integer-truncation,implicit-integer-arithmetic-value-change,implicit-conversion,integer
Мой код генерирует случайные байты с
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<uint8_t> dd(0, 255);
...
ch = uint8_t(dd(gen));
Эта последняя строка заставляет дезинфицирующее средство сообщать о неопределенном поведении в bits/random.tcc.
template<...> void mersenne_twister_engine<...>::
_M_gen_rand(void) {
const _UIntType __upper_mask = (~_UIntType()) << __r;
const _UIntType __lower_mask = ~__upper_mask;
for (size_t __k = 0; __k < (__n - __m); ++__k)
{
_UIntType __y = ((_M_x[__k] & __upper_mask)
| (_M_x[__k + 1] & __lower_mask));
_M_x[__k] = (_M_x[__k + __m] ^ (__y >> 1)
^ ((__y & 0x01) ? __a : 0));
}
for (size_t __k = (__n - __m); __k < (__n - 1); ++__k)
{
_UIntType __y = ((_M_x[__k] & __upper_mask)
| (_M_x[__k + 1] & __lower_mask));
_M_x[__k] = (_M_x[__k + (__m - __n)] ^ (__y >> 1) <<<<===== this line
^ ((__y & 0x01) ? __a : 0));
}
_UIntType __y = ((_M_x[__n - 1] & __upper_mask)
| (_M_x[0] & __lower_mask));
_M_x[__n - 1] = (_M_x[__m - 1] ^ (__y >> 1)
^ ((__y & 0x01) ? __a : 0));
_M_p = 0;
}
Ошибка гласит:
/usr/include/c++/10/bits/random.tcc:413:33: runtime error: unsigned integer overflow: 397 - 624 cannot be represented in type 'unsigned long'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/include/c++/10/bits/random.tcc:413:33 in
/usr/include/c++/10/bits/random.tcc:413:26: runtime error: unsigned integer overflow: 227 + 18446744073709551389 cannot be represented in type 'unsigned long'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/include/c++/10/bits/random.tcc:413:26 in
Похоже, что есть разница __m-__n == 397 - 624
, которая явно отрицательна, но все операнды беззнаковые.
Вычитаемые переменные являются параметрами шаблона, определенными как size_t __n, size_t __m
, так что это не случайный пограничный случай, а реальный реализуемый шаблон.
Является ли это ошибкой в этой реализации STL или мое использование неверно?
Минимальный воспроизводимый пример: https://godbolt.org/z/vvjWscPnj
ОБНОВЛЕНИЕ: Проблема (не ошибка) зарегистрирована в GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106469 - закрыта как "НЕ ИСПРАВЛЕНО"
Команда GCC назвала переполнение ubsan целого числа без знака clang проверяющим неправильную практику, потому что поведение четко определено (как перенос по модулю) в ISO C++. Хотя в ГПСЧ используется модульная арифметика, в данном конкретном случае это не так.
Однако в большинстве кодов пользовательского пространства беззнаковое переполнение является почти всегда является ошибкой, которую нужно отловить, и эта не-ошибка в GCC STL не позволяет пользователям воспользоваться этой полезной проверкой.
Переполнение целого числа без знака не является UB в C++. UBSAN отключает эту проверку по умолчанию. Если вы включите эту проверку, вы должны понимать, что делаете. Обычно сторонний код идет на подавление. Руководство: -fsanitize=unsigned-integer-overflow
: переполнение целого числа без знака, когда результат вычисления целого числа без знака не может быть представлен в своем типе. В отличие от переполнения целого числа со знаком, это не неопределенное поведение, а часто непреднамеренное.
Я бегу с -ggdb3 -O0 -fsanitize-undefined-trap-on-error -fsanitize=address,undefined,nullability,implicit-integer-truncation,implicit-integer-arithmetic-value-change,implicit-conversion,integer -fno-omit-frame-pointer
@HFTrader Все параметры -fsanitize=
nullability
, implicit-integer-truncation
, implicit-integer-arithmetic-value-change
, implicit-conversion
и integer
включают проверки UBsan, которые не отмечают фактическое неопределенное поведение. Вы должны быть осторожны, чтобы не включить их для стороннего кода, который может полагаться на поведение, которое они отмечают, и что вы сами не полагаетесь на их поведение. Флаги предназначены только для обозначения поведения часто непреднамеренный. См. clang.llvm.org/docs/UndefinedBehaviorSanitizer.html. Хотя оказывается, что у вас действительно есть библиотека UB, согласно опубликованному ответу.
Я думаю, нам нужно увидеть минимальный воспроизводимый пример.
@TedLyngmo Поехали: godbolt.org/z/vvjWscPnj
«Команда GCC сослалась на плохую практику проверки переполнения целочисленного числа без знака ubsan в clang» — что? Я понятия не имею, что это должно было сказать.
@user17732522 user17732522 Судя по опубликованному ответу, здесь можно использовать uint8_t
UB, но это четко определенное беззнаковое переполнение, которое дезинфицирующее средство улавливает и ошибочно вызывает неопределенное поведение.
@JanHudec Да, я написал ответ, объясняющий это после того, как сделал комментарии выше.
Хорошая коллекция предупреждающих флагов дезинфицирующих средств! Предполагая, что их поведение не слишком педантично, я должен рассмотреть возможность их использования для моих сборок CI...
Результат использования uint8_t
в std::uniform_int_distribution
не определен, поэтому:
std::uniform_int_distribution<uint8_t> dd(0, 255); // Don't do this!
Вместо этого вы можете использовать любой из short
, int
, long
, long long
, unsigned short
, unsigned int
, unsigned long
или unsigned long long
.
Throughout this subclause [rand], the effect of instantiating a template:
that has a template type parameter namedIntType
is undefined unless the corresponding template argument is cv-unqualified and is one ofshort
,int
,long
,long long
,unsigned short
,unsigned int
,unsigned long
, orunsigned long long
.
Если это не помогает, пропустите вариант -fsanitize=integer
, так как
-fsanitize=integer
: Checks for undefined or suspicious integer behavior (e.g. unsigned integer overflow). Enablessigned-integer-overflow
... и переполнение целого числа без знака имеет ли нетнеопределенное поведение. Проверка целочисленного переполнения подписал будет автоматически включена с помощью -fsanitize=undefined
, поэтому вам не нужно включать это отдельно.
Если это все еще не помогает, это может быть ошибка в реализации библиотеки gcc, используемой clang++
, которая вызывает это. Вы можете попробовать использовать реализацию библиотеки clang++
, чтобы увидеть, поможет ли это:
clang++ -stdlib=libc++ ...
Круто, ТИЛ. Теперь мне интересно, почему это так!
@CaptainGiraffe Да, я помню, как читал его некоторое время назад и исправил место на cppreference, в котором не упоминалось это ограничение. Мои правки были отменены, пока я не процитировал стандарт. В стандарте не сказано Почему, просто так оно и есть.
Это в стандарте С++? Я не вижу там ссылки на разрешенные типы.
@HFTrader угорь.is/c++draft/rand.req.genl#1.5
Боже, как сложно добавить чек в этот шаблон?
@HFTrader В то время, когда эта функция была добавлена, она не была распространена в аргументах шаблона класса ограничения SFINAE, подобных этому. Если бы это было введено сейчас, я почти уверен, что использовались бы проверки концепции. Но я также не очень понимаю, почему было сделано это ограничение. По-видимому, по этому поводу была проблема LWG, но она была закрыта как не являющаяся дефектом (вместо запроса функции): cplusplus.github.io/LWG/lwg-closed.html#2326
@TedLyngmo В конце концов, комментирование этой строки не привело к исчезновению сообщения. Проблема все еще в другой строке: std::uniform_int_distribution<uint64_t> ds(1, maxsize); size_t size = ds(gen);
где maxsize имеет значения от 2 до 4096. Даже при 2 он срабатывает.
@HFTrader Хорошо, я не вижу другой причины для UB во фрагменте, который вы нам показали, поэтому, пожалуйста, обновите вопрос и поставьте в нем минимальный воспроизводимый пример.
@TedLyngmo, использующий libc++, фактически заставляет сообщение исчезнуть
@HFTrader Хорошо! Затем я сильно подозреваю ошибку в реализации библиотеки gcc или в том, как clang++
ее использует.
Не нужно заменять на signed-integer-overflow
. -fsanitize=undefined
уже включает почти все проверки, которые проверяют фактическое неопределенное поведение, я думаю, за исключением local-bounds
(я думаю, из соображений производительности) и float-divide-by-zero
(который определен для IEEE 754, но не для C и C++).
@user17732522 user17732522 Да, я заметил, что «-fsanitize=undefined
: все проверки, перечисленные выше, кроме float-divide-by-zero
, unsigned-integer-overflow
, implicit-conversion
, local-bounds
и группы проверок nullability-*
». Обновил ответ.
«Глупого санитайзера clang, который жалуется на совершенно правильный код» недостаточно, чтобы заключить, что в gcc std::lib есть ошибка.
@JonathanWakely Действительно. Я не пришел к выводу, что это ошибка в gcc std::lib (хотя я сильно подозревал, что это так). Я прочитал обо всех clang
конкретных параметрах, используемых OP, и добавил возможное исправление, чтобы прекратить использование -fsanitize=integer
к ответу.
Интересно, есть ли у них также другие дезинфицирующие средства, «полагающиеся на явные гарантии в стандарте языка C++». Возможно, дезинфицирующее средство «предполагает, что байт имеет не менее 8 бит» или «предполагает, что std::string.c_str()
возвращает буфер с нулевым завершением».
@CodyGray Есть много разных дезинфицирующих средств. Я не думаю, что два, о которых вы упомянули, существуют :-)
@CodyGray, это единственная известная мне ловушка для поведения, которое на 100% гарантирует определенное поведение, которое вовсе не является «неопределенным».
@CaptainGiraffe Почему нельзя использовать std::uniform_int_distribution<uint8_t>
и std::uniform_int_distribution<int8_t>
?
Ах, всегда неприятная проблема «унифицированное распределение int не специализировано для char/uint8_t». Я действительно думаю, что это должно быть зарегистрировано как дефект стандарта C++, если это еще не было сделано. Кажется довольно произвольным не поддерживать это (даже если реализация просто оборачивает это для uint16_t
и приводит результат, что я делаю сейчас, когда мне нужны случайные байты).
@JonathanWakely В ссылке, которой поделился phuclv, вы цитируете слова «Однобайтовые целые числа не поддерживаются намеренно, а не случайное упущение, и поэтому мы должны быть осторожны, просто изменяя это, не консультируясь с разработчиками C++ 11». И, поскольку отчет о дефекте был закрыт как «не дефект», интересно, по какой первоначальной причине не были включены однобайтовые целые числа? Я предполагаю, что те из вас, кто обрабатывал отчет о дефектах, получили мотивацию от разработчиков C++11.
@saxbophone Отчет о дефекте был отправлен и закрыт как «не дефект», как видно из ссылки, которой поделился phuclv.
@JonathanWakely Я нашел ответ на свой вопрос в комментарий Брайану Би, который спросил то же самое: "нет, но если бы мне пришлось угадывать, это потому, что символы не являются целыми числами, а целые числа не являются символами".
Хотя, как указывает другой ответ, в соответствии со стандартом создание экземпляра std::uniform_int_distribution
с аргументом шаблона uint8_t
является неопределенным поведением, предупреждение UBsan здесь не связано с этим.
UBsan помечает реализацию самого вихря Мерсенна, но реализация не имеет неопределённого поведения или ошибок.
Если вы внимательно посмотрите, то увидите, что оскорбительное выражение
_M_x[__k + (__m - __n)]
где __k
— значение в диапазоне от (__n - __m)
до (__n - 1)
через цикл for
.
Все типы, участвующие в этих операциях, являются std::size_t
беззнаковыми. Как следствие, все эти операции используют модульную арифметику, и, следовательно, даже если __m - __n
отрицательно и не может быть представлено в беззнаковом типе, результат
__k + (__m - __n)
будет лежать между 0
и __m - 1
, так что индексация массива с ним не проблема. Никакого неопределенного поведения, неопределенного поведения или поведения, определяемого реализацией, не задействовано.
Проверка UBSan, которая помечает это, не помечает фактическое неопределенное поведение. Совершенно нормально полагаться на циклическое поведение беззнаковой арифметики, подобное этому, если кто-то знает об этом. Проверка неподписанного переполнения предназначена только для пометки случаев такого переноса, когда это не было преднамеренным. Вы не должны использовать его в чужом коде, который может полагаться на него, или в своем собственном коде, если вы можете на него полагаться.
В -fsanitize=address,undefined,nullability,implicit-integer-truncation,implicit-integer-arithmetic-value-change,implicit-conversion,integer
все, кроме address
и undefined
, включают проверки UBsan, которые не отмечают фактическое неопределенное поведение, но во многих случаях обусловливают непреднамеренность май. Флаг дезинфицирующего средства по умолчанию -fsanitize=undefined
не включает проверку переполнения целого числа без знака по умолчанию по причинам, указанным выше. Подробнее см. в https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html.
Тогда сообщение вводит в заблуждение SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ...
@HFTrader Я думаю, они могли бы сформулировать сообщение лучше и использовать для этой проверки что-то еще, кроме «неопределенного поведения», но вы можете получить это сообщение, только если намеренно включите проверку, которую обычно не следует включать, поэтому я думаю, что один можно ожидать, что пользователь будет знать фактическое значение. Возможно, стоит опубликовать отчет об ошибке или запрос функции, чтобы исправить формулировку.
Меньшее значение mre: godbolt.org/z/9T6nqxdvs . Треки к этому: -fsanitize=unsigned-integer-overflow: Unsigned integer overflow, where the result of an unsigned integer computation cannot be represented in its type. Unlike signed integer overflow, this is not undefined behavior, but it is often unintentional. This sanitizer does not check for lossy implicit conversions performed before such a computation
Хотя я согласен с тем, что результат будет правильным, тот факт, что собственная libc++ clang не показывает такого поведения, говорит мне, что это не предназначено и работает случайно.
@MadFred Я уверен, что авторы libstdc++ знают, что делают, и что эта конструкция создана намеренно. Если вы посмотрите с точки зрения, что арифметические операторы выполняют модульную арифметику вместо ограниченной арифметики, такой код имеет смысл и на самом деле часто более элегантен, чем работа в ограниченной арифметике.
@MadFred Libc++ также не избегает этого полностью. Вместо этого он использует атрибуты для подавления проверки переполнения целого числа без знака там, где на него полагаются. Найдите _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
в исходном коде libc++. Libstdc++ не беспокоится о добавлении таких аннотаций, потому что они не поддерживают использование проверки с самого начала. Это просто разница в подходе между разработчиками Clang и GCC.
Я бы с вами согласился, ЕСЛИ не было бы проще написать вместо этого (__k+__m)-__n
. Единственная опасность этого — беззнаковое переполнение k+m
, но это кажется невозможным, учитывая, что они являются индексами в массиве. Я считаю, что это просто недосмотр, который прошел тесты.
@MadFred Дело в том, что сложение в модульной арифметике ассоциативно. Неважно, где вы ставите скобки. Я предполагаю, что они размещены так, как они есть, чтобы указать, что этот индекс перемещается по массиву, сдвинутому на __m-__n
из диапазона, в котором сам __k
перемещается. Я думаю, в этом отношении это выглядело бы лучше, чем __k - (__n - __m)
, но на самом деле это не имеет значения.
Я серьезно подумал о том, чтобы поставить принятый флаг на ваш ответ, поскольку он так же хорош и вдумчив, как и ответ выше. Но поскольку на самом деле это не было проблемой, все сводилось только к выбору времени.
@MadFred Если вы сомневаетесь, какой ответ лучше, я с радостью отдам свой ответ этому. Мне нравится мой собственный ответ как руководство к ответу «как решить проблему». Этот ответ копается в нем. Я был бы не против, если бы вы изменили свой голос. Кстати, я дал этому ответу свой голос, как только увидел его.
@TedLyngmo Вот в чем дело: вы помогли мне найти проблему, и это имеет наибольшую ценность, по крайней мере, для меня, и вот мое благодарственное голосование. Админы могут не согласиться.
@MadFred Администраторы не решают, какой ответ лучше. Я просто хотел, чтобы вы знали, что мне очень понравился этот ответ и его глубокий анализ, и решение назвать это принятым было бы разумным. Мне все еще нравится мой ответ, и я рад, что он вам тоже понравился.
unsigned
типы имеют четко определенное поведение переноса в C++. Это одна из причин, по которой они используются в PRNG и других случаях использования битовых манипуляций, где это желательно и ожидается (и необходимо для алгоритма), а не ошибка.
Разработчики GCC правы: неразумно рассматривать неподписанную упаковку все как проблему. Еще более неразумно распечатывать, что это «неопределенное поведение», а не проблема возможный. Если бы ubsan clang сказал вам в первую очередь, что он четко определен в C++ и, возможно, предназначен, вам не пришлось бы беспокоить разработчиков GCC отчетом об ошибке, который им не был полезен. Или вы могли бы сформулировать это как запрос функции после понимания проблемы.
Но вы также правы: с библиотечными функциями в заголовках, где они становятся частью вашего собственного кода, очень сложно отделить библиотечный код (такой как этот PRNG) от вашего собственного кода, когда он встраивается в ту же единицу компиляции. И параметры ubsan для каждого файла.
Реализация libС++ mt19937 отключает эту проверку ubsan, где это необходимо. Это более поздняя реализация стандартной библиотеки C++ с чистого листа, разработанная как часть LLVM и в основном используемая с clang. Если бы какая-либо библиотека заголовков собиралась обслуживать этот санитайзер, который помечает некоторые допустимые операции C++ как проблемы, то это была бы libc++. https://godbolt.org/z/aeY5Yn9c6 показывает, что добавление -stdlib=libc++
к параметрам компиляции в Godbolt позволяет вашему тестовому сценарию работать без сбоев. Вам нужно будет установить его локально, чтобы фактически использовать его.
libc++ определяет макрос препроцессора _LIBCPP_DISABLE_UBSAN_UNSIGNED_INTEGER_CHECK
как __attribute__((__no_sanitize__("unsigned-integer-overflow")))
(если поддерживается), поэтому он может отключить его для каждой функции. См., например, <utility>
заголовок libcxx, где различные функции используют этот тег, и mersenne_twister_engine<...>::seed()
в <random>
. Но что интересно, он используется не везде, так что вы все равно можете воспользоваться проверкой переполнения.
Или вы можете написать функцию-оболочку вокруг генерации случайных чисел и поместить ее в отдельный .cpp
, который вы компилируете без sanitize=integer
. В сборке релиза с -flto
он все еще может быть встроен. Или, если вам не нужна такая качественная случайность, используйте libc random(3)
; он компилируется отдельно, а не встроенный заголовок. random()
Linux не ужасен, хотя и не велик. Другие PRNG, такие как xorshift / хороширо, быстры и хороши, но также будут использовать типы unsigned
и полагаться на их обертку для умножения и/или добавления/подчинения, если только они не используют только сдвиги и xor, как LFSR.
Невозможно пометить только некоторые неподписанные операции как ожидаемые в ISO C++.
По крайней мере, один язык, Rust, делает решают эту проблему: переполнение диапазона значений всегда является ошибкой для простых +
, -
, *
, /
и т. д. для любого целочисленного типа, включая знаковый и беззнаковый. Вы можете использовать x.wrapping_sub(y) для выполнения знакового или беззнакового вычитания с четко определенным переходом. Аналогично для add/mul/div/rem/shift/pow. И есть saturating_sub/add/etc, и overflowing_..., который возвращает обернутый результат и логическое значение, или checked_add/sub/etc, который возвращает тип, который может быть None вместо того, чтобы содержать целое число. Так что, если вы хотите возиться с целочисленными переполнениями, Rust может быть языком для вас.
(Я не удивлюсь, если внутренняя проверка LLVM на неподписанное переполнение была частично мотивирована Rust, и кто-то подумал, что иногда может быть полезно выставить это для использования в C++. Но ожидайте ложных срабатываний в коде, написанном без этой проверки в разум.)
GCC/Clang и другие компиляторы, которые понимают диалект GNU C и C++, имеют встроенные функции целочисленного переполнения. Это включает в себя как signed
, так и unsigned
обертку add/sub/mul. Но только для (без подписи) int
/long
/long long
; вам нужно будет выяснить, какой из них использовать для size_t
в libstdС++. (например, в Windows x64 size_t
должно быть long long
, а в x86-64 System V это long
)
unsigned long wrapping_sub(unsigned long x, unsigned long y)
{
// return x - y; // ISO C++ without working around sanitize=integer
unsigned long res;
bool borrow = __builtin_usubl_overflow(x, y, &res);
return res;
}
Тестовый случай на Godbolt показывает, что __builtin_usubl_overflow
безопасно выполняет вычитание с переносом 1UL, 2UL
. (Создание asm, который даже не пытается обнаружить перенос, потому что мы сказали компилятору, что это не ошибка в этой операции.) Раскомментирование return x-y;
перекрывает переполнение.
Было бы очень громоздко использовать это для каждой операции без знака в коде стандартной библиотеки, где упаковка не является ошибкой, поэтому libc++ вместо этого отключает дезинфицирующее средство для упаковки без знака для каждой функции.
Поскольку беззнаковая математика четко определена как упаковка, обычной причиной использования неподписанных версий этих встроенных функций GNU C является захват вывода переноса/заимствования, поэтому вы знаете, что если они обернули. Вместо того, чтобы использовать sanitize=integer
clang, вы можете использовать эти функции в своих операциях собственныйunsigned
и assert()
, что результат логического значения является ложным (без переполнения переноса).
Clang поддерживает внешний список игнорирования дезинфицирующего средства, основанный на искаженном имени, а также с подстановочными знаками (clang.llvm.org/docs/SanitizerSpecialCaseList.html), поэтому я думаю, что можно просто добавить в него оскорбительные стандартные библиотечные функции или даже полностью игнорировать стандартные библиотечные функции C++ с несколькими записями форма fun:_ZSt*
, fun:_ZNSt*
и т. д. Или, возможно, записи в исходном файле также работают для экземпляров шаблона (не тестировалось), и может быть достаточно добавить запись формы src:
с подстановочным знаком к заголовкам стандартной библиотеки.
Mersenne Twister был находкой, когда появился 25 лет назад, но у него есть несколько недостатки. В наши дни я бы серьезно рекомендовал более современный PRNG, например, что-то из семейств ПКГ или xorshift.
@Peter Cordes Rust по умолчанию обнаружит этот случай в нерелизном коде. Когда что-то было сделано по умолчанию на таком строгом языке, как Rust, вы знаете, что этот вопрос очень важен для пользователя.
@MadFred: Вы все еще утверждаете, что людям следует избегать написания кода на C++, который намеренно выполняет беззнаковую математику с оберткой по модулю, даже если это то, что им нужно? Как вы предлагаете написать libstdc++? С __builtin_usubl_overflow
вместо -
в этой и многих других функциях? Или со специфичным для clang __attribute__(())
, контролируемым #ifdef
, как это использует libc++? Или что этот случай должен был привести к ssize_t
и обратно для вычитания со знаком?
@MadFred: недавно разработанный язык, такой как Rust, имеет хорошие функции, которые позволяют более четко указывать безопасность целочисленного переполнения. Это не то, о чем думали разработчики C в начале 70-х, когда язык разрабатывался, а C++ наследует его семантику. Как я объяснил в своем ответе, ISO C++ не имеет способа указать, что перенос ожидается только для некоторых операций. Если вам не нравится, как работает общий C++, не используйте его. Или используйте только библиотеки (например, libc++), которые обслуживают то подмножество, которое вы хотите использовать, где вы определяете неподписанную упаковку как недопустимую.
@PeterCordes Как вы считаете (как и команда GCC), что эта проверка (так в оригинале) «глупая» и ее следует удалить из инструментов clang?
@MadFred: Нет, это полезно иметь, если вы понимаете последствия его использования в мире C++. Как я уже сказал в своем ответе, глупо утверждать, что существует «неопределенное поведение», что вводит в заблуждение и сбивает с толку, и именно это привело к разрыву между вами и командой GCC. Вместо того, чтобы вежливо попросить их обойти дезинфицирующее средство clang, чтобы вы могли использовать его с libstdc++, вы сообщили о не-ошибке с заголовком ошибки, говоря, что это было неопределенное поведение, и предположив, что то, что делает код, было на самом деле неправильным. Естественно, они отнесутся к этому с пренебрежением.
@MadFred: Кроме того, как указывает пользователь17732522, clang.llvm.org/docs/SanitizerSpecialCaseList.html документирует, как вы можете добавить список игнорируемых функций, чтобы вы могли отфильтровать функции libstdc++, которые могут вызвать ложные срабатывания с этим дезинфицирующим средством.
Мне кажется, это скорее ложное срабатывание дезинфицирующего средства UB.