Fix refcounting on thread creation failure path for android_res_nsend()

SocketClient instances are manually reference-counted. DnsProxyListener
passes them down to handler threads, and expects them to decrement the
reference coint when done.

On thread creation failure, tryThreadOrError() deletes the handler and
decrements the reference:

void tryThreadOrError(SocketClient* cli, T* handler) {
    const int rval = netdutils::threadLaunch(handler);
    if (rval == 0) {
        // SocketClient decRef() happens in the handler's run() method.
        return;
    }
    ...
    delete handler;  // Calls decRef() in ~ResNSendHandler!!!
    cli->decRef();
}

However, the assumption that decRef() is done in the handler's run()
method was not true in all cases: ResNSendHandler, added in Android Q,
actually decrements the client's reference count in its destructor.

Since tryThreadOrError() decrements the counter twice on the error path,
the SocketClient self-deletes too early, while it is still referenced by
the SocketListener.

Triggering this condition requires an unresponsive network and multiple
apps sending queries, until netd runs out of threads (each app is
limited to 256 concurrent queries by queryLimiter). At least one app
should be using android_res_nsend().

The fix consists in moving ownership of SocketClient to a base class of
all handlers, with strong encapsulation of the manual reference
counting, RAII-style. This simplifies DnsProxyListener while making
further refcounting bugs unlikely to occur in the future.

DnsProxyListener remains dangerously open to memory leaks in error paths
due to manual memory management. We should take care of this in a
followup change.

Bug: 169105756
Change-Id: I9241a094653651e1bdda79eb753e7a53e5e51d8f
2 files changed
tree: 506d915f3479140ca083fe080493c55d3329fdf3
  1. aidl_api/
  2. apex/
  3. binder/
  4. include/
  5. tests/
  6. .editorconfig
  7. Android.bp
  8. Dns64Configuration.cpp
  9. Dns64Configuration.h
  10. DnsProxyListener.cpp
  11. DnsProxyListener.h
  12. DnsQueryLog.cpp
  13. DnsQueryLog.h
  14. DnsQueryLogTest.cpp
  15. DnsResolver.cpp
  16. DnsResolver.h
  17. DnsResolverService.cpp
  18. DnsResolverService.h
  19. DnsStats.cpp
  20. DnsStats.h
  21. DnsStatsTest.cpp
  22. DnsTlsDispatcher.cpp
  23. DnsTlsDispatcher.h
  24. DnsTlsQueryMap.cpp
  25. DnsTlsQueryMap.h
  26. DnsTlsServer.cpp
  27. DnsTlsServer.h
  28. DnsTlsSessionCache.cpp
  29. DnsTlsSessionCache.h
  30. DnsTlsSocket.cpp
  31. DnsTlsSocket.h
  32. DnsTlsSocketFactory.h
  33. DnsTlsTransport.cpp
  34. DnsTlsTransport.h
  35. Experiments.cpp
  36. Experiments.h
  37. ExperimentsTest.cpp
  38. getaddrinfo.cpp
  39. getaddrinfo.h
  40. gethnamaddr.cpp
  41. gethnamaddr.h
  42. hostent.h
  43. IDnsTlsSocket.h
  44. IDnsTlsSocketFactory.h
  45. IDnsTlsSocketObserver.h
  46. libnetd_resolv.map.txt
  47. LockedQueue.h
  48. NOTICE
  49. OWNERS
  50. params.h
  51. PREUPLOAD.cfg
  52. PrivateDnsConfiguration.cpp
  53. PrivateDnsConfiguration.h
  54. README-DoT.md
  55. README.md
  56. res_cache.cpp
  57. res_comp.cpp
  58. res_comp.h
  59. res_debug.cpp
  60. res_debug.h
  61. res_init.cpp
  62. res_init.h
  63. res_mkquery.cpp
  64. res_query.cpp
  65. res_send.cpp
  66. res_send.h
  67. res_stats.cpp
  68. resolv_cache.h
  69. resolv_cache_unit_test.cpp
  70. resolv_callback_unit_test.cpp
  71. resolv_private.h
  72. resolv_test_config_template.xml
  73. resolv_tls_unit_test.cpp
  74. resolv_unit_test.cpp
  75. ResolverController.cpp
  76. ResolverController.h
  77. ResolverEventReporter.cpp
  78. ResolverEventReporter.h
  79. ResolverStats.h
  80. sethostent.cpp
  81. stats.h
  82. stats.proto
  83. TEST_MAPPING
  84. util.cpp
  85. util.h
README.md

Logging

This code uses LOG(X) for logging. Log levels are VERBOSE,DEBUG,INFO,WARNING and ERROR. The default setting is WARNING and logs relate to WARNING and ERROR will be shown. If you want to enable the DEBUG level logs, using following command. adb shell service call dnsresolver 10 i32 1 VERBOSE 0 DEBUG 1 INFO 2 WARNING 3 ERROR 4 Verbose resolver logs could contain PII -- do NOT enable in production builds.