[sanitizer] Close per-thread glibc resolver state on thread exit (#242571)
In glibc, DNS lookups (such as `getaddrinfo()` and `res_query()`)
allocate per-thread nameserver address structures in `_res`
(`_res._u._ext.nsaddrs`) and free them in `res_thread_freeres()` during
`__libc_thread_freeres()`, after pthread TSD destructors have finished.
Because ASan, LSan, and HWASan unregister the exiting thread in their
pthread TSD destructors during `__nptl_deallocate_tsd()`, LSan stops
scanning that thread's TLS before `res_thread_freeres()` runs. If a leak
check runs in that window (for example, when the main thread exits while
a detached worker thread is finishing), LSan reports a false-positive
28-byte leak (`sizeof(struct sockaddr_in6)`) from `__res_context_send`
or `update_from_conf`.
In `ClearDlerror()`, if `_res.nscount > 0`, call `res_nclose(&_res)` and
reset `_res.options = 0` (matching glibc's `res_thread_freeres()`, glibc
BZ #434) before unregistering the thread. Resetting `_res.options = 0`
ensures that if a subsequent TSD destructor invokes the resolver after
`res_nclose(&_res)` freed `nsaddrs` and detached `resolv_conf`,
`maybe_init()` re-initializes `_res` instead of dereferencing a null
`nsaddrs` pointer in `sock_eq()`.
Unlike a runtime LSan suppression (alternative to #238901, suggested by
@vitalybuka), freeing `_res` before thread unregistration avoids
symbolizer initialization when no other leaks are present, works without
`libc`/`libresolv` debug symbols (`symbolize=0`), and covers both IPv4
(`__res_context_send`) and IPv6 (`update_from_conf`) nameserver
allocations.
Assisted-by: Automated tooling, human reviewed.