mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Fix problem with netlink socket and TCP DNS.
When dnsmasq forks a child to handle a TCP connection, that child inherits the netlink socket that the main process has open. The child never uses that socket, but there's a chance that when the main process uses the netlink socket, the answer will go to a child process which has a copy of the socket. This causes the main process to block forever awaiting the answer which never comes. The solution is for the child process to close the netlink socket it inherits after the fork(). There's a nasty race because the error decribed above could still occur in the window between the fork() and the close() syscalls. That's fixed by blocking the parent awaiting a single byte sent though the pipe the two processes share. This byte is sent by the child after calling close() on the netlink socket. Thanks to Alin Năstac for spotting this.
This commit is contained in:
@@ -1861,8 +1861,26 @@ static void check_dns_listeners(time_t now)
|
||||
for (i = 0; i < MAX_PROCS; i++)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
{
|
||||
char a;
|
||||
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
retry_send(read(pipefd[0], &a, 1));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1894,9 +1912,15 @@ static void check_dns_listeners(time_t now)
|
||||
terminate the process. */
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
char a = 0;
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re netlink socket. */
|
||||
close(daemon->netlinkfd);
|
||||
retry_send(write(pipefd[1], &a, 1));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* start with no upstream connections. */
|
||||
|
||||
Reference in New Issue
Block a user