GCC 10.3.0使用-fprofile-arcs触发stringop-overflow警告问题
GCC 10.3.0下-fprofile-arcs触发stringop-overflow警告的原因与解决方法
问题现象
在GCC 10.3.0环境下,使用-fprofile-arcs编译选项编译代码时,会触发stringop-overflow编译警告;移除该选项后警告消失。
编译命令及警告信息如下:
$ g++ test.cpp -O3 -W -Wall -Werror -fprofile-arcs -o test -std=c++17 -g In function ‘void memcpy_inlined(void*, const void*, size_t)’, inlined from ‘EncodedUtf8Char::EncodedUtf8Char(const char*, size_t)’ at test.cpp:46:23: test.cpp:16:29: error: writing 8 bytes into a region of size 4 [-Werror=stringop-overflow=] 16 | __builtin_memcpy(dst + size - 8, src + size - 8, 8); | ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test.cpp: In constructor ‘EncodedUtf8Char::EncodedUtf8Char(const char*, size_t)’: test.cpp:38:18: note: at offset 0 to object ‘EncodedUtf8Char::U::<unnamed struct>::bytes’ with size 4 declared here 38 | char bytes[4]; | ^~~~~ cc1plus: all warnings being treated as errors
对应代码
#include <cassert> #include <cmath> #include <functional> #include <list> #include <string> using namespace std; __always_inline void memcpy_inlined(void *__restrict _dst, const void *__restrict _src, size_t size) { auto dst = static_cast<uint8_t *>(_dst); auto src = static_cast<const uint8_t *>(_src); if (size <= 16) { if (size >= 8) { __builtin_memcpy(dst + size - 8, src + size - 8, 8); __builtin_memcpy(dst, src, 8); } else if (size >= 4) { __builtin_memcpy(dst + size - 4, src + size - 4, 4); __builtin_memcpy(dst, src, 4); } else if (size >= 2) { __builtin_memcpy(dst + size - 2, src + size - 2, 2); __builtin_memcpy(dst, src, 2); } else if (size >= 1) { *dst = *src; } } else { __builtin_memcpy(dst, src, size); } } struct EncodedUtf8Char { static constexpr uint32_t ABSENT_ENCODED = 0xffff'ffffU; union U { struct { char bytes[4]; } __attribute__((packed)); uint32_t value; } u; explicit EncodedUtf8Char(const char *p, size_t size) { u.value = ABSENT_ENCODED; memcpy_inlined(u.bytes, p, std::min(4UL, size)); } }; int main() { std::string str = "12345678"; EncodedUtf8Char encoded(str.data(), str.size()); return 0; }
-fprofile-arcs官方说明
Add code so that program flow arcs are instrumented. During execution the program records how many times each branch and call is executed and how many times it is taken or returns.
原因分析
是,-fprofile-arcs的分支插桩确实会导致这个警告,具体原因如下:
- 当使用返回
const size_t&的std::min时,结合-fprofile-arcs的插桩逻辑,GCC 10.x版本无法正确推导出std::min(4UL, size)的结果始终≤4。 - 编译器误判传入
memcpy_inlined的size可能大于4,进而触发stringop-overflow警告;若使用返回值类型为size_t的min实现,编译器能正确识别size的范围,不会触发警告。 - GCC 12及以上版本已修复该常量推导缺陷,即使使用返回引用的min也不会触发警告。
测试验证:
- GCC 10.x使用返回
const size_t&的min:触发警告 - GCC 10.x使用返回
size_t的min:无警告 - GCC 12使用返回
const size_t&的min:无警告
测试代码示例:
// 结合-fprofile-arcs会报stringop-overflow const size_t& min(const size_t &lhs, const size_t &rhs) { return lhs < rhs ? lhs : rhs; } // 结合-fprofile-arcs无警告 size_t min(const size_t &lhs, const size_t &rhs) { return lhs < rhs ? lhs : rhs; }
解决方案
要保留-fprofile-arcs选项并规避警告,可选择以下方法:
- 显式转换min结果为值类型:将
std::min(4UL, size)改为static_cast<size_t>(std::min(4UL, size)),让编译器能正确识别size的范围。 - 替换min实现:使用返回值类型而非引用类型的min函数,比如自定义一个返回
size_t的min,或者确保调用std::min的重载版本返回值类型。 - 手动限制size范围:在调用
memcpy_inlined前,将size赋值给局部变量并显式限制其≤4,例如:size_t copy_size = std::min(4UL, size); memcpy_inlined(u.bytes, p, copy_size); - 升级GCC版本:升级到GCC 12及以上,该版本已修复此推导问题。
- 临时禁用警告(不推荐):如果确认代码无越界风险,可针对相关代码块禁用
-Wstringop-overflow警告:#pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow=" memcpy_inlined(u.bytes, p, std::min(4UL, size)); #pragma GCC diagnostic pop
内容的提问来源于stack exchange,提问作者Zihe Liu
相关产品推荐
相关产品推荐

