Allow blocking calls when parsing etcResolver/hosts files (#11009)

Motivation:

When etcResolver/hosts files are parsed, FileInputStream.read(...) is internally called by

- UnixResolverDnsServerAddressStreamProvider#parseEtcResolverSearchDomains
- UnixResolverDnsServerAddressStreamProvider#parseEtcResolverOptions
- HostsFileParser#parse

This will cause the error below when BlockHound is enabled

reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes
     	at java.io.FileInputStream.readBytes(FileInputStream.java)
     	at java.io.FileInputStream.read(FileInputStream.java:255)

Modifications:

- Add whitelist entries to BlockHound configuration
- Fix typos in UnixResolverDnsServerAddressStreamProvider
- Add tests

Result:

Fixes #11004
This commit is contained in:
Violeta Georgieva 2021-02-11 12:03:20 +02:00 committed by GitHub
parent bb9370f2a2
commit 6808d7582a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 44 additions and 7 deletions

View File

@ -119,6 +119,18 @@ class Hidden {
"io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider", "io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
"parse"); "parse");
builder.allowBlockingCallsInside(
"io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
"parseEtcResolverSearchDomains");
builder.allowBlockingCallsInside(
"io.netty.resolver.dns.UnixResolverDnsServerAddressStreamProvider",
"parseEtcResolverOptions");
builder.allowBlockingCallsInside(
"io.netty.resolver.HostsFileParser",
"parse");
builder.nonBlockingThreadPredicate(new Function<Predicate<Thread>, Predicate<Thread>>() { builder.nonBlockingThreadPredicate(new Function<Predicate<Thread>, Predicate<Thread>>() {
@Override @Override
public Predicate<Thread> apply(final Predicate<Thread> p) { public Predicate<Thread> apply(final Predicate<Thread> p) {

View File

@ -86,7 +86,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
* the default DNS server to use, and also overrides for individual domains. Also parse list of files of the format * the default DNS server to use, and also overrides for individual domains. Also parse list of files of the format
* <a href=" * <a href="
* https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/resolver.5.html"> * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/resolver.5.html">
* /etc/resolver</a> which may contain multiple files to override the name servers used for multimple domains. * /etc/resolver</a> which may contain multiple files to override the name servers used for multiple domains.
* @param etcResolvConf <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>. * @param etcResolvConf <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>.
* @param etcResolverFiles List of files of the format defined in * @param etcResolverFiles List of files of the format defined in
* <a href=" * <a href="
@ -121,7 +121,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
* the default DNS server to use, and also overrides for individual domains. Also parse a directory of the format * the default DNS server to use, and also overrides for individual domains. Also parse a directory of the format
* <a href=" * <a href="
* https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/resolver.5.html"> * https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man5/resolver.5.html">
* /etc/resolver</a> which may contain multiple files to override the name servers used for multimple domains. * /etc/resolver</a> which may contain multiple files to override the name servers used for multiple domains.
* @param etcResolvConf <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>. * @param etcResolvConf <a href="https://linux.die.net/man/5/resolver">/etc/resolv.conf</a>.
* @param etcResolverDir Directory containing files of the format defined in * @param etcResolverDir Directory containing files of the format defined in
* <a href=" * <a href="
@ -379,7 +379,7 @@ public final class UnixResolverDnsServerAddressStreamProvider implements DnsServ
} else if (line.startsWith(SEARCH_ROW_LABEL)) { } else if (line.startsWith(SEARCH_ROW_LABEL)) {
int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length()); int i = indexOfNonWhiteSpace(line, SEARCH_ROW_LABEL.length());
if (i >= 0) { if (i >= 0) {
// May contain more then one entry, either seperated by whitespace or tab. // May contain more then one entry, either separated by whitespace or tab.
// See https://linux.die.net/man/5/resolver // See https://linux.die.net/man/5/resolver
String[] domains = WHITESPACE_PATTERN.split(line.substring(i)); String[] domains = WHITESPACE_PATTERN.split(line.substring(i));
Collections.addAll(searchDomains, domains); Collections.addAll(searchDomains, domains);

View File

@ -26,6 +26,7 @@ import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
@ -35,7 +36,7 @@ import io.netty.handler.ssl.SslHandshakeCompletionEvent;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.resolver.dns.DnsServerAddressStreamProvider; import io.netty.resolver.dns.DnsNameResolverBuilder;
import io.netty.resolver.dns.DnsServerAddressStreamProviders; import io.netty.resolver.dns.DnsServerAddressStreamProviders;
import io.netty.util.HashedWheelTimer; import io.netty.util.HashedWheelTimer;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
@ -59,6 +60,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@ -320,7 +322,30 @@ public class NettyBlockHoundIntegrationTest {
} }
@Test(timeout = 5000L) @Test(timeout = 5000L)
public void testParseEtcResolverFilesAllowsBlockingCalls() throws InterruptedException { public void testUnixResolverDnsServerAddressStreamProvider_Parse() throws InterruptedException {
doTestParseResolverFilesAllowsBlockingCalls(DnsServerAddressStreamProviders::unixDefault);
}
@Test(timeout = 5000L)
public void testHostsFileParser_Parse() throws InterruptedException {
doTestParseResolverFilesAllowsBlockingCalls(DnsNameResolverBuilder::new);
}
@Test(timeout = 5000L)
public void testUnixResolverDnsServerAddressStreamProvider_ParseEtcResolverSearchDomainsAndOptions()
throws InterruptedException {
NioEventLoopGroup group = new NioEventLoopGroup();
try {
DnsNameResolverBuilder builder = new DnsNameResolverBuilder(group.next())
.channelFactory(NioDatagramChannel::new);
doTestParseResolverFilesAllowsBlockingCalls(builder::build);
} finally {
group.shutdownGracefully();
}
}
private static void doTestParseResolverFilesAllowsBlockingCalls(Callable<Object> callable)
throws InterruptedException {
SingleThreadEventExecutor executor = SingleThreadEventExecutor executor =
new SingleThreadEventExecutor(null, new DefaultThreadFactory("test"), true) { new SingleThreadEventExecutor(null, new DefaultThreadFactory("test"), true) {
@Override @Override
@ -335,11 +360,11 @@ public class NettyBlockHoundIntegrationTest {
}; };
try { try {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
List<DnsServerAddressStreamProvider> result = new ArrayList<>(); List<Object> result = new ArrayList<>();
List<Throwable> error = new ArrayList<>(); List<Throwable> error = new ArrayList<>();
executor.execute(() -> { executor.execute(() -> {
try { try {
result.add(DnsServerAddressStreamProviders.unixDefault()); result.add(callable.call());
} catch (Throwable t) { } catch (Throwable t) {
error.add(t); error.add(t);
} }