8597553
commit f30a54b21b83f254533c59ca72ad17af5249c6be
8597553
Author: Florian Weimer <fweimer@redhat.com>
8597553
Date:   Mon Jul 3 20:31:23 2017 +0200
8597553
8597553
    resolv: Introduce struct resolv_conf with extended resolver state
8597553
    
8597553
    This change provides additional resolver configuration state which
8597553
    is not exposed through the _res ABI.  It reuses the existing
8597553
    initstamp field in the supposedly-private part of _res.  Some effort
8597553
    is undertaken to avoid memory safety issues introduced by applications
8597553
    which directly patch the _res object.
8597553
    
8597553
    With this commit, only the initstamp field is moved into struct
8597553
    resolv_conf.  Additional members will be added later, eventually
8597553
    migrating the entire resolver configuration.
8597553
8597553
diff --git a/resolv/Makefile b/resolv/Makefile
8597553
index 7f5e07d618297f60..ef5ed2966e4d6c51 100644
8597553
--- a/resolv/Makefile
8597553
+++ b/resolv/Makefile
8597553
@@ -29,7 +29,7 @@ headers	:= resolv.h \
8597553
 
8597553
 routines := herror inet_addr inet_ntop inet_pton nsap_addr res_init \
8597553
 	    res_hconf res_libc res-state res_randomid res-close \
8597553
-	    resolv_context
8597553
+	    resolv_context resolv_conf
8597553
 
8597553
 tests = tst-aton tst-leaks tst-inet_ntop
8597553
 xtests = tst-leaks2
8597553
diff --git a/resolv/res-close.c b/resolv/res-close.c
8597553
index 97da73c99cfd0e12..21f038c2c77f7375 100644
8597553
--- a/resolv/res-close.c
8597553
+++ b/resolv/res-close.c
8597553
@@ -84,6 +84,7 @@
8597553
 
8597553
 #include <resolv-internal.h>
8597553
 #include <resolv_context.h>
8597553
+#include <resolv_conf.h>
8597553
 #include <not-cancel.h>
8597553
 
8597553
 /* Close all open sockets.  If FREE_ADDR is true, deallocate any
8597553
@@ -111,6 +112,8 @@ __res_iclose (res_state statp, bool free_addr)
8597553
             statp->_u._ext.nsaddrs[ns] = NULL;
8597553
           }
8597553
       }
8597553
+  if (free_addr)
8597553
+    __resolv_conf_detach (statp);
8597553
 }
8597553
 libc_hidden_def (__res_iclose)
8597553
 
8597553
diff --git a/resolv/res_init.c b/resolv/res_init.c
8597553
index 5d8b2c994d8e6f04..659d3ea81f973257 100644
8597553
--- a/resolv/res_init.c
8597553
+++ b/resolv/res_init.c
8597553
@@ -102,6 +102,7 @@
8597553
 #include <sys/types.h>
8597553
 #include <inet/net-internal.h>
8597553
 #include <errno.h>
8597553
+#include <resolv_conf.h>
8597553
 
8597553
 static void res_setoptions (res_state, const char *);
8597553
 static uint32_t net_mask (struct in_addr);
8597553
@@ -137,7 +138,6 @@ res_vinit_1 (res_state statp, bool preinit, FILE *fp, char **buffer)
8597553
   bool havesearch = false;
8597553
   int nsort = 0;
8597553
   char *net;
8597553
-  statp->_u._ext.initstamp = __res_initstamp;
8597553
 
8597553
   if (!preinit)
8597553
     {
8597553
@@ -457,6 +457,19 @@ __res_vinit (res_state statp, int preinit)
8597553
   bool ok = res_vinit_1 (statp, preinit, fp, &buffer);
8597553
   free (buffer);
8597553
 
8597553
+  if (ok)
8597553
+    {
8597553
+      struct resolv_conf init = { 0 }; /* No data yet.  */
8597553
+      struct resolv_conf *conf = __resolv_conf_allocate (&init);
8597553
+      if (conf == NULL)
8597553
+        ok = false;
8597553
+      else
8597553
+        {
8597553
+          ok = __resolv_conf_attach (statp, conf);
8597553
+          __resolv_conf_put (conf);
8597553
+        }
8597553
+    }
8597553
+
8597553
   if (!ok)
