Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

J9 Allow attachment as root #1631

Merged
merged 2 commits into from
May 3, 2024
Merged
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
102 changes: 66 additions & 36 deletions byte-buddy-agent/src/main/java/net/bytebuddy/agent/VirtualMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -1660,49 +1660,23 @@ protected ForOpenJ9(Socket socket) {
* @throws IOException If an IO exception occurs during establishing the connection.
*/
public static VirtualMachine attach(String processId) throws IOException {
return attach(processId, false);
}

/**
* Attaches to the supplied process id using the default JNA implementation.
*
* @param processId The process id.
* @param ignoreUser {@code true} if VM processes that are owned by different users should be considered.
* @return A suitable virtual machine implementation.
* @throws IOException If an IO exception occurs during establishing the connection.
*/
public static VirtualMachine attach(String processId, boolean ignoreUser) throws IOException {
return attach(processId, 5000, Platform.isWindows()
? new Dispatcher.ForJnaWindowsEnvironment()
: new Dispatcher.ForJnaPosixEnvironment(15, 100, TimeUnit.MILLISECONDS));
}

/**
* Attaches to the supplied process id. This method will not consider attaching to VMs owned by
* different users than the current user.
*
* @param processId The process id.
* @param timeout The timeout for establishing the socket connection.
* @param dispatcher The connector to use to communicate with the target VM.
* @return A suitable virtual machine implementation.
* @throws IOException If an IO exception occurs during establishing the connection.
*/
public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException {
return attach(processId, timeout, dispatcher, false);
}

/**
* Attaches to the supplied process id.
*
* @param processId The process id.
* @param timeout The timeout for establishing the socket connection.
* @param dispatcher The connector to use to communicate with the target VM.
* @param ignoreUser {@code true} if VM processes that are owned by different users should be considered.
* @return A suitable virtual machine implementation.
* @throws IOException If an IO exception occurs during establishing the connection.
*/
public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher, boolean ignoreUser) throws IOException {
public static VirtualMachine attach(String processId, int timeout, Dispatcher dispatcher) throws IOException {
File directory = new File(System.getProperty(IBM_TEMPORARY_FOLDER, dispatcher.getTemporaryFolder(processId)), ".com_ibm_tools_attach");
long userId = dispatcher.userId();
RandomAccessFile attachLock = new RandomAccessFile(new File(directory, "_attachlock"), "rw");
try {
FileLock attachLockLock = attachLock.getChannel().lock();
Expand All @@ -1716,10 +1690,9 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di
if (vmFolder == null) {
throw new IllegalStateException("No descriptor files found in " + directory);
}
long userId = dispatcher.userId();
virtualMachines = new ArrayList<Properties>();
for (File aVmFolder : vmFolder) {
if (aVmFolder.isDirectory() && (ignoreUser || dispatcher.getOwnerIdOf(aVmFolder) == userId)) {
if (aVmFolder.isDirectory() && isFileOwnedByUid(dispatcher, aVmFolder, userId)) {
File attachInfo = new File(aVmFolder, "attachInfo");
if (attachInfo.isFile()) {
Properties virtualMachine = new Properties();
Expand All @@ -1730,12 +1703,7 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di
inputStream.close();
}
int targetProcessId = Integer.parseInt(virtualMachine.getProperty("processId"));
long targetUserId;
try {
targetUserId = Long.parseLong(virtualMachine.getProperty("userUid"));
} catch (NumberFormatException ignored) {
targetUserId = 0L;
}
long targetUserId = getUserId(virtualMachine);
if (userId != 0L && targetUserId == 0L) {
targetUserId = dispatcher.getOwnerIdOf(attachInfo);
}
Expand Down Expand Up @@ -1782,10 +1750,14 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di
key = Long.toHexString(SECURE_RANDOM.nextLong());
}
File reply = new File(receiver, "replyInfo");
long targetUserId = getUserId(target);
try {
if (reply.createNewFile()) {
dispatcher.setPermissions(reply, 0600);
}
if (0 == userId && 0 != targetUserId) {
dispatcher.chownFileToTargetUid(reply, targetUserId);
}
FileOutputStream outputStream = new FileOutputStream(reply);
try {
outputStream.write(key.getBytes("UTF-8"));
Expand Down Expand Up @@ -1867,6 +1839,31 @@ public static VirtualMachine attach(String processId, int timeout, Dispatcher di
}
}

/**
* Returns the userUid present in the virtualMachine properties file
* @param virtualMachine Properties of the J9 attachInfo file
* @return the userUid if it can be parsed, <code>0L</code> otherwise.
*/
private static long getUserId(Properties virtualMachine) {
long targetUserId;
try {
targetUserId = Long.parseLong(virtualMachine.getProperty("userUid"));
} catch (NumberFormatException ignored) {
targetUserId = 0L;
}
return targetUserId;
}

/**
* Check if the file is owned by the UID. Note that UID 0 "owns" all files.
* @param aVmFolder File or directory
* @param userId user UID.
* @return true if the uid owns the file or uid == 0.
*/
private static boolean isFileOwnedByUid(Dispatcher dispatcher, File aVmFolder, long userId) {
return 0 == userId || dispatcher.getOwnerIdOf(aVmFolder) == userId;
}

/**
* {@inheritDoc}
*/
Expand Down Expand Up @@ -2077,6 +2074,13 @@ public interface Dispatcher {
*/
void decrementSemaphore(File directory, String name, boolean global, int count);

/**
* change the ownership of a file. Can be called only if this process is owned by root.
* @param path path to the file
* @param targetUserId effective userid
*/
void chownFileToTargetUid(File path, long targetUserId);

/**
* A connector implementation for a POSIX environment using JNA.
*/
Expand Down Expand Up @@ -2214,6 +2218,14 @@ public void decrementSemaphore(File directory, String name, boolean global, int
notifySemaphore(directory, name, count, (short) -1, (short) (PosixLibrary.SEM_UNDO | PosixLibrary.IPC_NOWAIT), true);
}

/**
* {@inheritDoc}
*/
@Override
public void chownFileToTargetUid(File file, long targetUserId) {
library.chown(file.getAbsolutePath(), targetUserId);
}

/**
* Notifies a POSIX semaphore.
*
Expand Down Expand Up @@ -2319,6 +2331,16 @@ protected interface PosixLibrary extends Library {
*/
int chmod(String path, int mode) throws LastErrorException;

/**
* Runs the {@code chown} command.
*
* @param path The file path.
* @param uid The userid to set.
* @return The return code.
* @throws LastErrorException If an error occurred.
*/
int chown(String path, long uid) throws LastErrorException;

/**
* Runs the {@code ftok} command.
*
Expand Down Expand Up @@ -2502,6 +2524,14 @@ public void decrementSemaphore(File directory, String name, boolean global, int
}
}

/**
* {@inheritDoc}
*/
@Override
public void chownFileToTargetUid(File path, long targetUserId) {
/* do nothing */
}

/**
* Opens a semaphore for signaling another process that an attachment is performed.
*
Expand Down
Loading