diff options
author | KJ Tsanaktsidis <ktsanaktsidis@zendesk.com> | 2024-01-22 12:06:03 +1100 |
---|---|---|
committer | KJ Tsanaktsidis <kj@kjtsanaktsidis.id.au> | 2024-01-22 14:34:31 +1100 |
commit | 6c0e58a54e3fda604386d9c409e2a9998bbc9352 (patch) | |
tree | 653b9271c7182c83bfb7fe4d6e87e74ab4334cc4 /ext/socket | |
parent | ce5e7629b57904c34a529372f365e04d4f9abb06 (diff) |
Make sure the correct error is raised for EAI_SYSTEM resolver fail
In case of EAI_SYSTEM, getaddrinfo is supposed to set more detail in
errno; however, because we call getaddrinfo on a thread now, and errno
is threadlocal, that information is being lost. Instead, we just raise
whatever errno happens to be on the calling thread (which can be
something very confusing, like `ECHILD`).
Fix it by explicitly propagating errno back to the calling thread
through the getaddrinfo_arg structure.
[Bug #20198]
Diffstat (limited to 'ext/socket')
-rw-r--r-- | ext/socket/raddrinfo.c | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/ext/socket/raddrinfo.c b/ext/socket/raddrinfo.c index 560312741f..cdb785f268 100644 --- a/ext/socket/raddrinfo.c +++ b/ext/socket/raddrinfo.c @@ -345,7 +345,7 @@ struct getaddrinfo_arg char *node, *service; struct addrinfo hints; struct addrinfo *ai; - int err, refcount, done, cancelled; + int err, gai_errno, refcount, done, cancelled; rb_nativethread_lock_t lock; rb_nativethread_cond_t cond; }; @@ -406,8 +406,9 @@ do_getaddrinfo(void *ptr) { struct getaddrinfo_arg *arg = (struct getaddrinfo_arg *)ptr; - int err; + int err, gai_errno; err = getaddrinfo(arg->node, arg->service, &arg->hints, &arg->ai); + gai_errno = errno; #ifdef __linux__ /* On Linux (mainly Ubuntu 13.04) /etc/nsswitch.conf has mdns4 and * it cause getaddrinfo to return EAI_SYSTEM/ENOENT. [ruby-list:49420] @@ -420,6 +421,7 @@ do_getaddrinfo(void *ptr) rb_nativethread_lock_lock(&arg->lock); { arg->err = err; + arg->gai_errno = gai_errno; if (arg->cancelled) { freeaddrinfo(arg->ai); } @@ -479,7 +481,7 @@ rb_getaddrinfo(const char *hostp, const char *portp, const struct addrinfo *hint { int retry; struct getaddrinfo_arg *arg; - int err; + int err, gai_errno; start: retry = 0; @@ -503,6 +505,7 @@ start: { if (arg->done) { err = arg->err; + gai_errno = arg->gai_errno; if (err == 0) *ai = arg->ai; } else if (arg->cancelled) { @@ -525,6 +528,10 @@ start: rb_thread_check_ints(); if (retry) goto start; + /* Because errno is threadlocal, the errno value we got from the call to getaddrinfo() in the thread + * (in case of EAI_SYSTEM return value) is not propagated to the caller of _this_ function. Set errno + * explicitly, as round-tripped through struct getaddrinfo_arg, to deal with that */ + errno = gai_errno; return err; } @@ -591,7 +598,7 @@ struct getnameinfo_arg size_t hostlen; char *serv; size_t servlen; - int err, refcount, done, cancelled; + int err, gni_errno, refcount, done, cancelled; rb_nativethread_lock_t lock; rb_nativethread_cond_t cond; }; @@ -644,12 +651,14 @@ do_getnameinfo(void *ptr) { struct getnameinfo_arg *arg = (struct getnameinfo_arg *)ptr; - int err; + int err, gni_errno; err = getnameinfo(arg->sa, arg->salen, arg->host, (socklen_t)arg->hostlen, arg->serv, (socklen_t)arg->servlen, arg->flags); + gni_errno = errno; int need_free = 0; rb_nativethread_lock_lock(&arg->lock); arg->err = err; + arg->gni_errno = gni_errno; if (!arg->cancelled) { arg->done = 1; rb_native_cond_signal(&arg->cond); @@ -691,7 +700,7 @@ rb_getnameinfo(const struct sockaddr *sa, socklen_t salen, { int retry; struct getnameinfo_arg *arg; - int err; + int err, gni_errno; start: retry = 0; @@ -714,6 +723,7 @@ start: rb_nativethread_lock_lock(&arg->lock); if (arg->done) { err = arg->err; + gni_errno = arg->gni_errno; if (err == 0) { if (host) memcpy(host, arg->host, hostlen); if (serv) memcpy(serv, arg->serv, servlen); @@ -738,6 +748,9 @@ start: rb_thread_check_ints(); if (retry) goto start; + /* Make sure we copy the thread-local errno value from the getnameinfo thread back to this thread, so + * calling code sees the correct errno */ + errno = gni_errno; return err; } |