src/os/bsd/vm/perfMemory_bsd.cpp

changeset 7495
42f27b59c550
parent 7074
833b0f92429a
parent 7493
d7b6bdd51abe
child 7535
7ae4e26cb1e0
child 7715
f3ffb37f88a6
     1.1 --- a/src/os/bsd/vm/perfMemory_bsd.cpp	Wed Nov 26 08:14:21 2014 -0800
     1.2 +++ b/src/os/bsd/vm/perfMemory_bsd.cpp	Wed Nov 26 08:57:40 2014 -0800
     1.3 @@ -197,7 +197,38 @@
     1.4  }
     1.5  
     1.6  
     1.7 -// check if the given path is considered a secure directory for
     1.8 +// Check if the given statbuf is considered a secure directory for
     1.9 +// the backing store files. Returns true if the directory is considered
    1.10 +// a secure location. Returns false if the statbuf is a symbolic link or
    1.11 +// if an error occurred.
    1.12 +//
    1.13 +static bool is_statbuf_secure(struct stat *statp) {
    1.14 +  if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) {
    1.15 +    // The path represents a link or some non-directory file type,
    1.16 +    // which is not what we expected. Declare it insecure.
    1.17 +    //
    1.18 +    return false;
    1.19 +  }
    1.20 +  // We have an existing directory, check if the permissions are safe.
    1.21 +  //
    1.22 +  if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) {
    1.23 +    // The directory is open for writing and could be subjected
    1.24 +    // to a symlink or a hard link attack. Declare it insecure.
    1.25 +    //
    1.26 +    return false;
    1.27 +  }
    1.28 +  // See if the uid of the directory matches the effective uid of the process.
    1.29 +  //
    1.30 +  if (statp->st_uid != geteuid()) {
    1.31 +    // The directory was not created by this user, declare it insecure.
    1.32 +    //
    1.33 +    return false;
    1.34 +  }
    1.35 +  return true;
    1.36 +}
    1.37 +
    1.38 +
    1.39 +// Check if the given path is considered a secure directory for
    1.40  // the backing store files. Returns true if the directory exists
    1.41  // and is considered a secure location. Returns false if the path
    1.42  // is a symbolic link or if an error occurred.
    1.43 @@ -211,27 +242,185 @@
    1.44      return false;
    1.45    }
    1.46  
    1.47 -  // the path exists, now check it's mode
    1.48 -  if (S_ISLNK(statbuf.st_mode) || !S_ISDIR(statbuf.st_mode)) {
    1.49 -    // the path represents a link or some non-directory file type,
    1.50 -    // which is not what we expected. declare it insecure.
    1.51 -    //
    1.52 +  // The path exists, see if it is secure.
    1.53 +  return is_statbuf_secure(&statbuf);
    1.54 +}
    1.55 +
    1.56 +
    1.57 +// Check if the given directory file descriptor is considered a secure
    1.58 +// directory for the backing store files. Returns true if the directory
    1.59 +// exists and is considered a secure location. Returns false if the path
    1.60 +// is a symbolic link or if an error occurred.
    1.61 +//
    1.62 +static bool is_dirfd_secure(int dir_fd) {
    1.63 +  struct stat statbuf;
    1.64 +  int result = 0;
    1.65 +
    1.66 +  RESTARTABLE(::fstat(dir_fd, &statbuf), result);
    1.67 +  if (result == OS_ERR) {
    1.68      return false;
    1.69    }
    1.70 -  else {
    1.71 -    // we have an existing directory, check if the permissions are safe.
    1.72 -    //
    1.73 -    if ((statbuf.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
    1.74 -      // the directory is open for writing and could be subjected
    1.75 -      // to a symlnk attack. declare it insecure.
    1.76 -      //
    1.77 -      return false;
    1.78 +
    1.79 +  // The path exists, now check its mode.
    1.80 +  return is_statbuf_secure(&statbuf);
    1.81 +}
    1.82 +
    1.83 +
    1.84 +// Check to make sure fd1 and fd2 are referencing the same file system object.
    1.85 +//
    1.86 +static bool is_same_fsobject(int fd1, int fd2) {
    1.87 +  struct stat statbuf1;
    1.88 +  struct stat statbuf2;
    1.89 +  int result = 0;
    1.90 +
    1.91 +  RESTARTABLE(::fstat(fd1, &statbuf1), result);
    1.92 +  if (result == OS_ERR) {
    1.93 +    return false;
    1.94 +  }
    1.95 +  RESTARTABLE(::fstat(fd2, &statbuf2), result);
    1.96 +  if (result == OS_ERR) {
    1.97 +    return false;
    1.98 +  }
    1.99 +
   1.100 +  if ((statbuf1.st_ino == statbuf2.st_ino) &&
   1.101 +      (statbuf1.st_dev == statbuf2.st_dev)) {
   1.102 +    return true;
   1.103 +  } else {
   1.104 +    return false;
   1.105 +  }
   1.106 +}
   1.107 +
   1.108 +
   1.109 +// Open the directory of the given path and validate it.
   1.110 +// Return a DIR * of the open directory.
   1.111 +//
   1.112 +static DIR *open_directory_secure(const char* dirname) {
   1.113 +  // Open the directory using open() so that it can be verified
   1.114 +  // to be secure by calling is_dirfd_secure(), opendir() and then check
   1.115 +  // to see if they are the same file system object.  This method does not
   1.116 +  // introduce a window of opportunity for the directory to be attacked that
   1.117 +  // calling opendir() and is_directory_secure() does.
   1.118 +  int result;
   1.119 +  DIR *dirp = NULL;
   1.120 +  RESTARTABLE(::open(dirname, O_RDONLY|O_NOFOLLOW), result);
   1.121 +  if (result == OS_ERR) {
   1.122 +    // Directory doesn't exist or is a symlink, so there is nothing to cleanup.
   1.123 +    if (PrintMiscellaneous && Verbose) {
   1.124 +      if (errno == ELOOP) {
   1.125 +        warning("directory %s is a symlink and is not secure\n", dirname);
   1.126 +      } else {
   1.127 +        warning("could not open directory %s: %s\n", dirname, strerror(errno));
   1.128 +      }
   1.129      }
   1.130 +    return dirp;
   1.131 +  }
   1.132 +  int fd = result;
   1.133 +
   1.134 +  // Determine if the open directory is secure.
   1.135 +  if (!is_dirfd_secure(fd)) {
   1.136 +    // The directory is not a secure directory.
   1.137 +    os::close(fd);
   1.138 +    return dirp;
   1.139 +  }
   1.140 +
   1.141 +  // Open the directory.
   1.142 +  dirp = ::opendir(dirname);
   1.143 +  if (dirp == NULL) {
   1.144 +    // The directory doesn't exist, close fd and return.
   1.145 +    os::close(fd);
   1.146 +    return dirp;
   1.147 +  }
   1.148 +
   1.149 +  // Check to make sure fd and dirp are referencing the same file system object.
   1.150 +  if (!is_same_fsobject(fd, dirfd(dirp))) {
   1.151 +    // The directory is not secure.
   1.152 +    os::close(fd);
   1.153 +    os::closedir(dirp);
   1.154 +    dirp = NULL;
   1.155 +    return dirp;
   1.156 +  }
   1.157 +
   1.158 +  // Close initial open now that we know directory is secure
   1.159 +  os::close(fd);
   1.160 +
   1.161 +  return dirp;
   1.162 +}
   1.163 +
   1.164 +// NOTE: The code below uses fchdir(), open() and unlink() because
   1.165 +// fdopendir(), openat() and unlinkat() are not supported on all
   1.166 +// versions.  Once the support for fdopendir(), openat() and unlinkat()
   1.167 +// is available on all supported versions the code can be changed
   1.168 +// to use these functions.
   1.169 +
   1.170 +// Open the directory of the given path, validate it and set the
   1.171 +// current working directory to it.
   1.172 +// Return a DIR * of the open directory and the saved cwd fd.
   1.173 +//
   1.174 +static DIR *open_directory_secure_cwd(const char* dirname, int *saved_cwd_fd) {
   1.175 +
   1.176 +  // Open the directory.
   1.177 +  DIR* dirp = open_directory_secure(dirname);
   1.178 +  if (dirp == NULL) {
   1.179 +    // Directory doesn't exist or is insecure, so there is nothing to cleanup.
   1.180 +    return dirp;
   1.181 +  }
   1.182 +  int fd = dirfd(dirp);
   1.183 +
   1.184 +  // Open a fd to the cwd and save it off.
   1.185 +  int result;
   1.186 +  RESTARTABLE(::open(".", O_RDONLY), result);
   1.187 +  if (result == OS_ERR) {
   1.188 +    *saved_cwd_fd = -1;
   1.189 +  } else {
   1.190 +    *saved_cwd_fd = result;
   1.191 +  }
   1.192 +
   1.193 +  // Set the current directory to dirname by using the fd of the directory.
   1.194 +  result = fchdir(fd);
   1.195 +
   1.196 +  return dirp;
   1.197 +}
   1.198 +
   1.199 +// Close the directory and restore the current working directory.
   1.200 +//
   1.201 +static void close_directory_secure_cwd(DIR* dirp, int saved_cwd_fd) {
   1.202 +
   1.203 +  int result;
   1.204 +  // If we have a saved cwd change back to it and close the fd.
   1.205 +  if (saved_cwd_fd != -1) {
   1.206 +    result = fchdir(saved_cwd_fd);
   1.207 +    ::close(saved_cwd_fd);
   1.208 +  }
   1.209 +
   1.210 +  // Close the directory.
   1.211 +  os::closedir(dirp);
   1.212 +}
   1.213 +
   1.214 +// Check if the given file descriptor is considered a secure.
   1.215 +//
   1.216 +static bool is_file_secure(int fd, const char *filename) {
   1.217 +
   1.218 +  int result;
   1.219 +  struct stat statbuf;
   1.220 +
   1.221 +  // Determine if the file is secure.
   1.222 +  RESTARTABLE(::fstat(fd, &statbuf), result);
   1.223 +  if (result == OS_ERR) {
   1.224 +    if (PrintMiscellaneous && Verbose) {
   1.225 +      warning("fstat failed on %s: %s\n", filename, strerror(errno));
   1.226 +    }
   1.227 +    return false;
   1.228 +  }
   1.229 +  if (statbuf.st_nlink > 1) {
   1.230 +    // A file with multiple links is not expected.
   1.231 +    if (PrintMiscellaneous && Verbose) {
   1.232 +      warning("file %s has multiple links\n", filename);
   1.233 +    }
   1.234 +    return false;
   1.235    }
   1.236    return true;
   1.237  }
   1.238  
   1.239 -
   1.240  // return the user name for the given user id
   1.241  //
   1.242  // the caller is expected to free the allocated memory.
   1.243 @@ -317,9 +506,11 @@
   1.244  
   1.245    const char* tmpdirname = os::get_temp_directory();
   1.246  
   1.247 +  // open the temp directory
   1.248    DIR* tmpdirp = os::opendir(tmpdirname);
   1.249  
   1.250    if (tmpdirp == NULL) {
   1.251 +    // Cannot open the directory to get the user name, return.
   1.252      return NULL;
   1.253    }
   1.254  
   1.255 @@ -344,25 +535,14 @@
   1.256      strcat(usrdir_name, "/");
   1.257      strcat(usrdir_name, dentry->d_name);
   1.258  
   1.259 -    DIR* subdirp = os::opendir(usrdir_name);
   1.260 +    // open the user directory
   1.261 +    DIR* subdirp = open_directory_secure(usrdir_name);
   1.262  
   1.263      if (subdirp == NULL) {
   1.264        FREE_C_HEAP_ARRAY(char, usrdir_name, mtInternal);
   1.265        continue;
   1.266      }
   1.267  
   1.268 -    // Since we don't create the backing store files in directories
   1.269 -    // pointed to by symbolic links, we also don't follow them when
   1.270 -    // looking for the files. We check for a symbolic link after the
   1.271 -    // call to opendir in order to eliminate a small window where the
   1.272 -    // symlink can be exploited.
   1.273 -    //
   1.274 -    if (!is_directory_secure(usrdir_name)) {
   1.275 -      FREE_C_HEAP_ARRAY(char, usrdir_name, mtInternal);
   1.276 -      os::closedir(subdirp);
   1.277 -      continue;
   1.278 -    }
   1.279 -
   1.280      struct dirent* udentry;
   1.281      char* udbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(usrdir_name), mtInternal);
   1.282      errno = 0;
   1.283 @@ -465,26 +645,6 @@
   1.284  }
   1.285  
   1.286  
   1.287 -// remove file
   1.288 -//
   1.289 -// this method removes the file with the given file name in the
   1.290 -// named directory.
   1.291 -//
   1.292 -static void remove_file(const char* dirname, const char* filename) {
   1.293 -
   1.294 -  size_t nbytes = strlen(dirname) + strlen(filename) + 2;
   1.295 -  char* path = NEW_C_HEAP_ARRAY(char, nbytes, mtInternal);
   1.296 -
   1.297 -  strcpy(path, dirname);
   1.298 -  strcat(path, "/");
   1.299 -  strcat(path, filename);
   1.300 -
   1.301 -  remove_file(path);
   1.302 -
   1.303 -  FREE_C_HEAP_ARRAY(char, path, mtInternal);
   1.304 -}
   1.305 -
   1.306 -
   1.307  // cleanup stale shared memory resources
   1.308  //
   1.309  // This method attempts to remove all stale shared memory files in
   1.310 @@ -496,16 +656,11 @@
   1.311  //
   1.312  static void cleanup_sharedmem_resources(const char* dirname) {
   1.313  
   1.314 -  // open the user temp directory
   1.315 -  DIR* dirp = os::opendir(dirname);
   1.316 -
   1.317 +  int saved_cwd_fd;
   1.318 +  // open the directory and set the current working directory to it
   1.319 +  DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
   1.320    if (dirp == NULL) {
   1.321 -    // directory doesn't exist, so there is nothing to cleanup
   1.322 -    return;
   1.323 -  }
   1.324 -
   1.325 -  if (!is_directory_secure(dirname)) {
   1.326 -    // the directory is not a secure directory
   1.327 +    // directory doesn't exist or is insecure, so there is nothing to cleanup
   1.328      return;
   1.329    }
   1.330  
   1.331 @@ -519,6 +674,7 @@
   1.332    //
   1.333    struct dirent* entry;
   1.334    char* dbuf = NEW_C_HEAP_ARRAY(char, os::readdir_buf_size(dirname), mtInternal);
   1.335 +
   1.336    errno = 0;
   1.337    while ((entry = os::readdir(dirp, (struct dirent *)dbuf)) != NULL) {
   1.338  
   1.339 @@ -529,7 +685,7 @@
   1.340        if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
   1.341  
   1.342          // attempt to remove all unexpected files, except "." and ".."
   1.343 -        remove_file(dirname, entry->d_name);
   1.344 +        unlink(entry->d_name);
   1.345        }
   1.346  
   1.347        errno = 0;
   1.348 @@ -552,11 +708,14 @@
   1.349      if ((pid == os::current_process_id()) ||
   1.350          (kill(pid, 0) == OS_ERR && (errno == ESRCH || errno == EPERM))) {
   1.351  
   1.352 -        remove_file(dirname, entry->d_name);
   1.353 +        unlink(entry->d_name);
   1.354      }
   1.355      errno = 0;
   1.356    }
   1.357 -  os::closedir(dirp);
   1.358 +
   1.359 +  // close the directory and reset the current working directory
   1.360 +  close_directory_secure_cwd(dirp, saved_cwd_fd);
   1.361 +
   1.362    FREE_C_HEAP_ARRAY(char, dbuf, mtInternal);
   1.363  }
   1.364  
   1.365 @@ -613,19 +772,54 @@
   1.366      return -1;
   1.367    }
   1.368  
   1.369 +  int saved_cwd_fd;
   1.370 +  // open the directory and set the current working directory to it
   1.371 +  DIR* dirp = open_directory_secure_cwd(dirname, &saved_cwd_fd);
   1.372 +  if (dirp == NULL) {
   1.373 +    // Directory doesn't exist or is insecure, so cannot create shared
   1.374 +    // memory file.
   1.375 +    return -1;
   1.376 +  }
   1.377 +
   1.378 +  // Open the filename in the current directory.
   1.379 +  // Cannot use O_TRUNC here; truncation of an existing file has to happen
   1.380 +  // after the is_file_secure() check below.
   1.381    int result;
   1.382 -
   1.383 -  RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_TRUNC, S_IREAD|S_IWRITE), result);
   1.384 +  RESTARTABLE(::open(filename, O_RDWR|O_CREAT|O_NOFOLLOW, S_IREAD|S_IWRITE), result);
   1.385    if (result == OS_ERR) {
   1.386      if (PrintMiscellaneous && Verbose) {
   1.387 -      warning("could not create file %s: %s\n", filename, strerror(errno));
   1.388 +      if (errno == ELOOP) {
   1.389 +        warning("file %s is a symlink and is not secure\n", filename);
   1.390 +      } else {
   1.391 +        warning("could not create file %s: %s\n", filename, strerror(errno));
   1.392 +      }
   1.393      }
   1.394 +    // close the directory and reset the current working directory
   1.395 +    close_directory_secure_cwd(dirp, saved_cwd_fd);
   1.396 +
   1.397      return -1;
   1.398    }
   1.399 +  // close the directory and reset the current working directory
   1.400 +  close_directory_secure_cwd(dirp, saved_cwd_fd);
   1.401  
   1.402    // save the file descriptor
   1.403    int fd = result;
   1.404  
   1.405 +  // check to see if the file is secure
   1.406 +  if (!is_file_secure(fd, filename)) {
   1.407 +    ::close(fd);
   1.408 +    return -1;
   1.409 +  }
   1.410 +
   1.411 +  // truncate the file to get rid of any existing data
   1.412 +  RESTARTABLE(::ftruncate(fd, (off_t)0), result);
   1.413 +  if (result == OS_ERR) {
   1.414 +    if (PrintMiscellaneous && Verbose) {
   1.415 +      warning("could not truncate shared memory file: %s\n", strerror(errno));
   1.416 +    }
   1.417 +    ::close(fd);
   1.418 +    return -1;
   1.419 +  }
   1.420    // set the file size
   1.421    RESTARTABLE(::ftruncate(fd, (off_t)size), result);
   1.422    if (result == OS_ERR) {
   1.423 @@ -683,8 +877,15 @@
   1.424        THROW_MSG_(vmSymbols::java_io_IOException(), strerror(errno), OS_ERR);
   1.425      }
   1.426    }
   1.427 +  int fd = result;
   1.428  
   1.429 -  return result;
   1.430 +  // check to see if the file is secure
   1.431 +  if (!is_file_secure(fd, filename)) {
   1.432 +    ::close(fd);
   1.433 +    return -1;
   1.434 +  }
   1.435 +
   1.436 +  return fd;
   1.437  }
   1.438  
   1.439  // create a named shared memory region. returns the address of the
   1.440 @@ -716,13 +917,21 @@
   1.441    char* dirname = get_user_tmp_dir(user_name);
   1.442    char* filename = get_sharedmem_filename(dirname, vmid);
   1.443  
   1.444 +  // get the short filename
   1.445 +  char* short_filename = strrchr(filename, '/');
   1.446 +  if (short_filename == NULL) {
   1.447 +    short_filename = filename;
   1.448 +  } else {
   1.449 +    short_filename++;
   1.450 +  }
   1.451 +
   1.452    // cleanup any stale shared memory files
   1.453    cleanup_sharedmem_resources(dirname);
   1.454  
   1.455    assert(((size > 0) && (size % os::vm_page_size() == 0)),
   1.456           "unexpected PerfMemory region size");
   1.457  
   1.458 -  fd = create_sharedmem_resources(dirname, filename, size);
   1.459 +  fd = create_sharedmem_resources(dirname, short_filename, size);
   1.460  
   1.461    FREE_C_HEAP_ARRAY(char, user_name, mtInternal);
   1.462    FREE_C_HEAP_ARRAY(char, dirname, mtInternal);
   1.463 @@ -837,12 +1046,12 @@
   1.464    // constructs for the file and the shared memory mapping.
   1.465    if (mode == PerfMemory::PERF_MODE_RO) {
   1.466      mmap_prot = PROT_READ;
   1.467 -    file_flags = O_RDONLY;
   1.468 +    file_flags = O_RDONLY | O_NOFOLLOW;
   1.469    }
   1.470    else if (mode == PerfMemory::PERF_MODE_RW) {
   1.471  #ifdef LATER
   1.472      mmap_prot = PROT_READ | PROT_WRITE;
   1.473 -    file_flags = O_RDWR;
   1.474 +    file_flags = O_RDWR | O_NOFOLLOW;
   1.475  #else
   1.476      THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
   1.477                "Unsupported access mode");

mercurial