mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Fix rare problem allocating frec for DNSSEC.
A call to get_new_frec() for a DNSSEC query could manage to free the original frec that we're doing the DNSSEC query to validate. Bad things then happen. This requires that the original frec is old, so it doesn't happen in practice. I found it when running under gdb, and there have been reports of SEGV associated with large system-clock warps which are probably the same thing.
This commit is contained in:
@@ -1683,7 +1683,7 @@ static int set_dns_listeners(time_t now)
|
||||
|
||||
/* will we be able to get memory? */
|
||||
if (daemon->port != 0)
|
||||
get_new_frec(now, &wait, 0);
|
||||
get_new_frec(now, &wait, NULL);
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
|
||||
@@ -1324,7 +1324,7 @@ void receive_query(struct listener *listen, time_t now);
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force);
|
||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
unsigned int iface);
|
||||
|
||||
@@ -348,7 +348,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
type &= ~SERV_DO_DNSSEC;
|
||||
|
||||
if (daemon->servers && !flags)
|
||||
forward = get_new_frec(now, NULL, 0);
|
||||
forward = get_new_frec(now, NULL, NULL);
|
||||
/* table full - flags == 0, return REFUSED */
|
||||
|
||||
if (forward)
|
||||
@@ -1039,7 +1039,9 @@ void reply_query(int fd, int family, time_t now)
|
||||
/* Find the original query that started it all.... */
|
||||
for (orig = forward; orig->dependent; orig = orig->dependent);
|
||||
|
||||
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, 1)))
|
||||
/* Make sure we don't expire and free the orig frec during the
|
||||
allocation of a new one. */
|
||||
if (--orig->work_counter == 0 || !(new = get_new_frec(now, NULL, orig)))
|
||||
status = STAT_ABANDONED;
|
||||
else
|
||||
{
|
||||
@@ -2251,9 +2253,10 @@ static void free_frec(struct frec *f)
|
||||
else return *wait zero if one available, or *wait is delay to
|
||||
when the oldest in-use record will expire. Impose an absolute
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
||||
If force is set, always return a result, even if we have
|
||||
to allocate above the limit. */
|
||||
struct frec *get_new_frec(time_t now, int *wait, int force)
|
||||
If force is non-NULL, always return a result, even if we have
|
||||
to allocate above the limit, and never free the record pointed
|
||||
to by the force argument. */
|
||||
struct frec *get_new_frec(time_t now, int *wait, struct frec *force)
|
||||
{
|
||||
struct frec *f, *oldest, *target;
|
||||
int count;
|
||||
@@ -2270,7 +2273,7 @@ struct frec *get_new_frec(time_t now, int *wait, int force)
|
||||
/* Don't free DNSSEC sub-queries here, as we may end up with
|
||||
dangling references to them. They'll go when their "real" query
|
||||
is freed. */
|
||||
if (!f->dependent)
|
||||
if (!f->dependent && f != force)
|
||||
#endif
|
||||
{
|
||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||
|
||||
Reference in New Issue
Block a user