Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions src/hotspot/os/bsd/os_bsd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,99 @@ pid_t os::Bsd::gettid() {
}
}

// Returns the uid of a process or -1 on error
uid_t os::Bsd::get_process_uid(pid_t pid) {
struct kinfo_proc kp;
size_t size = sizeof kp;
int mib_kern[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
if (sysctl(mib_kern, 4, &kp, &size, nullptr, 0) == 0) {
if (size > 0 && kp.kp_proc.p_pid == pid) {
return kp.kp_eproc.e_ucred.cr_uid;
}
}
return (uid_t)-1;
}

// Returns true if the process is running as root
bool os::Bsd::is_process_root(pid_t pid) {
uid_t uid = get_process_uid(pid);
return (uid != (uid_t)-1) ? os::Posix::is_root(uid) : false;
}

#ifdef __APPLE__

// macOS has a secure per-user temporary directory.
// Root can attach to a non-root process, hence it needs
// to lookup /var/folders for the user specific temporary directory
// of the form /var/folders/*/*/T, that contains PERFDATA_NAME_user
// directory.
//
static const char VAR_FOLDERS[] = "/var/folders/";
int os::Bsd::get_user_tmp_dir_macos(const char* user, int vmid, char* output_path, int output_size) {

// read the var/folders directory
DIR* varfolders_dir = os::opendir(VAR_FOLDERS);
if (varfolders_dir != nullptr) {

// var/folders directory contains 2-characters subdirectories (buckets)
struct dirent* bucket_de;

// loop until the PERFDATA_NAME_user directory has been found
while ((bucket_de = os::readdir(varfolders_dir)) != nullptr) {

// skip over files and special "." and ".."
if (bucket_de->d_type != DT_DIR || bucket_de->d_name[0] == '.') {
continue;
}

// absolute path to the bucket
char bucket[PATH_MAX];
int b = os::snprintf(bucket, PATH_MAX, "%s%s/", VAR_FOLDERS, bucket_de->d_name);

// the total length of the absolute path must not exceed the buffer size
if (b >= PATH_MAX || b < 0) {
continue;
}

// each bucket contains next level subdirectories
DIR* bucket_dir = os::opendir(bucket);
if (bucket_dir == nullptr) {
continue;
}

// read each subdirectory, skipping over regular files
struct dirent* subbucket_de;
while ((subbucket_de = os::readdir(bucket_dir)) != nullptr) {
if (subbucket_de->d_type != DT_DIR || subbucket_de->d_name[0] == '.') {
continue;
}

// if the PERFDATA_NAME_user directory exists in the T subdirectory,
// this means the subdirectory is the temporary directory of the user.
//
char perfdata_path[PATH_MAX];
int p = os::snprintf(perfdata_path, PATH_MAX, "%s%s/T/%s_%s/", bucket, subbucket_de->d_name, PERFDATA_NAME, user);

// the total length must not exceed the output buffer size
if (p >= PATH_MAX || p < 0) {
continue;
}

// check if the subdirectory exists
if (os::file_exists(perfdata_path)) {

// the return value of snprintf is not checked for the second time
return os::snprintf(output_path, output_size, "%s%s/T", bucket, subbucket_de->d_name);
}
}
os::closedir(bucket_dir);
}
os::closedir(varfolders_dir);
}
return -1;
}
#endif

intx os::current_thread_id() {
#ifdef __APPLE__
return (intx)os::Bsd::gettid();
Expand Down
8 changes: 7 additions & 1 deletion src/hotspot/os/bsd/os_bsd.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2025, 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
Expand Down Expand Up @@ -61,6 +61,12 @@ class os::Bsd {
static pthread_t main_thread(void) { return _main_thread; }

static pid_t gettid();
static uid_t get_process_uid(pid_t pid);
static bool is_process_root(pid_t pid);

#ifdef __APPLE__
static int get_user_tmp_dir_macos(const char* user, int vmid, char* output_buffer, int buffer_size);
#endif

static intptr_t* ucontext_get_sp(const ucontext_t* uc);
static intptr_t* ucontext_get_fp(const ucontext_t* uc);
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/os/posix/os_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,10 @@ bool os::Posix::is_root(uid_t uid){
return ROOT_UID == uid;
}

bool os::Posix::is_current_user_root(){
return is_root(geteuid());
}

bool os::Posix::matches_effective_uid_or_root(uid_t uid) {
return is_root(uid) || geteuid() == uid;
}
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/os/posix/os_posix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class os::Posix {
// Returns true if given uid is root.
static bool is_root(uid_t uid);

// Returns true if the current user is root.
static bool is_current_user_root();

// Returns true if given uid is effective or root uid.
static bool matches_effective_uid_or_root(uid_t uid);

Expand Down
18 changes: 17 additions & 1 deletion src/hotspot/os/posix/perfMemory_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
#if defined(LINUX)
#include "os_linux.hpp"
#endif
#if defined(BSD)
#include "os_bsd.hpp"
#endif

# include <errno.h>
# include <pwd.h>
Expand Down Expand Up @@ -143,6 +146,18 @@ static char* get_user_tmp_dir(const char* user, int vmid, int nspid) {
jio_snprintf(buffer, TMP_BUFFER_LEN, "/proc/%d/root%s", vmid, tmpdir);
tmpdir = buffer;
}
#endif
#ifdef __APPLE__
char buffer[PATH_MAX] = {0};
// Check if the current user is root and the target VM is running as non-root.
// Otherwise the output of os::get_temp_directory() is used.
//
if (os::Posix::is_current_user_root() && !os::Bsd::is_process_root(vmid)) {
int path_size = os::Bsd::get_user_tmp_dir_macos(user, vmid, buffer, sizeof buffer);
if (path_size > 0 && (size_t)path_size < sizeof buffer) {
tmpdir = buffer;
}
}
#endif
const char* perfdir = PERFDATA_NAME;
size_t nbytes = strlen(tmpdir) + strlen(perfdir) + strlen(user) + 3;
Expand Down Expand Up @@ -1160,7 +1175,8 @@ static void mmap_attach_shared(int vmid, char** addr, size_t* sizep, TRAPS) {

// for linux, determine if vmid is for a containerized process
int nspid = LINUX_ONLY(os::Linux::get_namespace_pid(vmid)) NOT_LINUX(-1);
const char* luser = get_user_name(vmid, &nspid, CHECK);
const char* luser = NOT_MACOS(get_user_name(vmid, &nspid, CHECK))
MACOS_ONLY(get_user_name(os::Bsd::get_process_uid(vmid)));

if (luser == nullptr) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,13 @@
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.spi.AttachProvider;

import sun.jvmstat.PlatformSupport;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is convenient but I'm not sure it is appropriate. Need the serviceability folk to comment.


import java.io.InputStream;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

import static java.nio.charset.StandardCharsets.UTF_8;

Expand All @@ -39,14 +43,17 @@
*/
@SuppressWarnings("restricted")
public class VirtualMachineImpl extends HotSpotVirtualMachine {
// "tmpdir" is used as a global well-known location for the files
// .java_pid<pid>. and .attach_pid<pid>. It is important that this
// location is the same for all processes, otherwise the tools
// will not be able to find all Hotspot processes.
// This is intentionally not the same as java.io.tmpdir, since
// the latter can be changed by the user.
// Any changes to this needs to be synchronized with HotSpot.
private static final String tmpdir;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we can't cache this any more?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks David for pointing this out. A different instance with a different PID may have a different temp path, so the path should be specific to the instance. Since we don't need the path outside the constructor, I think we don't need to cache it. On the other hand I see that getTempDirFromPid is called twice, once for .java_pid and another time in createAttachFile when .java_pid doesn't exist. Would you suggest storing the path in a local variable in the constructor and then concatenating it with attach file name, or would a non-static member variable be more suitable for this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would save it in a local in the constructor and pass it to createAttachFile rather than passing the pid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


/**
* HotSpot PerfData file prefix
*/
private static final String HSPERFDATA_PREFIX = "hsperfdata_";

/**
* Use platform specific methods for looking up temporary directories.
*/
private static final PlatformSupport platformSupport = PlatformSupport.getInstance();

String socket_path;
private OperationProperties props = new OperationProperties(VERSION_1); // updated in ctor

Expand All @@ -67,10 +74,12 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
// Find the socket file. If not found then we attempt to start the
// attach mechanism in the target VM by sending it a QUIT signal.
// Then we attempt to find the socket file again.
File socket_file = new File(tmpdir, ".java_pid" + pid);
// In macOS the socket file is located in per-user temp directory.
String tempDir = getTempDirFromPid(pid);
File socket_file = new File(tempDir, ".java_pid" + pid);
socket_path = socket_file.getPath();
if (!socket_file.exists()) {
File f = createAttachFile(pid);
File f = createAttachFile(tempDir, pid);
try {
checkCatchesAndSendQuitTo(pid, false);

Expand Down Expand Up @@ -211,12 +220,34 @@ protected void close(long fd) throws IOException {
}
}

private File createAttachFile(int pid) throws IOException {
File f = new File(tmpdir, ".attach_pid" + pid);
private File createAttachFile(String dir, int pid) throws IOException {
File f = new File(dir, ".attach_pid" + pid);
createAttachFile0(f.getPath());
return f;
}

/*
* Returns a platform-specific temporary directory for a given process.
* In VMs running as unprivileged user it returns the default platform-specific
* temporary directory. In VMs running as root it searches over the list of
* temporary directories for one containing HotSpot PerfData directory.
*/
private String getTempDirFromPid(int pid) {
ProcessHandle ph = ProcessHandle.of(pid).orElse(null);
if (ph != null) {
String user = ph.info().user().orElse(null);
if (user != null) {
for (String dir : platformSupport.getTemporaryDirectories(pid)) {
Path fullPath = Path.of(dir, HSPERFDATA_PREFIX + user, String.valueOf(pid));
if (Files.exists(fullPath)) {
return dir;
}
}
}
}
return PlatformSupport.getTemporaryDirectory();
}

//-- native methods

static native boolean checkCatchesAndSendQuitTo(int pid, boolean throwIfNotReady) throws IOException, AttachNotSupportedException;
Expand All @@ -235,10 +266,7 @@ private File createAttachFile(int pid) throws IOException {

static native void createAttachFile0(String path);

static native String getTempDir();

static {
System.loadLibrary("attach");
tmpdir = getTempDir();
}
}
24 changes: 0 additions & 24 deletions src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,27 +335,3 @@ JNIEXPORT void JNICALL Java_sun_tools_attach_VirtualMachineImpl_createAttachFile
JNU_ReleaseStringPlatformChars(env, path, _path);
}
}

/*
* Class: sun_tools_attach_BSDVirtualMachine
* Method: getTempDir
* Signature: (V)Ljava.lang.String;
*/
JNIEXPORT jstring JNICALL Java_sun_tools_attach_VirtualMachineImpl_getTempDir(JNIEnv *env, jclass cls)
{
// This must be hard coded because it's the system's temporary
// directory not the java application's temp directory, ala java.io.tmpdir.

#ifdef __APPLE__
// macosx has a secure per-user temporary directory.
// Don't cache the result as this is only called once.
char path[PATH_MAX];
int pathSize = confstr(_CS_DARWIN_USER_TEMP_DIR, path, PATH_MAX);
if (pathSize == 0 || pathSize > PATH_MAX) {
strlcpy(path, "/tmp", sizeof(path));
}
return JNU_NewStringPlatform(env, path);
#else /* __APPLE__ */
return (*env)->NewStringUTF(env, "/tmp");
#endif /* __APPLE__ */
}
Loading