https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120915
Bug ID: 120915 Summary: Possible -fsanitize=pointer-subtract false positive Product: gcc Version: 15.1.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: analyzer Assignee: dmalcolm at gcc dot gnu.org Reporter: christian.morales.vega at gmail dot com Target Milestone: --- Using Boost 1.88, using this simple code ``` #include <boost/json/src.hpp> int main() { const auto payload = boost::json::serialize(boost::json::object{{"field", "value"}}); } ``` built with `g++ -g -o test test.cpp -I boost_1_88_0 -fsanitize=address -fsanitize=pointer-subtract` and run as `ASAN_OPTIONS="detect_invalid_pointer_pairs=1" ./test` you get ``` $ ASAN_OPTIONS="detect_invalid_pointer_pairs=1" ./test ================================================================= ==20812==ERROR: AddressSanitizer: invalid-pointer-pair: 0x7998dc601040 0x7998dc600042 #0 0x000000435ac5 in boost::json::detail::stream::remain() const boost_1_88_0/boost/json/detail/stream.hpp:265 #1 0x000000449789 in bool boost::json::detail::do_write_string<true>(boost::json::detail::writer&, boost::json::detail::stream&) boost_1_88_0/boost/json/impl/serializer.ipp:228 #2 0x000000414960 in boost::json::detail::write_string(boost::json::detail::writer&, boost::json::detail::stream&) boost_1_88_0/boost/json/impl/serializer.ipp:348 #3 0x00000044c86c in bool boost::json::detail::write_impl<boost::json::object, true>(boost::json::detail::map_like_conversion_tag, boost::json::detail::writer&, boost::json::detail::stream&) boost_1_88_0/boost/json/impl/serializer.hpp:336 #4 0x00000044c86c in bool boost::json::detail::write_object<true>(boost::json::detail::writer&, boost::json::detail::stream&) boost_1_88_0/boost/json/impl/serializer.ipp:380 #5 0x000000415678 in boost::json::serializer::read(char*, unsigned long) boost_1_88_0/boost/json/impl/serializer.ipp:548 #6 0x000000447e03 in boost::core::basic_string_view<char> boost::json::serializer::read<4096ul>(char (&) [4096ul]) boost_1_88_0/boost/json/serializer.hpp:292 #7 0x000000412b67 in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, boost::json::serializer&) boost_1_88_0/boost/json/impl/serialize.ipp:64 #8 0x000000413417 in boost::json::serialize[abi:cxx11](boost::json::object const&, boost::json::serialize_options const&) boost_1_88_0/boost/json/impl/serialize.ipp:147 #9 0x0000004272be in main /test/test.cpp:5 #10 0x7d98de2ae5f4 in __libc_start_call_main (/lib64/libc.so.6+0x35f4) (BuildId: 7504db94dbf054e06eaac49950f57161c601f5c6) #11 0x7d98de2ae6a7 in __libc_start_main@@GLIBC_2.34 (/lib64/libc.so.6+0x36a7) (BuildId: 7504db94dbf054e06eaac49950f57161c601f5c6) #12 0x0000004009b4 in _start (/test/test+0x4009b4) (BuildId: c19e3884d2a136258e826e7c13e90e801896f155) Address 0x7998dc601040 is located in stack of thread T0 at offset 4160 in frame #0 0x000000412a22 in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, boost::json::serializer&) boost_1_88_0/boost/json/impl/serialize.ipp:59 This frame has 2 object(s): [32, 48) 'sv' (line 63) [64, 4160) 'buf' (line 62) <== Memory access at offset 4160 overflows this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) Address 0x7998dc600042 is located in stack of thread T0 at offset 66 in frame #0 0x000000412a22 in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, boost::json::serializer&) boost_1_88_0/boost/json/impl/serialize.ipp:59 This frame has 2 object(s): [32, 48) 'sv' (line 63) [64, 4160) 'buf' (line 62) <== Memory access at offset 66 is inside this variable HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: invalid-pointer-pair boost_1_88_0/boost/json/detail/stream.hpp:265 in boost::json::detail::stream::remain() const ==20812==ABORTING ``` When using clang++ you get a similar ``` bash-5.2# ASAN_OPTIONS="detect_invalid_pointer_pairs=1" ./test ================================================================= ==16667==ERROR: AddressSanitizer: invalid-pointer-pair: 0x7a4a68801020 0x7a4a68800022 #0 0x000000535cca in boost::json::detail::stream::remain() const /test/boost_1_88_0/boost/json/detail/stream.hpp:265:21 #1 0x000000523083 in bool boost::json::detail::do_write_string<true>(boost::json::detail::writer&, boost::json::detail::stream&) /test/boost_1_88_0/boost/json/impl/serializer.ipp:228:19 #2 0x0000005036ec in boost::json::detail::write_string(boost::json::detail::writer&, boost::json::detail::stream&) /test/boost_1_88_0/boost/json/impl/serializer.ipp:348:12 #3 0x0000005263cb in bool boost::json::detail::write_impl<boost::json::object, true>(boost::json::detail::map_like_conversion_tag, boost::json::detail::writer&, boost::json::detail::stream&) /test/boost_1_88_0/boost/json/impl/serializer.hpp:336:16 #4 0x0000005263cb in bool boost::json::detail::write_object<true>(boost::json::detail::writer&, boost::json::detail::stream&) /test/boost_1_88_0/boost/json/impl/serializer.ipp:380:12 #5 0x00000050048c in boost::json::serializer::read(char*, unsigned long) /test/boost_1_88_0/boost/json/impl/serializer.ipp:548:9 #6 0x000000520cd1 in boost::core::basic_string_view<char> boost::json::serializer::read<4096ul>(char (&) [4096ul]) /test/boost_1_88_0/boost/json/serializer.hpp:292:16 #7 0x0000004ffff7 in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, boost::json::serializer&) /test/boost_1_88_0/boost/json/impl/serialize.ipp:64:13 #8 0x000000501208 in boost::json::serialize[abi:cxx11](boost::json::object const&, boost::json::serialize_options const&) /test/boost_1_88_0/boost/json/impl/serialize.ipp:147:5 #9 0x0000005160c4 in main /test/test.cpp:5:7 #10 0x7e4a6a1d75f4 in __libc_start_call_main (/lib64/libc.so.6+0x35f4) (BuildId: 7504db94dbf054e06eaac49950f57161c601f5c6) #11 0x7e4a6a1d76a7 in __libc_start_main@GLIBC_2.2.5 (/lib64/libc.so.6+0x36a7) (BuildId: 7504db94dbf054e06eaac49950f57161c601f5c6) #12 0x000000400b84 in _start (/test/test+0x400b84) (BuildId: 28faa094e25175e27e52d2c74ae0f585a02ece4d) Address 0x7a4a68801020 is located in stack of thread T0 at offset 4128 in frame #0 0x0000004ffebf in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, boost::json::serializer&) /test/boost_1_88_0/boost/json/impl/serialize.ipp:59 This frame has 2 object(s): [32, 4128) 'buf' (line 62) <== Memory access at offset 4128 overflows this variable [4256, 4272) 'sv' (line 63) HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) Address 0x7a4a68800022 is located in stack of thread T0 at offset 34 in frame #0 0x0000004ffebf in boost::json::detail::serialize_impl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&, boost::json::serializer&) /test/boost_1_88_0/boost/json/impl/serialize.ipp:59 This frame has 2 object(s): [32, 4128) 'buf' (line 62) <== Memory access at offset 34 is inside this variable [4256, 4272) 'sv' (line 63) HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: invalid-pointer-pair /test/boost_1_88_0/boost/json/detail/stream.hpp:265:21 in boost::json::detail::stream::remain() const ==16667==ABORTING ``` (I'm reporting it here because of https://github.com/google/sanitizers/issues/1860#issuecomment-2999555091) The Boost.JSON author thinks this is actually a bug in the sanitizer: https://github.com/boostorg/json/issues/1090#issuecomment-3022073666 The issue the sanitizer complains about seems to be just a simple function in a string_view like class. In https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/detail/stream.hpp#L265, it's using "one after the buffer". With "end" having been initialized to data + size in https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/detail/stream.hpp#L251. The object was created in https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/impl/serializer.ipp#L546 , via https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/serializer.hpp#L290 , from this array: https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/impl/serialize.ipp#L62. So nothing special here. The array would have been 4096 bytes long: https://github.com/boostorg/json/blob/boost-1.88.0/include/boost/json/detail/config.hpp#L141