Skip to content

Commit

Permalink
Properly fix memory leak in _nss_dns_gethostbyname4_r with big DNS an…
Browse files Browse the repository at this point in the history
…swer

Instead of trying to guess whether the second buffer needs to be freed
set a flag at the place it is allocated
  • Loading branch information
andreas-schwab committed Feb 19, 2014
1 parent c6af2d8 commit ab09bf6
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 49 deletions.
37 changes: 37 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,40 @@
2014-02-18 Andreas Schwab <schwab@suse.de>

[BZ #16574]
* resolv/res_send.c (send_vc): Add parameter ansp2_malloced.
Store non-zero if the second buffer was newly allocated.
(send_dg): Likewise.
(__libc_res_nsend): Add parameter ansp2_malloced and pass it down
to send_vc and send_dg.
(res_nsend): Pass NULL for ansp2_malloced.
* resolv/res_query.c (__libc_res_nquery): Add parameter
answerp2_malloced and pass it down to __libc_res_nsend.
(res_nquery): Pass additional NULL to __libc_res_nquery.
(__libc_res_nsearch): Add parameter answerp2_malloced and pass it
down to __libc_res_nquery and __libc_res_nquerydomain. Deallocate
second answer buffer if answerp2_malloced was set.
(res_nsearch): Pass additional NULL to __libc_res_nsearch.
(__libc_res_nquerydomain): Add parameter
answerp2_malloced and pass it down to __libc_res_nquery.
(res_nquerydomain): Pass additional NULL to
__libc_res_nquerydomain.
* resolv/nss_dns/dns-network.c (_nss_dns_getnetbyname_r): Pass
additional NULL to __libc_res_nsend and __libc_res_nquery.
* resolv/nss_dns/dns-host.c (_nss_dns_gethostbyname3_r): Pass
additional NULL to __libc_res_nsearch.
(_nss_dns_gethostbyname4_r): Revert last change. Use new
parameter of __libc_res_nsearch to check for separately allocated
second buffer.
(_nss_dns_gethostbyaddr2_r): Pass additional NULL to
__libc_res_nquery.
* resolv/nss_dns/dns-canon.c (_nss_dns_getcanonname_r): Pass
additional NULL to __libc_res_nquery.
* resolv/gethnamaddr.c (gethostbyname2): Pass additional NULL to
__libc_res_nsearch.
(gethostbyaddr): Pass additional NULL to __libc_res_nquery.
* include/resolv.h: Update prototypes of __libc_res_nquery,
__libc_res_nsearch, __libc_res_nsend.

2014-02-18 Joseph Myers <joseph@codesourcery.com>

* math/auto-libm-test-in: Add tests of fma.
Expand Down
6 changes: 3 additions & 3 deletions include/resolv.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ libc_hidden_proto (__res_randomid)
libc_hidden_proto (__res_state)

int __libc_res_nquery (res_state, const char *, int, int, u_char *, int,
u_char **, u_char **, int *, int *);
u_char **, u_char **, int *, int *, int *);
int __libc_res_nsearch (res_state, const char *, int, int, u_char *, int,
u_char **, u_char **, int *, int *);
u_char **, u_char **, int *, int *, int *);
int __libc_res_nsend (res_state, const u_char *, int, const u_char *, int,
u_char *, int, u_char **, u_char **, int *, int *)
u_char *, int, u_char **, u_char **, int *, int *, int *)
attribute_hidden;

libresolv_hidden_proto (_sethtent)
Expand Down
6 changes: 3 additions & 3 deletions resolv/gethnamaddr.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ gethostbyname2(name, af)
buf.buf = origbuf = (querybuf *) alloca (1024);

