8173848: realpath is unsafe
authorstuefe
Mon, 13 Mar 2017 20:23:11 +0100
changeset 46331 e3017116b9e5
parent 46329 53ccc37bda19
child 46332 cdb55474bc2e
8173848: realpath is unsafe Summary: Fix occurrences of realpath in hotspot to use safe POSIX.1-2008 form. Reviewed-by: dsamersoff, dholmes, clanger
hotspot/src/os/aix/vm/os_aix.cpp
hotspot/src/os/bsd/vm/os_bsd.cpp
hotspot/src/os/linux/vm/os_linux.cpp
hotspot/src/os/posix/vm/os_posix.cpp
hotspot/src/os/posix/vm/os_posix.hpp
hotspot/src/os/solaris/vm/os_solaris.cpp
--- a/hotspot/src/os/aix/vm/os_aix.cpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/aix/vm/os_aix.cpp	Mon Mar 13 20:23:11 2017 +0100
@@ -1576,7 +1576,7 @@
   Dl_info dlinfo;
   int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
   assert(ret != 0, "cannot locate libjvm");
-  char* rp = realpath((char *)dlinfo.dli_fname, buf);
+  char* rp = os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen);
   assert(rp != NULL, "error in realpath(): maybe the 'path' argument is too long?");
 
   strncpy(saved_jvm_path, buf, sizeof(saved_jvm_path));
--- a/hotspot/src/os/bsd/vm/os_bsd.cpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/bsd/vm/os_bsd.cpp	Mon Mar 13 20:23:11 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1754,7 +1754,7 @@
   assert(ret, "cannot locate libjvm");
   char *rp = NULL;
   if (ret && dli_fname[0] != '\0') {
-    rp = realpath(dli_fname, buf);
+    rp = os::Posix::realpath(dli_fname, buf, buflen);
   }
   if (rp == NULL) {
     return;
@@ -1786,7 +1786,7 @@
         p = strrchr(buf, '/');
         assert(strstr(p, "/libjvm") == p, "invalid library name");
 
-        rp = realpath(java_home_var, buf);
+        rp = os::Posix::realpath(java_home_var, buf, buflen);
         if (rp == NULL) {
           return;
         }
@@ -1820,7 +1820,7 @@
           snprintf(buf + len, buflen-len, "/libjvm%s", JNI_LIB_SUFFIX);
         } else {
           // Fall back to path of current library
-          rp = realpath(dli_fname, buf);
+          rp = os::Posix::realpath(dli_fname, buf, buflen);
           if (rp == NULL) {
             return;
           }
--- a/hotspot/src/os/linux/vm/os_linux.cpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/linux/vm/os_linux.cpp	Mon Mar 13 20:23:11 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -2318,7 +2318,7 @@
   assert(ret, "cannot locate libjvm");
   char *rp = NULL;
   if (ret && dli_fname[0] != '\0') {
-    rp = realpath(dli_fname, buf);
+    rp = os::Posix::realpath(dli_fname, buf, buflen);
   }
   if (rp == NULL) {
     return;
@@ -2352,7 +2352,7 @@
         }
         assert(strstr(p, "/libjvm") == p, "invalid library name");
 
-        rp = realpath(java_home_var, buf);
+        rp = os::Posix::realpath(java_home_var, buf, buflen);
         if (rp == NULL) {
           return;
         }
@@ -2373,7 +2373,7 @@
           snprintf(buf + len, buflen-len, "/hotspot/libjvm.so");
         } else {
           // Go back to path of .so
-          rp = realpath(dli_fname, buf);
+          rp = os::Posix::realpath(dli_fname, buf, buflen);
           if (rp == NULL) {
             return;
           }
--- a/hotspot/src/os/posix/vm/os_posix.cpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/posix/vm/os_posix.cpp	Mon Mar 13 20:23:11 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1105,6 +1105,48 @@
   return buf;
 }
 
