1
0
mirror of synced 2025-10-21 14:13:38 +03:00

Fix UB by use of dangling references in getaddrinfo_with_timeout (#2232)

* Fix use of dangling references

When the resolve thread is detached, local variables were still used, which could lead to a program crash.

* Add test to verify dangling ref fix

* Add missing brace initialization

* Assert that the remaining fields are really zeroed

* Fix use of chrono literals
This commit is contained in:
Jonas van den Berg
2025-09-15 02:05:09 +02:00
committed by GitHub
parent f72b4582e6
commit 6e52d0a057
2 changed files with 61 additions and 16 deletions

View File

@@ -3798,31 +3798,55 @@ inline int getaddrinfo_with_timeout(const char *node, const char *service,
} }
#else #else
// Fallback implementation using thread-based timeout for other Unix systems // Fallback implementation using thread-based timeout for other Unix systems
std::mutex result_mutex;
std::condition_variable result_cv;
auto completed = false;
auto result = EAI_SYSTEM;
struct addrinfo *result_addrinfo = nullptr;
std::thread resolve_thread([&]() { struct GetAddrInfoState {
auto thread_result = getaddrinfo(node, service, hints, &result_addrinfo); std::mutex mutex{};
std::condition_variable result_cv{};
bool completed{false};
int result{0};
std::string node{};
std::string service{};
struct addrinfo hints {};
struct addrinfo *info{nullptr};
};
std::lock_guard<std::mutex> lock(result_mutex); // Allocate on the heap, so the resolver thread can keep using the data.
result = thread_result; auto state = std::make_shared<GetAddrInfoState>();
completed = true;
result_cv.notify_one(); state->result = EAI_SYSTEM;
state->node = node;
state->service = service;
state->hints.ai_flags = hints->ai_flags;
state->hints.ai_family = hints->ai_family;
state->hints.ai_socktype = hints->ai_socktype;
state->hints.ai_protocol = hints->ai_protocol;
// The remaining fields of "hints" must be zeroed, so do not copy them.
assert(hints->ai_addrlen == 0);
assert(hints->ai_addr == nullptr);
assert(hints->ai_canonname == nullptr);
assert(hints->ai_next == nullptr);
std::thread resolve_thread([=]() {
auto thread_result = getaddrinfo(
state->node.c_str(), state->service.c_str(), hints, &state->info);
std::lock_guard<std::mutex> lock(state->mutex);
state->result = thread_result;
state->completed = true;
state->result_cv.notify_one();
}); });
// Wait for completion or timeout // Wait for completion or timeout
std::unique_lock<std::mutex> lock(result_mutex); std::unique_lock<std::mutex> lock(state->mutex);
auto finished = result_cv.wait_for(lock, std::chrono::seconds(timeout_sec), auto finished =
[&] { return completed; }); state->result_cv.wait_for(lock, std::chrono::seconds(timeout_sec),
[&] { return state->completed; });
if (finished) { if (finished) {
// Operation completed within timeout // Operation completed within timeout
resolve_thread.join(); resolve_thread.join();
*res = result_addrinfo; *res = state->info;
return result; return state->result;
} else { } else {
// Timeout occurred // Timeout occurred
resolve_thread.detach(); // Let the thread finish in background resolve_thread.detach(); // Let the thread finish in background

View File

@@ -1331,6 +1331,27 @@ TEST(RangeTest, FromHTTPBin_Online) {
} }
} }
TEST(GetAddrInfoDanglingRefTest, LongTimeout) {
auto host = "unresolvableaddress.local";
auto path = std::string{"/"};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
auto port = 443;
SSLClient cli(host, port);
#else
auto port = 80;
Client cli(host, port);
#endif
cli.set_connection_timeout(1);
{
auto res = cli.Get(path);
ASSERT_FALSE(res);
}
std::this_thread::sleep_for(std::chrono::seconds(8));
}
TEST(ConnectionErrorTest, InvalidHost) { TEST(ConnectionErrorTest, InvalidHost) {
auto host = "-abcde.com"; auto host = "-abcde.com";