if ((n = __libc_res_nsearch(&_res, name, C_IN, type, buf.buf->buf, 1024,
&buf.ptr, NULL, NULL, NULL)) < 0) {
&buf.ptr, NULL, NULL, NULL, NULL)) < 0) {
if (buf.buf != origbuf)
free (buf.buf);
Dprintf("res_nsearch failed (%d)\n", n);
Expand Down Expand Up @@ -716,12 +716,12 @@ gethostbyaddr(addr, len, af)
buf.buf = orig_buf = (querybuf *) alloca (1024);

n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf, 1024,
&buf.ptr, NULL, NULL, NULL);
&buf.ptr, NULL, NULL, NULL, NULL);
if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0) {
strcpy(qp, "ip6.int");
n = __libc_res_nquery(&_res, qbuf, C_IN, T_PTR, buf.buf->buf,
buf.buf != orig_buf ? MAXPACKET : 1024,
&buf.ptr, NULL, NULL, NULL);
&buf.ptr, NULL, NULL, NULL, NULL);
}
if (n < 0) {
if (buf.buf != orig_buf)
Expand Down
2 changes: 1 addition & 1 deletion resolv/nss_dns/dns-canon.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ _nss_dns_getcanonname_r (const char *name, char *buffer, size_t buflen,
{
int r = __libc_res_nquery (&_res, name, ns_c_in, qtypes[i],
buf, sizeof (buf), &ansp.ptr, NULL, NULL,
NULL);
NULL, NULL);
if (r > 0)
{
/* We need to decode the response. Just one question record.
Expand Down
23 changes: 10 additions & 13 deletions resolv/nss_dns/dns-host.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (1024);

n = __libc_res_nsearch (&_res, name, C_IN, type, host_buffer.buf->buf,
1024, &host_buffer.ptr, NULL, NULL, NULL);
1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
if (n < 0)
{
switch (errno)
Expand Down Expand Up @@ -225,7 +225,7 @@ _nss_dns_gethostbyname3_r (const char *name, int af, struct hostent *result,
n = __libc_res_nsearch (&_res, name, C_IN, T_A, host_buffer.buf->buf,
host_buffer.buf != orig_host_buffer
? MAXPACKET : 1024, &host_buffer.ptr,
NULL, NULL, NULL);
NULL, NULL, NULL, NULL);

if (n < 0)
{
Expand Down Expand Up @@ -298,23 +298,23 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
name = cp;
}

int anslen = 2048;
union
{
querybuf *buf;
u_char *ptr;
} host_buffer;
querybuf *orig_host_buffer;
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (anslen);
host_buffer.buf = orig_host_buffer = (querybuf *) alloca (2048);
u_char *ans2p = NULL;
int nans2p = 0;
int resplen2 = 0;
int ans2p_malloced = 0;

int olderr = errno;
enum nss_status status;
int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC,
host_buffer.buf->buf, anslen, &host_buffer.ptr,
&ans2p, &nans2p, &resplen2);
host_buffer.buf->buf, 2048, &host_buffer.ptr,
&ans2p, &nans2p, &resplen2, &ans2p_malloced);
if (n >= 0)
{
status = gaih_getanswer (host_buffer.buf, n, (const querybuf *) ans2p,
Expand Down Expand Up @@ -351,10 +351,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
}

/* Check whether ans2p was separately allocated. */
if (host_buffer.buf != orig_host_buffer)
anslen = MAXPACKET;
if (ans2p != NULL
&& (ans2p < host_buffer.ptr || ans2p >= host_buffer.ptr + anslen))
if (ans2p_malloced)
free (ans2p);

if (host_buffer.buf != orig_host_buffer)
Expand Down Expand Up @@ -465,7 +462,7 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
strcpy (qp, "].ip6.arpa");
n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR,
host_buffer.buf->buf, 1024, &host_buffer.ptr,
NULL, NULL, NULL);
NULL, NULL, NULL, NULL);
if (n >= 0)
goto got_it_already;
}
Expand All @@ -486,14 +483,14 @@ _nss_dns_gethostbyaddr2_r (const void *addr, socklen_t len, int af,
}

n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
1024, &host_buffer.ptr, NULL, NULL, NULL);
1024, &host_buffer.ptr, NULL, NULL, NULL, NULL);
if (n < 0 && af == AF_INET6 && (_res.options & RES_NOIP6DOTINT) == 0)
{
strcpy (qp, "ip6.int");
n = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, host_buffer.buf->buf,
host_buffer.buf != orig_host_buffer
? MAXPACKET : 1024, &host_buffer.ptr,
NULL, NULL, NULL);
NULL, NULL, NULL, NULL);
}
if (n < 0)
{
Expand Down
4 changes: 2 additions & 2 deletions resolv/nss_dns/dns-network.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);

anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
1024, &net_buffer.ptr, NULL, NULL, NULL);
1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
if (anslen < 0)
{
/* Nothing found. */
Expand Down Expand Up @@ -205,7 +205,7 @@ _nss_dns_getnetbyaddr_r (uint32_t net, int type, struct netent *result,
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);

anslen = __libc_res_nquery (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
1024, &net_buffer.ptr, NULL, NULL, NULL);
1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
if (anslen < 0)
{
/* Nothing found. */
Expand Down
45 changes: 26 additions & 19 deletions resolv/res_query.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static int
__libc_res_nquerydomain(res_state statp, const char *name, const char *domain,
int class, int type, u_char *answer, int anslen,
u_char **answerp, u_char **answerp2, int *nanswerp2,
int *resplen2);
int *resplen2, int *answerp2_malloced);

/*
* Formulate a normal query, send, and await answer.
Expand All @@ -119,7 +119,8 @@ __libc_res_nquery(res_state statp,
u_char **answerp, /* if buffer needs to be enlarged */
u_char **answerp2,
int *nanswerp2,
int *resplen2)
int *resplen2,
int *answerp2_malloced)
{
HEADER *hp = (HEADER *) answer;
HEADER *hp2;
Expand Down Expand Up @@ -224,7 +225,8 @@ __libc_res_nquery(res_state statp,
}
assert (answerp == NULL || (void *) *answerp == (void *) answer);
n = __libc_res_nsend(statp, query1, nquery1, query2, nquery2, answer,
anslen, answerp, answerp2, nanswerp2, resplen2);
anslen, answerp, answerp2, nanswerp2, resplen2,
answerp2_malloced);
if (use_malloc)
free (buf);
if (n < 0) {
Expand Down Expand Up @@ -316,7 +318,7 @@ res_nquery(res_state statp,
int anslen) /* size of answer buffer */
{
return __libc_res_nquery(statp, name, class, type, answer, anslen,
NULL, NULL, NULL, NULL);
NULL, NULL, NULL, NULL, NULL);
}
libresolv_hidden_def (res_nquery)