+char* os::Posix::realpath(const char* filename, char* outbuf, size_t outbuflen) {
+
+  if (filename == NULL || outbuf == NULL || outbuflen < 1) {
+    assert(false, "os::Posix::realpath: invalid arguments.");
+    errno = EINVAL;
+    return NULL;
+  }
+
+  char* result = NULL;
+
+  // This assumes platform realpath() is implemented according to POSIX.1-2008.
+  // POSIX.1-2008 allows to specify NULL for the output buffer, in which case
+  // output buffer is dynamically allocated and must be ::free()'d by the caller.
+  char* p = ::realpath(filename, NULL);
+  if (p != NULL) {
+    if (strlen(p) < outbuflen) {
+      strcpy(outbuf, p);
+      result = outbuf;
+    } else {
+      errno = ENAMETOOLONG;
+    }
+    ::free(p); // *not* os::free
+  } else {
+    // Fallback for platforms struggling with modern Posix standards (AIX 5.3, 6.1). If realpath
+    // returns EINVAL, this may indicate that realpath is not POSIX.1-2008 compatible and
+    // that it complains about the NULL we handed down as user buffer.
+    // In this case, use the user provided buffer but at least check whether realpath caused
+    // a memory overwrite.
+    if (errno == EINVAL) {
+      outbuf[outbuflen - 1] = '\0';
+      p = ::realpath(filename, outbuf);
+      if (p != NULL) {
+        guarantee(outbuf[outbuflen - 1] == '\0', "realpath buffer overwrite detected.");
+        result = p;
+      }
+    }
+  }
+  return result;
+
+}
+
+
 // Check minimum allowable stack sizes for thread creation and to initialize
 // the java system classes, including StackOverflowError - depends on page
 // size.
--- a/hotspot/src/os/posix/vm/os_posix.hpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/posix/vm/os_posix.hpp	Mon Mar 13 20:23:11 2017 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -96,6 +96,13 @@
   // to buf with len buflen; buf is returned.
   static char* describe_pthread_attr(char* buf, size_t buflen, const pthread_attr_t* attr);
 
+  // A safe implementation of realpath which will not cause a buffer overflow if the resolved path
+  //   is longer than PATH_MAX.
+  // On success, returns 'outbuf', which now contains the path.
+  // On error, it will return NULL and set errno. The content of 'outbuf' is undefined.
+  // On truncation error ('outbuf' too small), it will return NULL and set errno to ENAMETOOLONG.
+  static char* realpath(const char* filename, char* outbuf, size_t outbuflen);
+
 };
 
 /*
--- a/hotspot/src/os/solaris/vm/os_solaris.cpp	Wed Mar 15 10:25:37 2017 -0400
+++ b/hotspot/src/os/solaris/vm/os_solaris.cpp	Mon Mar 13 20:23:11 2017 +0100
@@ -2034,7 +2034,9 @@
   int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo);
   assert(ret != 0, "cannot locate libjvm");
   if (ret != 0 && dlinfo.dli_fname != NULL) {
-    realpath((char *)dlinfo.dli_fname, buf);
+    if (os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen) == NULL) {
+      return;
+    }
   } else {
     buf[0] = '\0';
     return;
@@ -2065,7 +2067,9 @@
         p = strrchr(buf, '/');
         assert(strstr(p, "/libjvm") == p, "invalid library name");
 
-        realpath(java_home_var, buf);
+        if (os::Posix::realpath(java_home_var, buf, buflen) == NULL) {
+          return;
+        }
         // determine if this is a legacy image or modules image
         // modules image doesn't have "jre" subdirectory
         len = strlen(buf);
@@ -2082,7 +2086,9 @@
           snprintf(buf + len, buflen-len, "/hotspot/libjvm.so");
         } else {
           // Go back to path of .so
-          realpath((char *)dlinfo.dli_fname, buf);
+          if (os::Posix::realpath((char *)dlinfo.dli_fname, buf, buflen) == NULL) {
+            return;
+          }
         }
       }
     }