Skip to content

ResolverCache: make the cache before the TTLs - #64

Open
duncanmcqueen wants to merge 1 commit into
ingelabs:masterfrom
duncanmcqueen:fix-resolvercache-init
Open

duncanmcqueen wants to merge 1 commit into
ingelabs:masterfrom
duncanmcqueen:fix-resolvercache-init

Conversation

@duncanmcqueen

Copy link
Copy Markdown
Contributor

Split from #59.

Problem and fix

The static initializer of java.net.ResolverCache sets POSITIVE_TTL and NEGATIVE_TTL before cache. To get the TTLs, it calls Security.getProperty(). This can initialize java.security.Security, which reads its provider file from the gnu.classpath.home.url URL.

If that URL is not a local file, Security opens a network connection. The connection resolves a host name with ResolverCache.getHostByName(). ResolverCache is not fully initialized yet, so cache is null:

  1. ResolverCache.get() throws NullPointerException.
  2. InetSocketAddress catches all exceptions and makes an unresolved address.
  3. Socket.connect() throws UnresolvedAddressException. Security.loadProviders() does not catch this exception.
  4. The initialization of Security, ResolverCache and InetAddress fails with ExceptionInInitializerError. After that, no network operation works.

This change declares cache and killqueue before the TTLs.

How to reproduce

The test sets gnu.classpath.home.url to an HTTP URL and then resolves a host name. Run it as the first program in the VM (nothing must initialize Security or InetAddress before).

javac ResolverCacheInit.java
java ResolverCacheInit
ResolverCacheInit.java
import java.net.InetAddress;

/**
 * The first host name lookup initializes ResolverCache. Its static
 * initializer reads two security properties, which initializes
 * java.security.Security. Security reads its provider file from the
 * gnu.classpath.home.url URL. If that URL is not a local file, opening it
 * resolves a host name again, inside ResolverCache's static initializer,
 * before ResolverCache.cache is set.
 *
 * Run it before anything else uses Security or InetAddress:
 *
 *   java ResolverCacheInit
 */
public class ResolverCacheInit
{
  public static void main(String[] args) throws Exception
  {
    System.setProperty("gnu.classpath.home.url", "http://localhost:1/none");
    try
      {
        InetAddress a = InetAddress.getByName("localhost");
        System.out.println("PASS: " + a);
        System.exit(0);
      }
    catch (Throwable t)
      {
        System.out.println("FAIL: " + t);
        t.printStackTrace(System.out);
        System.exit(1);
      }
  }
}

Results

Linux x86_64, ingelabs/classpath master (0406074), JamVM master, javac from JDK 8, autoconf 2.73, automake 1.19, the configure options of the CI Linux job, out-of-tree build. "Before" is master. "After" is master with this commit.

Before:
FAIL: java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
   at java.net.ResolverCache.getTTL(ResolverCache.java:90)
   at java.net.ResolverCache.<clinit>(ResolverCache.java:77)
   at java.net.InetAddress.internalGetCanonicalHostName(InetAddress.java:319)
   at java.net.InetAddress.getCanonicalHostName(InetAddress.java:334)
   at java.net.InetAddress.getHostName(InetAddress.java:307)
   at java.net.InetAddress.<clinit>(InetAddress.java:92)
   at ResolverCacheInit.main(ResolverCacheInit.java:22)
Caused by: java.nio.channels.UnresolvedAddressException
   at gnu.java.nio.SocketChannelImpl.connect(SocketChannelImpl.java:160)
   ...
   at java.net.URL.openStream(URL.java:737)
   at java.security.Security.loadProviders(Security.java:129)
   at java.security.Security.<clinit>(Security.java:78)
   at java.net.ResolverCache.getTTL(ResolverCache.java:90)

After:
PASS: localhost/127.0.0.1

I first found the problem with Kaffe on Haiku.

Platforms

Not specific to Haiku. The problem occurs on all systems and VMs when gnu.classpath.home.url is not a local file. I reproduced it on Linux with JamVM.

AI assistance

An AI model (Claude Code, by Anthropic) wrote this change, the test and this text. The repository owner directed the work and checked the results. Please review it as you review other contributions.

🤖 Generated with Claude Code

The static initializer computed POSITIVE_TTL and NEGATIVE_TTL first.
Reading the security properties for them can resolve a host name,
which uses the cache before it exists (NullPointerException). Declare
the cache and the kill queue first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: duncanmcqueen <206344294+duncanmcqueen@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant