Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6595834

InetAddress.isReachable is not thread safe when using ICMP ECHO.

    XMLWordPrintable

    Details

    • Subcomponent:
    • Resolved In Build:
      b20
    • CPU:
      x86
    • OS:
      linux_redhat_9.0, solaris_2.5.1
    • Verification:
      Not verified

      Description

      FULL PRODUCT VERSION :
      java version "1.6.0_01"
      Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
      Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)

      ADDITIONAL OS VERSION INFORMATION :
      Linux name 2.6.9-42.EL #1 Sat Aug 12 09:17:58 CDT 2006 i686 i686 i386 GNU/Linux

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      dual-homed machine (but that shouldn't matter).
      must run as root.

      A DESCRIPTION OF THE PROBLEM :
      The return value of InetAddress.isReachable() may represent the results of another thread's call to isReachable. This only happens if isReachable uses the ICMP ECHO method to gather its results; this means the application must be run as root on linux. The problem does not exist on Windows.

      Thread-safety of InetAddress and isReachable is not documented. However, another defect in the Sun database states that InetAddress is thread safe because it's immutable.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See the attached test code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      is 192.0.2.250 reachable?
      is 127.0.0.1 reachable?
      is 192.0.2.251 reachable?
      is 192.0.2.252 reachable?
      192.0.2.250 not reachable on loop 0
      192.0.2.250 not reachable on loop 1
      192.0.2.250 not reachable on loop 2
      192.0.2.252 not reachable on loop 0
      192.0.2.252 not reachable on loop 1
      192.0.2.252 not reachable on loop 2
      192.0.2.251 not reachable on loop 0
      192.0.2.251 not reachable on loop 1
      192.0.2.251 not reachable on loop 2
      127.0.0.1 exists!
      Finished!

      ACTUAL -
      sudo java -cp . Ping 192.0.2.250 127.0.0.1 192.0.2.251 192.0.2.252

      is 192.0.2.250 reachable?
      is 127.0.0.1 reachable?
      127.0.0.1 exists!
      is 192.0.2.251 reachable?
      192.0.2.250 exists!
      is 192.0.2.252 reachable?
      192.0.2.251 not reachable on loop 0
      192.0.2.252 not reachable on loop 0
      192.0.2.251 not reachable on loop 1
      192.0.2.252 not reachable on loop 1
      192.0.2.251 not reachable on loop 2
      192.0.2.252 not reachable on loop 2
      Finished!


      (Notice that 192.0.2.250 is reported as existing but it does not).

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.net.*;
      import java.io.IOException;
      import java.util.concurrent.*;
      import java.util.*;

      /**
       * Test program to prove that InetAddress.isReachableWrapper() is not thread safe when ICMP ECHO is used.
       * Run this test with 1 IP address that DOES exist on your network and N IP addresses that do NOT exist.
       * This program will then falsely indicate that they were "reachable".
       * To run:
       * java -cp . Ping <ip1> <ip2> <ip3>
       *
       * NOTE: must run as root to use ICMP ECHO (otherwise this test is not valid).
       * This defect does not affect Windows because ICMP ECHO is not used.
       * Remember, at least one IP address must be reachable to trigger this defect and is should be listed 2nd, or 3rd.
       *
       * Work-arounds:
       * - synchronize the isReachableWrapper method (or at least the call to 'isReachable'.
       * - force isReachable to not use ICMP (I'm not sure how to force this).
       */
      public class Ping {
          private static ExecutorService executor = Executors.newFixedThreadPool(10);

          public static void main(String[] args) {
              Collection<Callable<PingDevice>> tasks = new ArrayList<Callable<PingDevice>>();
              try {
                  for (String ip : args) {
                      tasks.add(new PingDevice(ip));
                  }
                  executor.invokeAll(tasks); // blocks until complete
                  System.out.println("Finished!");
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  executor.shutdownNow();
              }

          }

          public static boolean isReachableWrapper(String ip, int retries, int timeout) {
              System.out.println("is " + ip + " reachable?");
              InetAddress address = null;
              try {
                  address = InetAddress.getByName(ip);
              } catch (UnknownHostException e) {
                  System.out.println("Unable to lookup " + ip);
                  return false;
              }
              for (int i = 0; i < retries + 1; i++) {
                  try {
                      //synchronized(InetAddress.class) { // required to work-around bug.
                      if (address.isReachable(timeout)) {
                          System.out.println(ip + " exists!");
                          return true;
                      }
                      //}
                      System.out.println(ip + " not reachable on loop " + i);
                  } catch (IOException e) {
                      //e.printStackTrace();
                      System.out.println("IOE: Unable to reach " + ip + " on try " + i);
                  }
              }
              return false;
          }

          private static class PingDevice implements Callable<PingDevice> {
              String ip;
              boolean pingable = false;

              public PingDevice(String ip) {
                  this.ip = ip;
              }

              public PingDevice call() throws Exception {
                  try {
                      //System.out.println("lookup " + ip);
                      InetAddress addr = InetAddress.getByName(ip);
                      // try pinging 3 times, waiting up to .5 second for each ping.
                      pingable = isReachableWrapper(addr.getHostAddress(), 2, 500);
                  } catch (UnknownHostException e) {
                      e.printStackTrace();
                  }
                  return this;
              }
          }
      }


      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I have created my own synchronized wrapper method for isReachable. Of course, everything within an application must be sure to call the wrapper method.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              chegar Chris Hegarty
              Reporter:
              ndcosta Nelson Dcosta (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: