commit 3e0853ab9a1609707ec8de453891b3c79ea556bc Author: Florian Weimer Date: Wed Jan 3 16:40:32 2024 +0100 Revert "resolve: Remove __res_context_query alloca usage" This reverts commit 40c0add7d48739f5d89ebba255c1df26629a76e2. The change causes an infinite loop with malformed domain names. diff --git a/resolv/res_query.c b/resolv/res_query.c index 1b148a2a05b8641c..049de91b95a2bd0c 100644 --- a/resolv/res_query.c +++ b/resolv/res_query.c @@ -80,7 +80,6 @@ #include #include #include -#include #if PACKETSZ > 65536 #define MAXPACKET PACKETSZ @@ -115,14 +114,11 @@ __res_context_query (struct resolv_context *ctx, const char *name, struct __res_state *statp = ctx->resp; UHEADER *hp = (UHEADER *) answer; UHEADER *hp2; - int n; - - /* It requires 2 times QUERYSIZE for type == T_QUERY_A_AND_AAAA. */ - struct scratch_buffer buf; - scratch_buffer_init (&buf); - _Static_assert (2 * QUERYSIZE <= sizeof (buf.__space.__c), - "scratch_buffer too small"); - u_char *query1 = buf.data; + int n, use_malloc = 0; + + size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE; + u_char *buf = alloca (bufsize); + u_char *query1 = buf; int nquery1 = -1; u_char *query2 = NULL; int nquery2 = 0; @@ -133,28 +129,37 @@ __res_context_query (struct resolv_context *ctx, const char *name, if (type == T_QUERY_A_AND_AAAA) { n = __res_context_mkquery (ctx, QUERY, name, class, T_A, NULL, - query1, buf.length); + query1, bufsize); if (n > 0) { if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) { /* Use RESOLV_EDNS_BUFFER_SIZE because the receive buffer can be reallocated. */ - n = __res_nopt (ctx, n, query1, buf.length, + n = __res_nopt (ctx, n, query1, bufsize, RESOLV_EDNS_BUFFER_SIZE); if (n < 0) goto unspec_nomem; } nquery1 = n; - query2 = buf.data + n; + /* Align the buffer. */ + int npad = ((nquery1 + __alignof__ (HEADER) - 1) + & ~(__alignof__ (HEADER) - 1)) - nquery1; + if (n > bufsize - npad) + { + n = -1; + goto unspec_nomem; + } + int nused = n + npad; + query2 = buf + nused; n = __res_context_mkquery (ctx, QUERY, name, class, T_AAAA, - NULL, query2, buf.length - n); + NULL, query2, bufsize - nused); if (n > 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) /* Use RESOLV_EDNS_BUFFER_SIZE because the receive buffer can be reallocated. */ - n = __res_nopt (ctx, n, query2, buf.length, + n = __res_nopt (ctx, n, query2, bufsize, RESOLV_EDNS_BUFFER_SIZE); nquery2 = n; } @@ -164,7 +169,7 @@ __res_context_query (struct resolv_context *ctx, const char *name, else { n = __res_context_mkquery (ctx, QUERY, name, class, type, NULL, - query1, buf.length); + query1, bufsize); if (n > 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0) @@ -176,25 +181,27 @@ __res_context_query (struct resolv_context *ctx, const char *name, advertise = anslen; else advertise = RESOLV_EDNS_BUFFER_SIZE; - n = __res_nopt (ctx, n, query1, buf.length, advertise); + n = __res_nopt (ctx, n, query1, bufsize, advertise); } nquery1 = n; } - if (__glibc_unlikely (n <= 0)) { + if (__glibc_unlikely (n <= 0) && !use_malloc) { /* Retry just in case res_nmkquery failed because of too short buffer. Shouldn't happen. */ - if (scratch_buffer_set_array_size (&buf, - T_QUERY_A_AND_AAAA ? 2 : 1, - MAXPACKET)) { - query1 = buf.data; + bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * MAXPACKET; + buf = malloc (bufsize); + if (buf != NULL) { + query1 = buf; + use_malloc = 1; goto again; } } if (__glibc_unlikely (n <= 0)) { RES_SET_H_ERRNO(statp, NO_RECOVERY); - scratch_buffer_free (&buf); + if (use_malloc) + free (buf); return (n); } @@ -217,7 +224,8 @@ __res_context_query (struct resolv_context *ctx, const char *name, answerp2_malloced); } - scratch_buffer_free (&buf); + if (use_malloc) + free (buf); if (n < 0) { RES_SET_H_ERRNO(statp, TRY_AGAIN); return (n);