Expand All @@ -335,7 +337,8 @@ __libc_res_nsearch(res_state statp,
u_char **answerp,
u_char **answerp2,
int *nanswerp2,
int *resplen2)
int *resplen2,
int *answerp2_malloced)
{
const char *cp, * const *domain;
HEADER *hp = (HEADER *) answer;
Expand All @@ -360,7 +363,7 @@ __libc_res_nsearch(res_state statp,
if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL)
return (__libc_res_nquery(statp, cp, class, type, answer,
anslen, answerp, answerp2,
nanswerp2, resplen2));
nanswerp2, resplen2, answerp2_malloced));

#ifdef DEBUG
if (statp->options & RES_DEBUG)
Expand All @@ -377,7 +380,8 @@ __libc_res_nsearch(res_state statp,
if (dots >= statp->ndots || trailing_dot) {
ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
answer, anslen, answerp,
answerp2, nanswerp2, resplen2);
answerp2, nanswerp2, resplen2,
answerp2_malloced);
if (ret > 0 || trailing_dot)
return (ret);
saved_herrno = h_errno;
Expand All @@ -386,11 +390,11 @@ __libc_res_nsearch(res_state statp,
answer = *answerp;
anslen = MAXPACKET;
}
if (answerp2
&& (*answerp2 < answer || *answerp2 >= answer + anslen))
if (answerp2 && *answerp2_malloced)
{
free (*answerp2);
*answerp2 = NULL;
*answerp2_malloced = 0;
}
}

Expand All @@ -417,20 +421,19 @@ __libc_res_nsearch(res_state statp,
class, type,
answer, anslen, answerp,
answerp2, nanswerp2,
resplen2);
resplen2, answerp2_malloced);
if (ret > 0)
return (ret);

if (answerp && *answerp != answer) {
answer = *answerp;
anslen = MAXPACKET;
}
if (answerp2
&& (*answerp2 < answer
|| *answerp2 >= answer + anslen))
if (answerp2 && *answerp2_malloced)
{
free (*answerp2);
*answerp2 = NULL;
*answerp2_malloced = 0;
}

/*
Expand Down Expand Up @@ -486,7 +489,8 @@ __libc_res_nsearch(res_state statp,
&& !(tried_as_is || root_on_list)) {
ret = __libc_res_nquerydomain(statp, name, NULL, class, type,
answer, anslen, answerp,
answerp2, nanswerp2, resplen2);
answerp2, nanswerp2, resplen2,
answerp2_malloced);
if (ret > 0)
return (ret);
}
Expand All @@ -498,10 +502,11 @@ __libc_res_nsearch(res_state statp,
* else send back meaningless H_ERRNO, that being the one from
* the last DNSRCH we did.
*/
if (answerp2 && (*answerp2 < answer || *answerp2 >= answer + anslen))
if (answerp2 && *answerp2_malloced)
{
free (*answerp2);
*answerp2 = NULL;
*answerp2_malloced = 0;
}
if (saved_herrno != -1)
RES_SET_H_ERRNO(statp, saved_herrno);
Expand All @@ -521,7 +526,7 @@ res_nsearch(res_state statp,
int anslen) /* size of answer */
{
return __libc_res_nsearch(statp, name, class, type, answer,
anslen, NULL, NULL, NULL, NULL);
anslen, NULL, NULL, NULL, NULL, NULL);
}
libresolv_hidden_def (res_nsearch)

Expand All @@ -539,7 +544,8 @@ __libc_res_nquerydomain(res_state statp,
u_char **answerp,
u_char **answerp2,
int *nanswerp2,
int *resplen2)
int *resplen2,
int *answerp2_malloced)
{
char nbuf[MAXDNAME];
const char *longname = nbuf;
Expand Down Expand Up @@ -581,7 +587,7 @@ __libc_res_nquerydomain(res_state statp,
}
return (__libc_res_nquery(statp, longname, class, type, answer,
anslen, answerp, answerp2, nanswerp2,
resplen2));
resplen2, answerp2_malloced));
}

int
Expand All @@ -593,7 +599,8 @@ res_nquerydomain(res_state statp,
int anslen) /* size of answer */
{
return __libc_res_nquerydomain(statp, name, domain, class, type,
answer, anslen, NULL, NULL, NULL, NULL);
answer, anslen, NULL, NULL, NULL, NULL,
NULL);
}
libresolv_hidden_def (res_nquerydomain)

Expand Down
Loading

0 comments on commit ab09bf6

Please sign in to comment.