(root)/
glibc-2.38/
resolv/
resolv_conf.c
       1  /* Extended resolver state separate from struct __res_state.
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <resolv_conf.h>
      20  
      21  #include <alloc_buffer.h>
      22  #include <assert.h>
      23  #include <libc-lock.h>
      24  #include <resolv-internal.h>
      25  #include <sys/stat.h>
      26  #include <libc-symbols.h>
      27  #include <file_change_detection.h>
      28  
      29  /* _res._u._ext.__glibc_extension_index is used as an index into a
      30     struct resolv_conf_array object.  The intent of this construction
      31     is to make reasonably sure that even if struct __res_state objects
      32     are copied around and patched by applications, we can still detect
      33     accesses to stale extended resolver state.  The array elements are
      34     either struct resolv_conf * pointers (if the LSB is cleared) or
      35     free list entries (if the LSB is set).  The free list is used to
      36     speed up finding available entries in the array.  */
      37  #define DYNARRAY_STRUCT resolv_conf_array
      38  #define DYNARRAY_ELEMENT uintptr_t
      39  #define DYNARRAY_PREFIX resolv_conf_array_
      40  #define DYNARRAY_INITIAL_SIZE 0
      41  #include <malloc/dynarray-skeleton.c>
      42  
      43  /* A magic constant for XORing the extension index
      44     (_res._u._ext.__glibc_extension_index).  This makes it less likely
      45     that a valid index is created by accident.  In particular, a zero
      46     value leads to an invalid index.  */
      47  #define INDEX_MAGIC 0x26a8fa5e48af8061ULL
      48  
      49  /* Global resolv.conf-related state.  */
      50  struct resolv_conf_global
      51  {
      52    /* struct __res_state objects contain the extension index
      53       (_res._u._ext.__glibc_extension_index ^ INDEX_MAGIC), which
      54       refers to an element of this array.  When a struct resolv_conf
      55       object (extended resolver state) is associated with a struct
      56       __res_state object (legacy resolver state), its reference count
      57       is increased and added to this array.  Conversely, if the
      58       extended state is detached from the basic state (during
      59       reinitialization or deallocation), the index is decremented, and
      60       the array element is overwritten with NULL.  */
      61    struct resolv_conf_array array;
      62  
      63    /* Start of the free list in the array.  Zero if the free list is
      64       empty.  Otherwise, free_list_start >> 1 is the first element of
      65       the free list (and the free list entries all have their LSB set
      66       and are shifted one to the left).  */
      67    uintptr_t free_list_start;
      68  
      69    /* Cached current configuration object for /etc/resolv.conf.  */
      70    struct resolv_conf *conf_current;
      71  
      72    /* File system identification for /etc/resolv.conf.  */
      73    struct file_change_detection file_resolve_conf;
      74  };
      75  
      76  /* Lazily allocated storage for struct resolv_conf_global.  */
      77  static struct resolv_conf_global *global;
      78  
      79  /* The lock synchronizes access to global and *global.  It also
      80     protects the __refcount member of struct resolv_conf.  */
      81  __libc_lock_define_initialized (static, lock);
      82  
      83  /* Ensure that GLOBAL is allocated and lock it.  Return NULL if
      84     memory allocation fails.  */
      85  static struct resolv_conf_global *
      86  get_locked_global (void)
      87  {
      88    __libc_lock_lock (lock);
      89    /* Use relaxed MO through because of load outside the lock in
      90       __resolv_conf_detach.  */
      91    struct resolv_conf_global *global_copy = atomic_load_relaxed (&global);
      92    if (global_copy == NULL)
      93      {
      94        global_copy = calloc (1, sizeof (*global));
      95        if (global_copy == NULL)
      96  	{
      97  	  __libc_lock_unlock (lock);
      98  	  return NULL;
      99  	}
     100        atomic_store_relaxed (&global, global_copy);
     101        resolv_conf_array_init (&global_copy->array);
     102      }
     103    return global_copy;
     104  }
     105  
     106  /* Relinquish the lock acquired by get_locked_global.  */
     107  static void
     108  put_locked_global (struct resolv_conf_global *global_copy)
     109  {
     110    __libc_lock_unlock (lock);
     111  }
     112  
     113  /* Decrement the reference counter.  The caller must acquire the lock
     114     around the function call.  */
     115  static void
     116  conf_decrement (struct resolv_conf *conf)
     117  {
     118    assert (conf->__refcount > 0);
     119    if (--conf->__refcount == 0)
     120      free (conf);
     121  }
     122  
     123  struct resolv_conf *
     124  __resolv_conf_get_current (void)
     125  {
     126    struct file_change_detection initial;
     127    if (!__file_change_detection_for_path (&initial, _PATH_RESCONF))
     128      return NULL;
     129  
     130    struct resolv_conf_global *global_copy = get_locked_global ();
     131    if (global_copy == NULL)
     132      return NULL;
     133    struct resolv_conf *conf;
     134    if (global_copy->conf_current != NULL
     135        && __file_is_unchanged (&initial, &global_copy->file_resolve_conf))
     136      /* We can reuse the cached configuration object.  */
     137      conf = global_copy->conf_current;
     138    else
     139      {
     140        /* Parse configuration while holding the lock.  This avoids
     141           duplicate work.  */
     142        struct file_change_detection after_load;
     143        conf = __resolv_conf_load (NULL, &after_load);
     144        if (conf != NULL)
     145          {
     146            if (global_copy->conf_current != NULL)
     147              conf_decrement (global_copy->conf_current);
     148            global_copy->conf_current = conf; /* Takes ownership.  */
     149  
     150            /* Update file change detection data, but only if it matches
     151               the initial measurement.  This avoids an ABA race in case
     152               /etc/resolv.conf is temporarily replaced while the file
     153               is read (after the initial measurement), and restored to
     154               the initial version later.  */
     155            if (__file_is_unchanged (&initial, &after_load))
     156              global_copy->file_resolve_conf = after_load;
     157            else
     158              /* If there is a discrepancy, trigger a reload during the
     159                 next use.  */
     160              global_copy->file_resolve_conf.size = -1;
     161          }
     162      }
     163  
     164    if (conf != NULL)
     165      {
     166        /* Return an additional reference.  */
     167        assert (conf->__refcount > 0);
     168        ++conf->__refcount;
     169        assert (conf->__refcount > 0);
     170      }
     171    put_locked_global (global_copy);
     172    return conf;
     173  }
     174  
     175  /* Internal implementation of __resolv_conf_get, without validation
     176     against *RESP.  */
     177  static struct resolv_conf *
     178  resolv_conf_get_1 (const struct __res_state *resp)
     179  {
     180    /* Not initialized, and therefore no associated context.  */
     181    if (!(resp->options & RES_INIT))
     182      return NULL;
     183  
     184    struct resolv_conf_global *global_copy = get_locked_global ();
     185    if (global_copy == NULL)
     186      /* A memory allocation failure here means that no associated
     187         contexts exists, so returning NULL is correct.  */
     188      return NULL;
     189    size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
     190    struct resolv_conf *conf = NULL;
     191    if (index < resolv_conf_array_size (&global_copy->array))
     192      {
     193        uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
     194        if (!(*slot & 1))
     195          {
     196            conf = (struct resolv_conf *) *slot;
     197            assert (conf->__refcount > 0);
     198            ++conf->__refcount;
     199          }
     200      }
     201    put_locked_global (global_copy);
     202    return conf;
     203  }
     204  
     205  /* Return true if both IPv4 addresses are equal.  */
     206  static bool
     207  same_address_v4 (const struct sockaddr_in *left,
     208                   const struct sockaddr_in *right)
     209  {
     210    return left->sin_addr.s_addr == right->sin_addr.s_addr
     211      && left->sin_port == right->sin_port;
     212  }
     213  
     214  /* Return true if both IPv6 addresses are equal.  This ignores the
     215     flow label.  */
     216  static bool
     217  same_address_v6 (const struct sockaddr_in6 *left,
     218                   const struct sockaddr_in6 *right)
     219  {
     220    return memcmp (&left->sin6_addr, &right->sin6_addr,
     221                   sizeof (left->sin6_addr)) == 0
     222      && left->sin6_port == right->sin6_port
     223      && left->sin6_scope_id == right->sin6_scope_id;
     224  }
     225  
     226  static bool
     227  same_address (const struct sockaddr *left, const struct sockaddr *right)
     228  {
     229    if (left->sa_family != right->sa_family)
     230      return false;
     231    switch (left->sa_family)
     232      {
     233      case AF_INET:
     234        return same_address_v4 ((const struct sockaddr_in *) left,
     235                                (const struct sockaddr_in *) right);
     236      case AF_INET6:
     237        return same_address_v6 ((const struct sockaddr_in6 *) left,
     238                                (const struct sockaddr_in6 *) right);
     239      }
     240    return false;
     241  }
     242  
     243  /* Check that *RESP and CONF match.  Used by __resolv_conf_get.  */
     244  static bool
     245  resolv_conf_matches (const struct __res_state *resp,
     246                       const struct resolv_conf *conf)
     247  {
     248    /* NB: Do not compare the options, retrans, retry, ndots.  These can
     249       be changed by application.  */
     250  
     251    /* Check that the name servers in *RESP have not been modified by
     252       the application.  */
     253    {
     254      size_t nserv = conf->nameserver_list_size;
     255      if (nserv > MAXNS)
     256        nserv = MAXNS;
     257      /* _ext.nscount is 0 until initialized by res_send.c.  */
     258      if (resp->nscount != nserv
     259          || (resp->_u._ext.nscount != 0 && resp->_u._ext.nscount != nserv))
     260        return false;
     261      for (size_t i = 0; i < nserv; ++i)
     262        {
     263          if (resp->nsaddr_list[i].sin_family == 0)
     264            {
     265              if (resp->_u._ext.nsaddrs[i]->sin6_family != AF_INET6)
     266                return false;
     267              if (!same_address ((struct sockaddr *) resp->_u._ext.nsaddrs[i],
     268                                 conf->nameserver_list[i]))
     269                return false;
     270            }
     271          else if (resp->nsaddr_list[i].sin_family != AF_INET)
     272            return false;
     273          else if (!same_address ((struct sockaddr *) &resp->nsaddr_list[i],
     274                                  conf->nameserver_list[i]))
     275            return false;
     276        }
     277    }
     278  
     279    /* Check that the search list in *RESP has not been modified by the
     280       application.  */
     281    {
     282      if (resp->dnsrch[0] == NULL)
     283        {
     284          /* Empty search list.  No default domain name.  */
     285          return conf->search_list_size == 0 && resp->defdname[0] == '\0';
     286        }
     287  
     288      if (resp->dnsrch[0] != resp->defdname)
     289        /* If the search list is not empty, it must start with the
     290           default domain name.  */
     291        return false;
     292  
     293      size_t nsearch;
     294      for (nsearch = 0; nsearch < MAXDNSRCH; ++nsearch)
     295        if (resp->dnsrch[nsearch] == NULL)
     296          break;
     297      if (nsearch > MAXDNSRCH)
     298        /* Search list is not null-terminated.  */
     299        return false;
     300  
     301      size_t search_list_size = 0;
     302      for (size_t i = 0; i < conf->search_list_size; ++i)
     303        {
     304          if (resp->dnsrch[i] != NULL)
     305            {
     306              search_list_size += strlen (resp->dnsrch[i]) + 1;
     307              if (strcmp (resp->dnsrch[i], conf->search_list[i]) != 0)
     308                return false;
     309            }
     310          else
     311            {
     312              /* resp->dnsrch is truncated if the number of elements
     313                 exceeds MAXDNSRCH, or if the combined storage space for
     314                 the search list exceeds what can be stored in
     315                 resp->defdname.  */
     316              if (i == MAXDNSRCH || search_list_size > sizeof (resp->dnsrch))
     317                break;
     318              /* Otherwise, a mismatch indicates a match failure.  */
     319              return false;
     320            }
     321        }
     322    }
     323  
     324    /* Check that the sort list has not been modified.  */
     325    {
     326      size_t nsort = conf->sort_list_size;
     327      if (nsort > MAXRESOLVSORT)
     328        nsort = MAXRESOLVSORT;
     329      if (resp->nsort != nsort)
     330        return false;
     331      for (size_t i = 0; i < nsort; ++i)
     332        if (resp->sort_list[i].addr.s_addr != conf->sort_list[i].addr.s_addr
     333            || resp->sort_list[i].mask != conf->sort_list[i].mask)
     334          return false;
     335    }
     336  
     337    return true;
     338  }
     339  
     340  struct resolv_conf *
     341  __resolv_conf_get (struct __res_state *resp)
     342  {
     343    struct resolv_conf *conf = resolv_conf_get_1 (resp);
     344    if (conf == NULL)
     345      return NULL;
     346    if (resolv_conf_matches (resp, conf))
     347      return conf;
     348    __resolv_conf_put (conf);
     349    return NULL;
     350  }
     351  
     352  void
     353  __resolv_conf_put (struct resolv_conf *conf)
     354  {
     355    if (conf == NULL)
     356      return;
     357  
     358    __libc_lock_lock (lock);
     359    conf_decrement (conf);
     360    __libc_lock_unlock (lock);
     361  }
     362  
     363  struct resolv_conf *
     364  __resolv_conf_allocate (const struct resolv_conf *init)
     365  {
     366    /* Allocate in decreasing order of alignment.  */
     367    _Static_assert (__alignof__ (const char *const *)
     368                    <= __alignof__ (struct resolv_conf), "alignment");
     369    _Static_assert (__alignof__ (struct sockaddr_in6)
     370                    <= __alignof__ (const char *const *), "alignment");
     371    _Static_assert (__alignof__ (struct sockaddr_in)
     372                    ==  __alignof__ (struct sockaddr_in6), "alignment");
     373    _Static_assert (__alignof__ (struct resolv_sortlist_entry)
     374                    <= __alignof__ (struct sockaddr_in), "alignment");
     375  
     376    /* Space needed by the nameserver addresses.  */
     377    size_t address_space = 0;
     378    for (size_t i = 0; i < init->nameserver_list_size; ++i)
     379      if (init->nameserver_list[i]->sa_family == AF_INET)
     380        address_space += sizeof (struct sockaddr_in);
     381      else
     382        {
     383          assert (init->nameserver_list[i]->sa_family == AF_INET6);
     384          address_space += sizeof (struct sockaddr_in6);
     385        }
     386  
     387    /* Space needed by the search list strings.  */
     388    size_t string_space = 0;
     389    for (size_t i = 0; i < init->search_list_size; ++i)
     390      string_space += strlen (init->search_list[i]) + 1;
     391  
     392    /* Allocate the buffer.  */
     393    void *ptr;
     394    struct alloc_buffer buffer = alloc_buffer_allocate
     395      (sizeof (struct resolv_conf)
     396       + init->nameserver_list_size * sizeof (init->nameserver_list[0])
     397       + address_space
     398       + init->search_list_size * sizeof (init->search_list[0])
     399       + init->sort_list_size * sizeof (init->sort_list[0])
     400       + string_space,
     401       &ptr);
     402    struct resolv_conf *conf
     403      = alloc_buffer_alloc (&buffer, struct resolv_conf);
     404    if (conf == NULL)
     405      /* Memory allocation failure.  */
     406      return NULL;
     407    assert (conf == ptr);
     408  
     409    /* Initialize the contents.  */
     410    conf->__refcount = 1;
     411    conf->retrans = init->retrans;
     412    conf->retry = init->retry;
     413    conf->options = init->options;
     414    conf->ndots = init->ndots;
     415  
     416    /* Allocate the arrays with pointers.  These must come first because
     417       they have the highets alignment.  */
     418    conf->nameserver_list_size = init->nameserver_list_size;
     419    const struct sockaddr **nameserver_array = alloc_buffer_alloc_array
     420      (&buffer, const struct sockaddr *, init->nameserver_list_size);
     421    conf->nameserver_list = nameserver_array;
     422  
     423    conf->search_list_size = init->search_list_size;
     424    const char **search_array = alloc_buffer_alloc_array
     425      (&buffer, const char *, init->search_list_size);
     426    conf->search_list = search_array;
     427  
     428    /* Fill the name server list array.  */
     429    for (size_t i = 0; i < init->nameserver_list_size; ++i)
     430      if (init->nameserver_list[i]->sa_family == AF_INET)
     431        {
     432          struct sockaddr_in *sa = alloc_buffer_alloc
     433            (&buffer, struct sockaddr_in);
     434          *sa = *(struct sockaddr_in *) init->nameserver_list[i];
     435          nameserver_array[i] = (struct sockaddr *) sa;
     436        }
     437      else
     438        {
     439          struct sockaddr_in6 *sa = alloc_buffer_alloc
     440            (&buffer, struct sockaddr_in6);
     441          *sa = *(struct sockaddr_in6 *) init->nameserver_list[i];
     442          nameserver_array[i] = (struct sockaddr *) sa;
     443        }
     444  
     445    /* Allocate and fill the sort list array.  */
     446    {
     447      conf->sort_list_size = init->sort_list_size;
     448      struct resolv_sortlist_entry *array = alloc_buffer_alloc_array
     449        (&buffer, struct resolv_sortlist_entry, init->sort_list_size);
     450      conf->sort_list = array;
     451      for (size_t i = 0; i < init->sort_list_size; ++i)
     452        array[i] = init->sort_list[i];
     453    }
     454  
     455    /* Fill the search list array.  This must come last because the
     456       strings are the least aligned part of the allocation.  */
     457    {
     458      for (size_t i = 0; i < init->search_list_size; ++i)
     459        search_array[i] = alloc_buffer_copy_string
     460          (&buffer, init->search_list[i]);
     461    }
     462  
     463    assert (!alloc_buffer_has_failed (&buffer));
     464    return conf;
     465  }
     466  
     467  /* Update *RESP from the extended state.  */
     468  static __attribute__ ((nonnull (1, 2), warn_unused_result)) bool
     469  update_from_conf (struct __res_state *resp, const struct resolv_conf *conf)
     470  {
     471    resp->defdname[0] = '\0';
     472    resp->pfcode = 0;
     473    resp->_vcsock = -1;
     474    resp->_flags = 0;
     475    resp->ipv6_unavail = false;
     476    resp->__glibc_unused_qhook = NULL;
     477    resp->__glibc_unused_rhook = NULL;
     478  
     479    resp->retrans = conf->retrans;
     480    resp->retry = conf->retry;
     481    resp->options = conf->options;
     482    resp->ndots = conf->ndots;
     483  
     484    /* Copy the name server addresses.  */
     485    {
     486      resp->nscount = 0;
     487      resp->_u._ext.nscount = 0;
     488      size_t nserv = conf->nameserver_list_size;
     489      if (nserv > MAXNS)
     490        nserv = MAXNS;
     491      for (size_t i = 0; i < nserv; i++)
     492        {
     493          if (conf->nameserver_list[i]->sa_family == AF_INET)
     494            {
     495              resp->nsaddr_list[i]
     496                = *(struct sockaddr_in *)conf->nameserver_list[i];
     497              resp->_u._ext.nsaddrs[i] = NULL;
     498            }
     499          else
     500            {
     501              assert (conf->nameserver_list[i]->sa_family == AF_INET6);
     502              resp->nsaddr_list[i].sin_family = 0;
     503              /* Make a defensive copy of the name server address, in
     504                 case the application overwrites it.  */
     505              struct sockaddr_in6 *sa = malloc (sizeof (*sa));
     506              if (sa == NULL)
     507                {
     508                  for (size_t j = 0; j < i; ++j)
     509                    free (resp->_u._ext.nsaddrs[j]);
     510                  return false;
     511                }
     512              *sa = *(struct sockaddr_in6 *)conf->nameserver_list[i];
     513              resp->_u._ext.nsaddrs[i] = sa;
     514            }
     515          resp->_u._ext.nssocks[i] = -1;
     516        }
     517      resp->nscount = nserv;
     518      /* Leave resp->_u._ext.nscount at 0.  res_send.c handles this.  */
     519    }
     520  
     521    /* Fill in the prefix of the search list.  It is truncated either at
     522       MAXDNSRCH, or if reps->defdname has insufficient space.  */
     523    {
     524      struct alloc_buffer buffer
     525        = alloc_buffer_create (resp->defdname, sizeof (resp->defdname));
     526      size_t size = conf->search_list_size;
     527      size_t i;
     528      for (i = 0; i < size && i < MAXDNSRCH; ++i)
     529        {
     530          resp->dnsrch[i] = alloc_buffer_copy_string
     531            (&buffer, conf->search_list[i]);
     532          if (resp->dnsrch[i] == NULL)
     533            /* No more space in resp->defdname.  Truncate.  */
     534            break;
     535        }
     536      resp->dnsrch[i] = NULL;
     537    }
     538  
     539    /* Copy the sort list.  */
     540    {
     541      size_t nsort = conf->sort_list_size;
     542      if (nsort > MAXRESOLVSORT)
     543        nsort = MAXRESOLVSORT;
     544      for (size_t i = 0; i < nsort; ++i)
     545        {
     546          resp->sort_list[i].addr = conf->sort_list[i].addr;
     547          resp->sort_list[i].mask = conf->sort_list[i].mask;
     548        }
     549      resp->nsort = nsort;
     550    }
     551  
     552    /* The overlapping parts of both configurations should agree after
     553       initialization.  */
     554    assert (resolv_conf_matches (resp, conf));
     555    return true;
     556  }
     557  
     558  /* Decrement the configuration object at INDEX and free it if the
     559     reference counter reaches 0.  *GLOBAL_COPY must be locked and
     560     remains so.  */
     561  static void
     562  decrement_at_index (struct resolv_conf_global *global_copy, size_t index)
     563  {
     564    if (index < resolv_conf_array_size (&global_copy->array))
     565      {
     566        /* Index found.  */
     567        uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
     568        /* Check that the slot is not already part of the free list.  */
     569        if (!(*slot & 1))
     570          {
     571            struct resolv_conf *conf = (struct resolv_conf *) *slot;
     572            conf_decrement (conf);
     573            /* Put the slot onto the free list.  */
     574            *slot = global_copy->free_list_start;
     575            global_copy->free_list_start = (index << 1) | 1;
     576          }
     577      }
     578  }
     579  
     580  bool
     581  __resolv_conf_attach (struct __res_state *resp, struct resolv_conf *conf)
     582  {
     583    assert (conf->__refcount > 0);
     584  
     585    struct resolv_conf_global *global_copy = get_locked_global ();
     586    if (global_copy == NULL)
     587      return false;
     588  
     589    /* Try to find an unused index in the array.  */
     590    size_t index;
     591    {
     592      if (global_copy->free_list_start & 1)
     593        {
     594          /* Unlink from the free list.  */
     595          index = global_copy->free_list_start >> 1;
     596          uintptr_t *slot = resolv_conf_array_at (&global_copy->array, index);
     597          global_copy->free_list_start = *slot;
     598          assert (global_copy->free_list_start == 0
     599                  || global_copy->free_list_start & 1);
     600          /* Install the configuration pointer.  */
     601          *slot = (uintptr_t) conf;
     602        }
     603      else
     604        {
     605          size_t size = resolv_conf_array_size (&global_copy->array);
     606          /* No usable index found.  Increase the array size.  */
     607          resolv_conf_array_add (&global_copy->array, (uintptr_t) conf);
     608          if (resolv_conf_array_has_failed (&global_copy->array))
     609            {
     610              put_locked_global (global_copy);
     611              __set_errno (ENOMEM);
     612              return false;
     613            }
     614          /* The new array element was added at the end.  */
     615          index = size;
     616        }
     617    }
     618  
     619    /* We have added a new reference to the object.  */
     620    ++conf->__refcount;
     621    assert (conf->__refcount > 0);
     622    put_locked_global (global_copy);
     623  
     624    if (!update_from_conf (resp, conf))
     625      {
     626        /* Drop the reference we acquired.  Reacquire the lock.  The
     627           object has already been allocated, so it cannot be NULL this
     628           time.  */
     629        global_copy = get_locked_global ();
     630        decrement_at_index (global_copy, index);
     631        put_locked_global (global_copy);
     632        return false;
     633      }
     634    resp->_u._ext.__glibc_extension_index = index ^ INDEX_MAGIC;
     635  
     636    return true;
     637  }
     638  
     639  void
     640  __resolv_conf_detach (struct __res_state *resp)
     641  {
     642    if (atomic_load_relaxed (&global) == NULL)
     643      /* Detach operation after a shutdown, or without any prior
     644         attachment.  We cannot free the data (and there might not be
     645         anything to free anyway).  */
     646      return;
     647  
     648    struct resolv_conf_global *global_copy = get_locked_global ();
     649    size_t index = resp->_u._ext.__glibc_extension_index ^ INDEX_MAGIC;
     650    decrement_at_index (global_copy, index);
     651  
     652    /* Clear the index field, so that accidental reuse is less
     653       likely.  */
     654    resp->_u._ext.__glibc_extension_index = 0;
     655  
     656    put_locked_global (global_copy);
     657  }
     658  
     659  /* Deallocate the global data.  */
     660  void
     661  __libc_resolv_conf_freemem (void)
     662  {
     663    /* No locking because this function is supposed to be called when
     664       the process has turned single-threaded.  */
     665    if (global == NULL)
     666      return;
     667  
     668    if (global->conf_current != NULL)
     669      {
     670        conf_decrement (global->conf_current);
     671        global->conf_current = NULL;
     672      }
     673  
     674    /* Note that this frees only the array itself.  The pointed-to
     675       configuration objects should have been deallocated by res_nclose
     676       and per-thread cleanup functions.  */
     677    resolv_conf_array_free (&global->array);
     678  
     679    free (global);
     680  
     681    /* Stop potential future __resolv_conf_detach calls from accessing
     682       deallocated memory.  */
     683    global = NULL;
     684  }