8597553
     {
8597553
       /* Deallocate the name server addresses which have been
8597553
diff --git a/resolv/resolv.h b/resolv/resolv.h
8597553
index 9fef8e9f248c84e6..b83232cca8e8f0c3 100644
8597553
--- a/resolv/resolv.h
8597553
+++ b/resolv/resolv.h
8597553
@@ -122,10 +122,10 @@ struct __res_state {
8597553
 			uint16_t		nsinit;
8597553
 			struct sockaddr_in6	*nsaddrs[MAXNS];
8597553
 #ifdef _LIBC
8597553
-			unsigned long long int	initstamp
8597553
+			unsigned long long int __glibc_extension_index
8597553
 			  __attribute__((packed));
8597553
 #else
8597553
-			unsigned int		_initstamp[2];
8597553
+			unsigned int		__glibc_reserved[2];
8597553
 #endif
8597553
 		} _ext;
8597553
 	} _u;
8597553
diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
8597553
new file mode 100644
8597553
index 0000000000000000..cabe69cf1ba3f4d5
8597553
--- /dev/null
8597553
+++ b/resolv/resolv_conf.c
8597553
@@ -0,0 +1,322 @@
8597553
+/* Extended resolver state separate from struct __res_state.
8597553
+   Copyright (C) 2017 Free Software Foundation, Inc.
8597553
+   This file is part of the GNU C Library.
8597553
+
8597553
+   The GNU C Library is free software; you can redistribute it and/or
8597553
+   modify it under the terms of the GNU Lesser General Public
8597553
+   License as published by the Free Software Foundation; either
8597553
+   version 2.1 of the License, or (at your option) any later version.
8597553
+
8597553
+   The GNU C Library is distributed in the hope that it will be useful,
8597553
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8597553
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8597553
+   Lesser General Public License for more details.
8597553
+
8597553
+   You should have received a copy of the GNU Lesser General Public
8597553
+   License along with the GNU C Library; if not, see
8597553
+   <http://www.gnu.org/licenses/>.  */
8597553
+
8597553
+#include <resolv_conf.h>
8597553
+
8597553
+#include <alloc_buffer.h>
8597553
+#include <assert.h>
8597553
+#include <libc-lock.h>
8597553
+#include <resolv-internal.h>
8597553
+
8597553
+/* _res._u._ext.__glibc_extension_index is used as an index into a
8597553
+   struct resolv_conf_array object.  The intent of this construction
8597553
+   is to make reasonably sure that even if struct __res_state objects
8597553
+   are copied around and patched by applications, we can still detect
8597553
+   accesses to stale extended resolver state.  */
8597553
+#define DYNARRAY_STRUCT resolv_conf_array
8597553
+#define DYNARRAY_ELEMENT struct resolv_conf *
8597553
+#define DYNARRAY_PREFIX resolv_conf_array_
8597553
+#define DYNARRAY_INITIAL_SIZE 0
8597553
+#include <malloc/dynarray-skeleton.c>
8597553
+
8597553
+/* A magic constant for XORing the extension index
8597553
+   (_res._u._ext.__glibc_extension_index).  This makes it less likely
8597553
+   that a valid index is created by accident.  In particular, a zero
8597553
+   value leads to an invalid index.  */
8597553
+#define INDEX_MAGIC 0x26a8fa5e48af8061ULL
8597553
+
8597553
+/* Global resolv.conf-related state.  */
8597553
+struct resolv_conf_global
8597553
+{
8597553
+  /* struct __res_state objects contain the extension index
8597553
+     (_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
8597553
+     refers to an element of this array.  When a struct resolv_conf
8597553
+     object (extended resolver state) is associated with a struct
8597553
+     __res_state object (legacy resolver state), its reference count
8597553
+     is increased and added to this array.  Conversely, if the
8597553
+     extended state is detached from the basic state (during
8597553
+     reinitialization or deallocation), the index is decremented, and
8597553
+     the array element is overwritten with NULL.  */
8597553
+  struct resolv_conf_array array;
8597553
+
8597553
+};
8597553
+
8597553
+/* Lazily allocated storage for struct resolv_conf_global.  */
8597553
+static struct resolv_conf_global *global;
8597553
+
8597553
+/* The lock synchronizes access to global and *global.  It also
8597553
+   protects the __refcount member of struct resolv_conf.  */
8597553
+__libc_lock_define_initialized (static, lock);
8597553
+
8597553
+/* Ensure that GLOBAL is allocated and lock it.  Return NULL if
8597553
+   memory allocation failes.  */
8597553
+static struct resolv_conf_global *
8597553
+get_locked_global (void)
8597553
+{
8597553
+  __libc_lock_lock (lock);
8597553
+  /* Use relaxed MO through because of load outside the lock in
8597553
+     __resolv_conf_detach.  */
8597553
+  struct resolv_conf_global *global_copy = atomic_load_relaxed (&global);
8597553
+  if (global_copy == NULL)
8597553
+    {
8597553
+      global_copy = calloc (1, sizeof (*global));
8597553
+      if (global_copy == NULL)
8597553
+        return NULL;
8597553
+      atomic_store_relaxed (&global, global_copy);
8597553
+      resolv_conf_array_init (&global_copy->array);
8597553
+    }
8597553
+  return global_copy;
8597553
+}
8597553
+
8597553
+/* Relinquish the lock acquired by get_locked_global.  */
8597553
+static void
8597553
+put_locked_global (struct resolv_conf_global *global_copy)
8597553
+{
8597553
+  __libc_lock_unlock (lock);
8597553
+}
8597553
+
8597553
+/* Decrement the reference counter.  The caller must acquire the lock
8597553
+   around the function call.  */
8597553
+static void
8597553
+conf_decrement (struct resolv_conf *conf)
8597553
+{
8597553
+  assert (conf->__refcount > 0);
8597553
+  if (--conf->__refcount == 0)
8597553
+    free (conf);
8597553
+}
8597553
+
8597553
+/* Internal implementation of __resolv_conf_get, without validation
8597553
+   against *RESP.  */
8597553
+static struct resolv_conf *
8597553
+resolv_conf_get_1 (const struct __res_state *resp)
8597553
+{
8597553
+  /* Not initialized, and therefore no assoicated context.  */
8597553
+  if (!(resp->options & RES_INIT))
8597553
+    return NULL;
8597553
+
8597553
+  struct resolv_conf_global *global_copy = get_locked_global ();
8597553
+  if (global_copy == NULL)
8597553
+    /* A memory allocation failure here means that no associated
8597553
+       contexts exists, so returning NULL is correct.  */
8597553
+    return NULL;
8597553
+  size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
8597553
+  struct resolv_conf *conf;
8597553
+  if (index < resolv_conf_array_size (&global_copy->array))
8597553
+    {
8597553
+      conf = *resolv_conf_array_at (&global_copy->array, index);
8597553
+      assert (conf->__refcount > 0);
8597553
+      ++conf->__refcount;
8597553
+    }
8597553
+  else
8597553
+    conf = NULL;
8597553
+  put_locked_global (global_copy);
8597553
+  return conf;
8597553
+}
8597553
+
8597553
+/* Check that *RESP and CONF match.  Used by __resolv_conf_get.  */
8597553
+static bool
8597553
+resolv_conf_matches (const struct __res_state *resp,
8597553
+                     const struct resolv_conf *conf)
8597553
+{
8597553
+  return true;
8597553
+}
8597553
+
8597553
+struct resolv_conf *
8597553
+__resolv_conf_get (struct __res_state *resp)
8597553
+{
8597553
+  struct resolv_conf *conf = resolv_conf_get_1 (resp);
8597553
+  if (conf == NULL)
8597553
+    return NULL;
8597553
+  if (resolv_conf_matches (resp, conf))
8597553
+    return conf;
8597553
+  __resolv_conf_put (conf);
8597553
+  return NULL;
8597553
+}
8597553
+
8597553
+void
8597553
+__resolv_conf_put (struct resolv_conf *conf)
8597553
+{
8597553
+  if (conf == NULL)
8597553
+    return;
8597553
+
8597553
+  __libc_lock_lock (lock);
8597553
+  conf_decrement (conf);
8597553
+  __libc_lock_unlock (lock);
8597553
+}
8597553
+
8597553
+struct resolv_conf *
8597553
+__resolv_conf_allocate (const struct resolv_conf *init)
8597553
+{
8597553
+  /* Allocate the buffer.  */
8597553
+  void *ptr;
8597553
+  struct alloc_buffer buffer = alloc_buffer_allocate
8597553
+    (sizeof (struct resolv_conf),
8597553
+     &ptr);
8597553
+  struct resolv_conf *conf
8597553
+    = alloc_buffer_alloc (&buffer, struct resolv_conf);
8597553
+  if (conf == NULL)
8597553
+    /* Memory allocation failure.  */
8597553
+    return NULL;
8597553
+  assert (conf == ptr);
8597553
+
8597553
+  /* Initialize the contents.  */
8597553
+  conf->__refcount = 1;
8597553
+  conf->initstamp = __res_initstamp;
8597553
+
8597553
+  assert (!alloc_buffer_has_failed (&buffer));
8597553
+  return conf;
8597553
+}
8597553
+
8597553
+/* Update *RESP from the extended state.  */
8597553
+static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
8597553
+update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
8597553
+{
8597553
+  /* The overlapping parts of both configurations should agree after
8597553
+     initialization.  */
8597553
+  assert (resolv_conf_matches (resp, conf));
8597553
+  return true;
8597553
+}
8597553
+
8597553
+/* Decrement the configuration object at INDEX and free it if the
8597553
+   reference counter reaches 0.  *GLOBAL_COPY must be locked and
8597553
+   remains so.  */
8597553
+static void
8597553
+decrement_at_index (struct resolv_conf_global *global_copy, size_t index)
8597553
+{
8597553
+  if (index < resolv_conf_array_size (&global_copy->array))
8597553
+    {
8597553
+      /* Index found.  Deallocate the struct resolv_conf object once
8597553
+         the reference counter reaches.  Free the array slot.  */
8597553
+      struct resolv_conf **slot
8597553
+        = resolv_conf_array_at (&global_copy->array, index);
8597553
+      struct resolv_conf *conf = *slot;
8597553
+      if (conf != NULL)
8597553
+        {
8597553
+          conf_decrement (conf);
8597553
+          /* Clear the slot even if the reference count is positive.
8597553
+             Slots are not shared.  */
8597553
+          *slot = NULL;
8597553
+        }
8597553
+    }
8597553
+}
8597553
+
8597553
+bool
8597553
+__resolv_conf_attach (struct __res_state *resp, struct resolv_conf *conf)
8597553
+{
8597553
+  assert (conf->__refcount > 0);
8597553
+
8597553
+  struct resolv_conf_global *global_copy = get_locked_global ();
8597553
+  if (global_copy == NULL)
8597553
+    {
8597553
+      free (conf);
8597553
+      return false;
8597553
+    }
8597553
+
8597553
+  /* Try to find an unused index in the array.  */
8597553
+  size_t index;
8597553
+  {
8597553
+    size_t size = resolv_conf_array_size (&global_copy->array);
8597553
+    bool found = false;
8597553
+    for (index = 0; index < size; ++index)
8597553
+      {
8597553
+        struct resolv_conf **p
8597553
+          = resolv_conf_array_at (&global_copy->array, index);
8597553
+        if (*p == NULL)
8597553
+          {
8597553
+            *p = conf;
8597553
+            found = true;
8597553
+            break;
8597553
+          }
8597553
+      }
8597553
+
8597553
+    if (!found)
8597553
+      {
8597553
+        /* No usable index found.  Increase the array size.  */
8597553
+        resolv_conf_array_add (&global_copy->array, conf);
8597553
+        if (resolv_conf_array_has_failed (&global_copy->array))
8597553
+          {
8597553
+            put_locked_global (global_copy);
8597553
+            __set_errno (ENOMEM);
8597553
+            return false;
8597553
+          }
8597553
+        /* The new array element was added at the end.  */
8597553
+        index = size;
8597553
+      }
8597553
+  }
8597553
+
8597553
+  /* We have added a new reference to the object.  */
8597553
+  ++conf->__refcount;
8597553
+  assert (conf->__refcount > 0);
8597553
+  put_locked_global (global_copy);
8597553
+
8597553
+  if (!update_from_conf (resp, conf))
8597553
+    {
8597553
+      /* Drop the reference we acquired.  Reacquire the lock.  The
8597553
+         object has already been allocated, so it cannot be NULL this
8597553
+         time.  */
8597553
+      global_copy = get_locked_global ();
8597553
+      decrement_at_index (global_copy, index);
8597553
+      put_locked_global (global_copy);
8597553
+      return false;
8597553
+    }
8597553
+  resp->_u._ext.__glibc_extension_index = index ^ INDEX_MAGIC;
8597553
+
8597553
+  return true;
8597553
+}
8597553
+
8597553
+void
8597553
+__resolv_conf_detach (struct __res_state *resp)
8597553
+{
8597553
+  if (atomic_load_relaxed (&global) == NULL)
8597553
+    /* Detach operation after a shutdown, or without any prior
8597553
+       attachment.  We cannot free the data (and there might not be
8597553
+       anything to free anyway).  */
8597553
+    return;
8597553
+
8597553
+  struct resolv_conf_global *global_copy = get_locked_global ();
8597553
+  size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
8597553
+  decrement_at_index (global_copy, index);
8597553
+
8597553
+  /* Clear the index field, so that accidental reuse is less
8597553
+     likely.  */
8597553
+  resp->_u._ext.__glibc_extension_index = 0;
8597553
+
8597553
+  put_locked_global (global_copy);
8597553
+}
8597553
+
8597553
+/* Deallocate the global data.  */
8597553
+static void __attribute__ ((section ("__libc_thread_freeres_fn")))
8597553
+freeres (void)
8597553
+{
8597553
+  /* No locking because this function is supposed to be called when
8597553
+     the process has turned single-threaded.  */
8597553
+  if (global == NULL)
8597553
+    return;
8597553
+
8597553
+  /* Note that this frees only the array itself.  The pointed-to
8597553
+     configuration objects should have been deallocated by res_nclose
8597553
+     and per-thread cleanup functions.  */
8597553
+  resolv_conf_array_free (&global->array);
8597553
+
8597553
+  free (global);
8597553
+
8597553
+  /* Stop potential future __resolv_conf_detach calls from accessing
8597553
+     deallocated memory.  */
8597553
+  global = NULL;
8597553
+}
8597553
+text_set_element (__libc_subfreeres, freeres);
8597553
diff --git a/resolv/resolv_conf.h b/resolv/resolv_conf.h
8597553
new file mode 100644
8597553
index 0000000000000000..48f92d6d5753aef1
8597553
--- /dev/null
8597553
+++ b/resolv/resolv_conf.h
8597553
@@ -0,0 +1,69 @@
8597553
+/* Extended resolver state separate from struct __res_state.
8597553
+   Copyright (C) 2017 Free Software Foundation, Inc.
8597553
+   This file is part of the GNU C Library.
8597553
+
8597553
+   The GNU C Library is free software; you can redistribute it and/or
8597553
+   modify it under the terms of the GNU Lesser General Public
8597553
+   License as published by the Free Software Foundation; either
8597553
+   version 2.1 of the License, or (at your option) any later version.
8597553
+
8597553
+   The GNU C Library is distributed in the hope that it will be useful,
8597553
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8597553
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8597553
+   Lesser General Public License for more details.
8597553
+
8597553
+   You should have received a copy of the GNU Lesser General Public
8597553
+   License along with the GNU C Library; if not, see
8597553
+   <http://www.gnu.org/licenses/>.  */
8597553
+
8597553
+#ifndef RESOLV_STATE_H
8597553
+#define RESOLV_STATE_H
8597553
+
8597553
+#include <stdbool.h>
8597553
+#include <stddef.h>
8597553
+
8597553
+/* Extended resolver state associated with res_state objects.  Client
8597553
+   code can reach this state through a struct resolv_context
8597553
+   object.  */
8597553
+struct resolv_conf
8597553
+{
8597553
+  /* Used to propagate the effect of res_init across threads.  This
8597553
+     member is mutable and prevents sharing of the same struct
8597553
+     resolv_conf object among multiple struct __res_state objects.  */
8597553
+  unsigned long long int initstamp;
8597553
+
8597553
+  /* Reference counter.  The object is deallocated once it reaches
8597553
+     zero.  For internal use within resolv_conf only.  */
8597553
+  size_t __refcount;
8597553
+};
8597553
+
8597553
+/* The functions below are for use by the res_init resolv.conf parser
8597553
+   and the struct resolv_context facility.  */
8597553
+
8597553
+struct __res_state;
8597553
+
8597553
+/* Return the extended resolver state for *RESP, or NULL if it cannot
8597553
+   be determined.  A call to this function must be paired with a call
8597553
+   to __resolv_conf_put.  */
8597553
+struct resolv_conf *__resolv_conf_get (struct __res_state *) attribute_hidden;
8597553
+
8597553
+/* Converse of __resolv_conf_get.  */
8597553
+void __resolv_conf_put (struct resolv_conf *) attribute_hidden;
8597553
+
8597553
+/* Allocate a new struct resolv_conf object and copy the
8597553
+   pre-configured values from *INIT.  Return NULL on allocation
8597553
+   failure.  The object must be deallocated using
8597553
+   __resolv_conf_put.  */
8597553
+struct resolv_conf *__resolv_conf_allocate (const struct resolv_conf *init)
8597553
+  attribute_hidden __attribute__ ((nonnull (1), warn_unused_result));
8597553
+
8597553
+/* Associate an existing extended resolver state with *RESP.  Return
8597553
+   false on allocation failure.  In addition, update *RESP with the
8597553
+   overlapping non-extended resolver state.  */
8597553
+bool __resolv_conf_attach (struct __res_state *, struct resolv_conf *)
8597553
+  attribute_hidden;
8597553
+
8597553
+/* Detach the extended resolver state from *RESP.  */
8597553
+void __resolv_conf_detach (struct __res_state *resp) attribute_hidden;
8597553
+
8597553
+#endif /* RESOLV_STATE_H */
8597553
diff --git a/resolv/resolv_context.c b/resolv/resolv_context.c
8597553
index 5083a40419bc463c..0ee2184055911d02 100644
8597553
--- a/resolv/resolv_context.c
8597553
+++ b/resolv/resolv_context.c
8597553
@@ -17,9 +17,11 @@
8597553
    <http://www.gnu.org/licenses/>.  */
8597553
 
8597553
 #include <resolv_context.h>
8597553
+#include <resolv_conf.h>
8597553
 #include <resolv-internal.h>
8597553
 
8597553
 #include <assert.h>
8597553
+#include <errno.h>
8597553
 #include <stdlib.h>
8597553
 #include <stdio.h>
8597553
 
8597553
@@ -52,19 +54,37 @@ static __thread struct resolv_context *current attribute_tls_model_ie;
8597553
 /* Initialize *RESP if RES_INIT is not yet set in RESP->options, or if
8597553
    res_init in some other thread requested re-initializing.  */
8597553
 static __attribute__ ((warn_unused_result)) bool
8597553
-maybe_init (struct __res_state *resp, bool preinit)
8597553
+maybe_init (struct resolv_context *ctx, bool preinit)
8597553
 {
8597553
+  struct __res_state *resp = ctx->resp;
8597553
   if (resp->options & RES_INIT)
8597553
     {
8597553
-      if (__res_initstamp != resp->_u._ext.initstamp)
8597553
+      /* If there is no associated resolv_conf object despite the
8597553
+         initialization, something modified *ctx->resp.  Do not
8597553
+         override those changes.  */
8597553
+      if (ctx->conf != NULL && ctx->conf->initstamp != __res_initstamp)
8597553
         {
8597553
           if (resp->nscount > 0)
8597553
+            /* This call will detach the extended resolver state.  */
8597553
             __res_iclose (resp, true);
8597553
-          return __res_vinit (resp, 1) == 0;
8597553
+          /* And this call will attach it again.  */
8597553
+          if (__res_vinit (resp, 1) < 0)
8597553
+            {
8597553
+              /* The configuration no longer matches after failed
8597553
+                 initialization.  */
8597553
+              __resolv_conf_put (ctx->conf);
8597553
+              ctx->conf = NULL;
8597553
+              return false;
8597553
+            }
8597553
+          /* Delay the release of the old configuration until this
8597553
+             point, so that __res_vinit can reuse it if possible.  */
8597553
+          __resolv_conf_put (ctx->conf);
8597553
+          ctx->conf = __resolv_conf_get (ctx->resp);
8597553
         }
8597553
       return true;
8597553
     }
8597553
 
8597553
+  assert (ctx->conf == NULL);
8597553
   if (preinit)
8597553
     {
8597553
       if (!resp->retrans)
8597553
@@ -75,7 +95,11 @@ maybe_init (struct __res_state *resp, bool preinit)
8597553
       if (!resp->id)
8597553
         resp->id = res_randomid ();
8597553
     }
8597553
-  return __res_vinit (resp, preinit) == 0;
8597553
+
8597553
+  if (__res_vinit (resp, preinit) < 0)
8597553
+    return false;
8597553
+  ctx->conf = __resolv_conf_get (ctx->resp);
8597553
+  return true;
8597553
 }
8597553
 
8597553
 /* Allocate a new context object and initialize it.  The object is put
8597553
@@ -87,6 +111,7 @@ context_alloc (struct __res_state *resp)
8597553
   if (ctx == NULL)
8597553
     return NULL;
8597553
   ctx->resp = resp;
8597553
+  ctx->conf = __resolv_conf_get (resp);
8597553
   ctx->__refcount = 1;
8597553
   ctx->__from_res = true;
8597553
   ctx->__next = current;
8597553
@@ -98,8 +123,11 @@ context_alloc (struct __res_state *resp)
8597553
 static void
8597553
 context_free (struct resolv_context *ctx)
8597553
 {
8597553
+  int error_code = errno;
8597553
   current = ctx->__next;
8597553
+  __resolv_conf_put (ctx->conf);
8597553
   free (ctx);
8597553
+  __set_errno (error_code);
8597553
 }
8597553
 
8597553
 /* Reuse the current context object.  */
8597553
@@ -130,7 +158,7 @@ context_get (bool preinit)
8597553
   struct resolv_context *ctx = context_alloc (&_res);
8597553
   if (ctx == NULL)
8597553
     return NULL;
8597553
-  if (!maybe_init (ctx->resp, preinit))
8597553
+  if (!maybe_init (ctx, preinit))
8597553
     {
8597553
       context_free (ctx);
8597553
       return NULL;
8597553
diff --git a/resolv/resolv_context.h b/resolv/resolv_context.h
8597553
index 27c8d56b36104521..ff9ef2c7fe6101f3 100644
8597553
--- a/resolv/resolv_context.h
8597553
+++ b/resolv/resolv_context.h
8597553
@@ -40,15 +40,22 @@
8597553
 #ifndef _RESOLV_CONTEXT_H
8597553
 #define _RESOLV_CONTEXT_H
8597553
 
8597553
+#include <bits/types/res_state.h>
8597553
+#include <resolv/resolv_conf.h>
8597553
 #include <stdbool.h>
8597553
 #include <stddef.h>
8597553
-#include <bits/types/res_state.h>
8597553
 
8597553
 /* Temporary resolver state.  */
8597553
 struct resolv_context
8597553
 {
8597553
   struct __res_state *resp;     /* Backing resolver state.   */
8597553
 
8597553
+  /* Extended resolver state.  This is set to NULL if the
8597553
+     __resolv_context_get functions are unable to locate an associated
8597553
+     extended state.  In this case, the configuration data in *resp
8597553
+     has to be used; otherwise, the data from *conf should be
8597553
+     preferred (because it is a superset).  */
8597553
+  struct resolv_conf *conf;
8597553
 
8597553
   /* The following fields are for internal use within the
8597553
      resolv_context module.  */