tdweb 1.8.52 - out of bounds on downloadFile
Browser - google chrome v139.0.7258.66
Hello, I encountered an issue when using TDLib in the browser, which throws the following error.
{
"@type": "updateFatalError",
"error": {
message: ''memory access out of bounds'',
stack: "'RuntimeError: memory access out of bounds\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[45806]:0x8b2030\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[1682]:0x52aaf\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[12473]:0x2e6cbb\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[19319]:0x45a688\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[20557]:0x497946\n at http://localhost:3001/3954d526ee7f66ea79957ddbbc4f4d44.wasm:wasm-function[45541]:0x895dbe\n at ccall (http://localhost:3001/1.b5452fdafbf71399f7a9.worker.js:2585:4712)\n at Object.td_receive (http://localhost:3001/1.b5452fdafbf71399f7a9.worker.js:2585:5102)\n at TdClient.receive (http://localhost:3001/b5452fdafbf71399f7a9.worker.js:5482:39)\n at http://localhost:3001/b5452fdafbf71399f7a9.worker.js:5534:23'"
}
}
I couldn't find a 100% reproducible way to trigger it, but if you send the downloadFile event, sooner or later this error occurs. I noticed some strange behavior: it happens after the page reloads, meaning after authorization, the download works fine and nothing breaks, but as soon as the page reloads, problems start.
Can you use addr2line for wasm to convert the stacktrace to the list of actual source code locations?
You can find the corresponding tool and documentation at https://github.com/bytecodealliance/wasm-tools.
Can you use addr2line for wasm to convert the stacktrace to the list of actual source code locations?
I had to build tdlib in debug mode using DCMAKE_BUILD_TYPE=debug instead of DCMAKE_BUILD_TYPE=MinSizeRel.
Here is console stack trace
"RuntimeError: memory access out of bounds\n at std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__node_insert_multi_prepare(unsigned long, std::__2::__hash_value_type<unsigned long long, int>&) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[296479]:0x3985b97)\n at std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__node_insert_multi(std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[296467]:0x39853c0)\n at std::__2::__hash_iterator<std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*> std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__emplace_multi<unsigned long long&, int>(unsigned long long&, int&&) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[294364]:0x3920960)\n at std::__2::__hash_map_iterator<std::__2::__hash_iterator<std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*> > std::__2::unordered_multimap<unsigned long long, int, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, std::__2::allocator<std::__2::pair<unsigned long long const, int> > >::emplace<unsigned long long&, int>(unsigned long long&, int&&) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[294357]:0x391e880)\n at td::Td::request(unsigned long long, td::tl::unique_ptr<td::td_api::Function>) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[294350]:0x391e401)\n at auto td::detail::mem_call_tuple_impl<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function>, 1ul, 2ul>(td::Td*, std::__2::tuple<void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >&&, td::detail::IntSeq<0ul, 1ul, 2ul>) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[35578]:0x7c4edf)\n at auto td::mem_call_tuple<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >(td::Td*, std::__2::tuple<void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >&&) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[35577]:0x7c4d8b)\n at td::DelayedClosure<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long&, td::tl::unique_ptr<td::td_api::Function>&&>::run(td::Td*) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[35561]:0x7c4806)\n at td::ClosureEvent<td::DelayedClosure<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long&, td::tl::unique_ptr<td::td_api::Function>&&> >::run(td::Actor*) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[35560]:0x7c47a4)\n at td::Scheduler::do_event(td::ActorInfo*, td::Event&&) (http://localhost:3001/73d4f25b0b99bfee3417ee62e17037ba.wasm:wasm-function[676]:0xb327e)"
And here is wasm-tools addr2line output
wasm-tools addr2line 73d4f25b0b99bfee3417ee62e17037ba.wasm 0x3985b97 0x39853c0 0x3920960 0x391e880 0x391e401 0x7c4edf 0x7c4d8b 0x7c4806 0x7c47a4 0xb327e
0x3985b97: std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__node_insert_multi_prepare(unsigned long, std::__2::__hash_value_type<unsigned long long, int>&) /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__hash_table:1952:42
0x39853c0: std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__node_insert_multi(std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*) /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__hash_table:2017:27
0x3920960: std::__2::__hash_iterator<std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*> std::__2::__hash_table<std::__2::__hash_value_type<unsigned long long, int>, std::__2::__unordered_map_hasher<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, true>, std::__2::__unordered_map_equal<unsigned long long, std::__2::__hash_value_type<unsigned long long, int>, std::__2::equal_to<unsigned long long>, std::__2::hash<unsigned long long>, true>, std::__2::allocator<std::__2::__hash_value_type<unsigned long long, int> > >::__emplace_multi<unsigned long long&, int>(unsigned long long&, int) /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__hash_table:2150:20
0x391e880: std::__2::__hash_map_iterator<std::__2::__hash_iterator<std::__2::__hash_node<std::__2::__hash_value_type<unsigned long long, int>, void*>*> > std::__2::unordered_multimap<unsigned long long, int, std::__2::hash<unsigned long long>, std::__2::equal_to<unsigned long long>, std::__2::allocator<std::__2::pair<unsigned long long const, int> > >::emplace<unsigned long long&, int>(unsigned long long&, int) /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/unordered_map:2069:25
0x391e401: td::Td::request(unsigned long long, td::tl::unique_ptr<td::td_api::Function>) /home/mikhail/projects/td/td/telegram/Td.cpp:283:16
0x7c4edf: auto td::detail::mem_call_tuple_impl<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function>, (unsigned long)1, (unsigned long)2>(td::Td*, std::__2::tuple<void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >&&, td::detail::IntSeq<(unsigned long)0, ((unsigned long)1, (unsigned long)2)...>) /home/mikhail/projects/td/tdutils/td/utils/invoke.h:130:10
0x7c4d8b: auto td::mem_call_tuple<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >(td::Td*, std::__2::tuple<void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long, td::tl::unique_ptr<td::td_api::Function> >&&) /home/mikhail/projects/td/tdutils/td/utils/invoke.h:165:10
0x7c4806: td::DelayedClosure<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long&, td::tl::unique_ptr<td::td_api::Function>&&>::run(td::Td*) /home/mikhail/projects/td/tdutils/td/utils/Closure.h:109:12
0x7c47a4: td::ClosureEvent<td::DelayedClosure<td::Td, void (td::Td::*)(unsigned long long, td::tl::unique_ptr<td::td_api::Function>), unsigned long long&, td::tl::unique_ptr<td::td_api::Function>&&> >::run(td::Actor*) /home/mikhail/projects/td/tdactor/td/actor/impl/Event.h:61:14
0xb327e: td::Scheduler::do_event(td::ActorInfo*, td::Event&&) /home/mikhail/projects/td/tdactor/td/actor/impl/Scheduler.cpp:294:32
Thank you.
The trace points to the line
request_set_.emplace(id, function->get_id());
and function isn't null there. I see no way for this code to produce out-of-bounds memory access, unless memory used for the variable request_set_ has already been corrupted earlier. Therefore, it gives no clues about the cause of the issue.
You can try to build tdweb with address sanitizer as described at https://emscripten.org/docs/debugging/Sanitizers.html#address-sanitizer to check whether it can catch the real cause.
I tried building TDLib with the -fsanitize=address flag, but unfortunately, this flag seems to fix the out-of-bounds problem, so I was unable to reproduce the issue with this sanitizer. However, I then built TDLib with the -fsanitize=undefined flag, which enables UBSan (Undefined Behavior Sanitizer), and I immediately saw problems reported in the console when launching the application in the browser for the very first time—two errors were printed right away.
wasm-tools addr2line output:
0x1568f58: std::__2::__wrap_iter<td::tl::unique_ptr<td::telegram_api::foundStory> const*>::operator*() const /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__iterator/wrap_iter.h:92:9
td::telegram_api::stories_foundStories::store(td::TlStorerToString&, char const*) const /home/mikhail/projects/td/td/generate/auto/td/telegram/telegram_api.cpp:49376:81
wasm-tools addr2line output:
0x1568cf8: td::telegram_api::stories_canSendStoryCount::store(td::TlStorerToString&, char const*) const /home/mikhail/projects/td/td/generate/auto/td/telegram/telegram_api.cpp:49336:0
after that i've tried to reproduce out of bounds error after login and page reload and here is what i got. I added screenshots of console and full console log as .log file
And here is wasm-tools addr2line output:
0x15c38ec: td::detail::NarrowCast::NarrowCast(char const*, int) /home/mikhail/projects/td/tdutils/td/utils/misc.h:298:44
void td::TlStoreVector<td::TlStoreBoxedUnknown<td::TlStoreObject> >::store<std::__2::vector<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId>, std::__2::allocator<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId> > >, td::TlStorerUnsafe>(std::__2::vector<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId>, std::__2::allocator<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId> > > const&, td::TlStorerUnsafe&) /home/mikhail/projects/td/td/tl/tl_object_store.h:71:25
0x15c3904: void td::TlStoreVector<td::TlStoreBoxedUnknown<td::TlStoreObject> >::store<std::__2::vector<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId>, std::__2::allocator<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId> > >, td::TlStorerUnsafe>(std::__2::vector<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId>, std::__2::allocator<td::tl::unique_ptr<td::telegram_api::StarGiftAttributeId> > > const&, td::TlStorerUnsafe&) /home/mikhail/projects/td/td/tl/tl_object_store.h:71:25
0x1568cf8: td::telegram_api::stories_canSendStoryCount::store(td::TlStorerToString&, char const*) const /home/mikhail/projects/td/td/generate/auto/td/telegram/telegram_api.cpp:49336:0
0x1568f58: std::__2::__wrap_iter<td::tl::unique_ptr<td::telegram_api::foundStory> const*>::operator*() const /home/mikhail/emsdk/upstream/emscripten/cache/sysroot/include/c++/v1/__iterator/wrap_iter.h:92:9
td::telegram_api::stories_foundStories::store(td::TlStorerToString&, char const*) const /home/mikhail/projects/td/td/generate/auto/td/telegram/telegram_api.cpp:49376:81
Thank you for the investigation.
Those are mostly false positives. Downcast of address and reference binding with insufficient spase are well-defined if the resulting object isn't accessed afterwards, which is the case. The sanitizer complains, because it sees the reference being passed to a function, but can't check whether the object is accessed though the reference in the function, which doesn't happen.
I fixed integer overflow at OrderedMessage.cpp:15, but it was unlikely to be a cause of any issue.
Misaligned stores in SQLite also shouldn't cause any issues, because they are supported by WebAssembly.
The provided addr2line output is unrelated, which is likely to be caused by usage of debug symbols from a different build. But it is fine, because file name and line number are printed by the sanitizer itself.
If build with address sanitizer works, you can try to use it instead. The issue may still be present, in which case it can be found by the sanitizer. If it isn't, then you will have a working code at least.