[PATCH v4] CVE-2026-89092: nscd: replace alloca with malloc in aicache, hstcache

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Sep 28 17:08:36 GMT 2026



On 28/09/26 11:01, Florian Weimer wrote:
> From: DJ Delorie <dj@redhat.com>
> 
> This changes the alloca fallback to a malloc fallback
> in the addrinfo and host cache code.
> 
> This fixes bug 34624.
> 
> Co-authored-by: Florian Weimer <fweimer@redhat.com>


LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> 
> ---
> v4: v3 was corrupted.  DJ is away for a bit, so I'm resending this, to
>     keep making progress.  I fixed the h_aliases_len allocation to
>     better deal with non-glibc interposed mallocs (not treating NULL
>     pointers as errors).  I removed outdated comments that still talked
>     about stack allocations.  I added the CVE ID to the commit message.
>  nscd/aicache.c  | 27 ++++++++++++++++++---------
>  nscd/hstcache.c | 47 ++++++++++++++++++++++++++++++++++-------------
>  2 files changed, 52 insertions(+), 22 deletions(-)
> 
> diff --git a/nscd/aicache.c b/nscd/aicache.c
> index ef15c373f5..d87e7e966f 100644
> --- a/nscd/aicache.c
> +++ b/nscd/aicache.c
> @@ -102,7 +102,9 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
>    int32_t ttl = INT32_MAX;
>    ssize_t total = 0;
>    char *key_copy = NULL;
> -  bool alloca_used = false;
> +  /* If not NULL, dataset is a temporary dataset not inserted into the
> +     database, and needs to be free'd.  */
> +  void *dataset_alloc = NULL;
>    time_t timeout = MAX_TIMEOUT_VALUE;
>  
>    while (!no_more)
> @@ -173,10 +175,12 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
>  	      /* We cannot permanently add the result in the moment.  But
>  		 we can provide the result as is.  Store the data in some
>  		 temporary memory.  */
> -	      dataset = (struct dataset *) alloca (total + req->key_len);
> +	      dataset = (struct dataset *) malloc (total + req->key_len);
> +	      if (dataset == NULL)
> +		goto out;
>  
>  	      /* We cannot add this record to the permanent database.  */
> -	      alloca_used = true;
> +	      dataset_alloc = dataset;
>  	    }
>  
>  	  /* Fill in the address and address families.  */
> @@ -345,10 +349,12 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
>  	      /* We cannot permanently add the result in the moment.  But
>  		 we can provide the result as is.  Store the data in some
>  		 temporary memory.  */
> -	      dataset = (struct dataset *) alloca (total + req->key_len);
> +	      dataset = (struct dataset *) malloc (total + req->key_len);
> +	      if (dataset == NULL)
> +		goto out;
>  
>  	      /* We cannot add this record to the permanent database.  */
> -	      alloca_used = true;
> +	      dataset_alloc = dataset;
>  	    }
>  
>  	  /* Fill in the address and address families.  */
> @@ -400,8 +406,7 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
>  						   resp)) == 0)
>  	    {
>  	      /* The data has not changed.  We will just bump the
> -		 timeout value.  Note that the new record has been
> -		 allocated on the stack and need not be freed.  */
> +		 timeout value.  */
>  	      dh->timeout = dataset->head.timeout;
>  	      dh->ttl = dataset->head.ttl;
>  	      ++dh->nreloads;
> @@ -419,7 +424,9 @@ addhstaiX (struct database_dyn *db, int fd, request_header *req,
>  		  key_copy = (char *) newp + (key_copy - (char *) dataset);
>  
>  		  dataset = memcpy (newp, dataset, total + req->key_len);
> -		  alloca_used = false;
> +
> +		  free (dataset_alloc);
> +		  dataset_alloc = NULL;
>  		}
>  
>  	      /* Mark the old record as obsolete.  */
> @@ -502,7 +509,7 @@ next_nip:
>   out:
>    __resolv_context_put (ctx);
>  
> -  if (dataset != NULL && !alloca_used)
> +  if (dataset != NULL && dataset_alloc == NULL)
>      {
>        /* If necessary, we also propagate the data to disk.  */
>        if (db->persistent)
> @@ -528,6 +535,8 @@ next_nip:
>    scratch_buffer_free (&tmpbuf4);
>    scratch_buffer_free (&canonbuf);
>  
> +  free (dataset_alloc);
> +
>    return timeout;
>  }
>  
> diff --git a/nscd/hstcache.c b/nscd/hstcache.c
> index fe91818640..44ef80be77 100644
> --- a/nscd/hstcache.c
> +++ b/nscd/hstcache.c
> @@ -15,7 +15,6 @@
>     You should have received a copy of the GNU General Public License
>     along with this program; if not, see <https://www.gnu.org/licenses/>.  */
>  
> -#include <alloca.h>
>  #include <assert.h>
>  #include <errno.h>
>  #include <error.h>
> @@ -188,7 +187,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>        /* Determine the I/O structure.  */
>        size_t h_name_len = strlen (hst->h_name) + 1;
>        size_t h_aliases_cnt;
> -      uint32_t *h_aliases_len;
> +      uint32_t *h_aliases_len = NULL;
>        size_t h_addr_list_cnt;
>        char *addresses;
>        char *aliases;
> @@ -202,7 +201,13 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>        for (cnt = 0; hst->h_aliases[cnt] != NULL; ++cnt)
>  	++h_aliases_cnt;
>        /* Determine the length of all aliases.  */
> -      h_aliases_len = (uint32_t *) alloca (h_aliases_cnt * sizeof (uint32_t));
> +      if (h_aliases_cnt > 0)
> +	{
> +	  h_aliases_len = (uint32_t *) reallocarray (NULL, h_aliases_cnt,
> +						     sizeof (uint32_t));
> +	  if (h_aliases_len == NULL)
> +	    return MAX_TIMEOUT_VALUE;
> +	}
>        total = 0;
>        for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
>  	{
> @@ -216,8 +221,11 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  	++h_addr_list_cnt;
>  
>        if (h_addr_list_cnt == 0)
> -	/* Invalid entry.  */
> -	return MAX_TIMEOUT_VALUE;
> +	{
> +	  /* Invalid entry.  */
> +	  free (h_aliases_len);
> +	  return MAX_TIMEOUT_VALUE;
> +	}
>  
>        total += (sizeof (struct dataset)
>  		+ h_name_len
> @@ -228,8 +236,10 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  	 change.  Allocate memory on the cache since it is likely
>  	 discarded anyway.  If it turns out to be necessary to have a
>  	 new record we can still allocate real memory.  */
> -      bool alloca_used = false;
>        dataset = NULL;
> +      /* If not NULL, dataset is a temporary dataset not inserted into
> +	 the database, and needs to be free'd.  */
> +      void *dataset_alloc = NULL;
>  
>        /* If the record contains more than one IP address (used for
>  	 load balancing etc) don't cache the entry.  This is something
> @@ -245,10 +255,15 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  	  /* We cannot permanently add the result in the moment.  But
>  	     we can provide the result as is.  Store the data in some
>  	     temporary memory.  */
> -	  dataset = (struct dataset *) alloca (total + req->key_len);
> +	  dataset = (struct dataset *) malloc (total + req->key_len);
> +	  if (dataset == NULL)
> +	    {
> +	      free (h_aliases_len);
> +	      return MAX_TIMEOUT_VALUE;
> +	    }
>  
>  	  /* We cannot add this record to the permanent database.  */
> -	  alloca_used = true;
> +	  dataset_alloc = dataset;
>  	}
>  
>        timeout = datahead_init_pos (&dataset->head, total + req->key_len,
> @@ -271,7 +286,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>        cp = dataset->strdata;
>  
>        cp = mempcpy (cp, hst->h_name, h_name_len);
> -      cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
> +      if (h_aliases_cnt > 0)
> +	cp = mempcpy (cp, h_aliases_len, h_aliases_cnt * sizeof (uint32_t));
>  
>        /* The normal addresses first.  */
>        addresses = cp;
> @@ -283,6 +299,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>        for (cnt = 0; cnt < h_aliases_cnt; ++cnt)
>  	cp = mempcpy (cp, hst->h_aliases[cnt], h_aliases_len[cnt]);
>  
> +      free (h_aliases_len);
> +
>        assert (cp
>  	      == dataset->strdata + total - offsetof (struct dataset,
>  						      strdata));
> @@ -308,8 +326,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  			 dh->allocsize - offsetof (struct dataset, resp)) == 0)
>  	    {
>  	      /* The data has not changed.  We will just bump the
> -		 timeout value.  Note that the new record has been
> -		 allocated on the stack and need not be freed.  */
> +		 timeout value.  */
>  	      assert (h_addr_list_cnt == 1);
>  	      dh->ttl = dataset->head.ttl;
>  	      dh->timeout = dataset->head.timeout;
> @@ -335,7 +352,9 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  		      key_copy = (char *) newp + (key_copy - (char *) dataset);
>  
>  		      dataset = memcpy (newp, dataset, total + req->key_len);
> -		      alloca_used = false;
> +
> +		      free (dataset_alloc);
> +		      dataset_alloc = NULL;
>  		    }
>  		}
>  
> @@ -363,7 +382,7 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  	 the current cache handling cannot handle and it is more than
>  	 questionable whether it is worthwhile complicating the cache
>  	 handling just for handling such a special case. */
> -      if (! alloca_used)
> +      if (dataset_alloc == NULL)
>  	{
>  	  /* If necessary, we also propagate the data to disk.  */
>  	  if (db->persistent)
> @@ -394,6 +413,8 @@ cache_addhst (struct database_dyn *db, int fd, request_header *req,
>  
>  	  pthread_rwlock_unlock (&db->lock);
>  	}
> +      free (dataset_alloc);
> +      dataset_alloc = NULL;
>      }
>  
>    if (__builtin_expect (!all_written, 0) && debug_level > 0)
> 
> base-commit: a71991c14f90d3abff914ae1573eeca118a86308



More information about the Libc-alpha mailing list