[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-serviceability-dev
Subject:    Re: RFR: 8307977: Fix dynamic attach to processes with elevated capabilities on Linux
From:       Sebastian =?UTF-8?B?TMO2dmRhaGw=?= <duke () openjdk ! org>
Date:       2024-01-30 10:59:33
Message-ID: EmxlQc4P1zqB9rC2Dn0VCksg9rDPBesRq-sLifPegeQ=.19da5a60-ceb4-40ee-856f-7873106502fc () github ! com
[Download RAW message or body]

On Tue, 30 Jan 2024 10:47:22 GMT, Sebastian Lövdahl <duke@openjdk.org> wrote:

> 8307977: Fix dynamic attach to processes with elevated capabilities on Linux

I have poked around in the JDK sources but not found any tests related to this. Is \
there some prior art to look at?

Anyway, this is how I reproduced it locally, and verified that the fix works.

Basic environment information:


slovdahl@ubuntu2204:~/reproducer$ systemd --version
systemd 249 (249.11-0ubuntu3.12)
+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL \
+BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 \
-PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT \
default-hierarchy=unified slovdahl@ubuntu2204:~/reproducer$ sudo apt-get install \
openjdk-17-jdk-headless slovdahl@ubuntu2204:~/reproducer$ \
/usr/lib/jvm/java-17-openjdk-amd64/bin/java -version openjdk version "17.0.9" \
2023-10-17 OpenJDK Runtime Environment (build 17.0.9+9-Ubuntu-122.04)
OpenJDK 64-Bit Server VM (build 17.0.9+9-Ubuntu-122.04, mixed mode, sharing)
slovdahl@ubuntu2204:~/reproducer$ \
/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/java \
-version openjdk version "23-internal" 2024-09-17
OpenJDK Runtime Environment (build 23-internal-adhoc.slovdahl.jdk)
OpenJDK 64-Bit Server VM (build 23-internal-adhoc.slovdahl.jdk, mixed mode, sharing)

Reproducer systemd unit that can bind to port 81 as non-`root` user thanks to \
`AmbientCapabilities=CAP_NET_BIND_SERVICE` \
(https://man7.org/linux/man-pages/man7/capabilities.7.html):


slovdahl@ubuntu2204:~/reproducer$ cat Reproducer.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;

public class Reproducer {
  public static void main(String[] args) throws InterruptedException, IOException {
    System.out.println("Hello, World!");
    try (var server = new ServerSocket()) {
      server.bind(new InetSocketAddress("localhost", 81));
      System.out.println("Bound to port 81");
      while (true) {
        Thread.sleep(1_000L);
      }
    }
  }
}

slovdahl@ubuntu2204:~/reproducer$ cat reproducer.service
[Service]
Type=simple
ExecStart=/usr/lib/jvm/java-17-openjdk-amd64/bin/java \
/home/slovdahl/reproducer/Reproducer.java

User=slovdahl
Group=slovdahl
ReadWritePaths=/tmp
AmbientCapabilities=CAP_NET_BIND_SERVICE



slovdahl@ubuntu2204:~/reproducer$ sudo cp -a reproducer.service /etc/systemd/system/
slovdahl@ubuntu2204:~/reproducer$ sudo systemctl daemon-reload
slovdahl@ubuntu2204:~/reproducer$ sudo systemctl start reproducer.service
slovdahl@ubuntu2204:~/reproducer$ sudo systemctl status reproducer.service
● reproducer.service
     Loaded: loaded (/etc/systemd/system/reproducer.service; static)
     Active: active (running) since Tue 2024-01-30 11:45:15 EET; 1s ago
   Main PID: 2543233 (java)
      Tasks: 26 (limit: 76971)
     Memory: 105.5M
        CPU: 751ms
     CGroup: /system.slice/reproducer.service
             └─2543233 /usr/lib/jvm/java-17-openjdk-amd64/bin/java \
/home/slovdahl/reproducer/Reproducer.java

jan 30 11:45:15 ubuntu2204 systemd[1]: Started reproducer.service.
jan 30 11:45:15 ubuntu2204 java[2543233]: Hello, World!
jan 30 11:45:15 ubuntu2204 java[2543233]: Bound to port 81

slovdahl@ubuntu2204:~/reproducer$ ls -lh /proc/$(pgrep -f Reproducer.java)/root
ls: cannot read symbolic link '/proc/2543233/root': Permission denied
lrwxrwxrwx 1 slovdahl slovdahl 0 jan 30 11:45 /proc/2543233/root
slovdahl@ubuntu2204:~/reproducer$ sudo ls -lh /proc/$(pgrep -f Reproducer.java)/root
lrwxrwxrwx 1 slovdahl slovdahl 0 jan 30 11:45 /proc/2543233/root -> /


Fails with vanilla OpenJDK 17:


slovdahl@ubuntu2204:~/reproducer$ /usr/lib/jvm/java-17-openjdk-amd64/bin/jcmd $(pgrep \
-f Reproducer.java) VM.version 2543233:
com.sun.tools.attach.AttachNotSupportedException: Unable to open socket file \
/proc/2543233/root/tmp/.java_pid2543233: target process 2543233 doesn't respond \
within 10500ms or HotSpot VM not loaded  at \
jdk.attach/sun.tools.attach.VirtualMachineImpl.<init>(VirtualMachineImpl.java:104)  \
at jdk.attach/sun.tools.attach.AttachProviderImpl.attachVirtualMachine(AttachProviderImpl.java:58)
  at jdk.attach/com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:207)
	at jdk.jcmd/sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:113)
	at jdk.jcmd/sun.tools.jcmd.JCmd.main(JCmd.java:97)


Works when trying to attach as `root` with vanilla OpenJDK 17:


slovdahl@ubuntu2204:~/reproducer$ sudo /usr/lib/jvm/java-17-openjdk-amd64/bin/jcmd \
$(pgrep -f Reproducer.java) VM.version 2543233:
OpenJDK 64-Bit Server VM version 17.0.9+9-Ubuntu-122.04
JDK 17.0.9


A JDK built with the fix in this PR:


slovdahl@ubuntu2204:~/reproducer$ sudo systemctl stop reproducer.service
slovdahl@ubuntu2204:~/reproducer$ cat reproducer-custom-jvm.service
[Service]
Type=simple
ExecStart=/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/java \
/home/slovdahl/reproducer/Reproducer.java

User=slovdahl
Group=slovdahl
ReadWritePaths=/tmp
AmbientCapabilities=CAP_NET_BIND_SERVICE
slovdahl@ubuntu2204:~/reproducer$ sudo cp -a reproducer-custom-jvm.service \
/etc/systemd/system/ slovdahl@ubuntu2204:~/reproducer$ sudo systemctl daemon-reload
slovdahl@ubuntu2204:~/reproducer$ sudo systemctl start reproducer-custom-jvm.service
slovdahl@ubuntu2204:~/reproducer$ sudo systemctl status reproducer-custom-jvm.service
● reproducer-custom-jvm.service
     Loaded: loaded (/etc/systemd/system/reproducer-custom-jvm.service; static)
     Active: active (running) since Tue 2024-01-30 11:49:13 EET; 1s ago
   Main PID: 2546431 (java)
      Tasks: 26 (limit: 76971)
     Memory: 68.4M
        CPU: 809ms
     CGroup: /system.slice/reproducer-custom-jvm.service
             └─2546431 \
/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/java \
/home/slovdahl/reproducer/Reproducer.java

jan 30 11:49:13 ubuntu2204 systemd[1]: Started reproducer.service.
jan 30 11:49:14 ubuntu2204 java[2546431]: Hello, World!
jan 30 11:49:14 ubuntu2204 java[2546431]: Bound to port 81

slovdahl@ubuntu2204:~/reproducer$ ls -lh /proc/$(pgrep -f Reproducer.java)/root
ls: cannot read symbolic link '/proc/2546431/root': Permission denied
lrwxrwxrwx 1 slovdahl slovdahl 0 jan 30 11:49 /proc/2546431/root


Attaching works as non-`root` with the fixed JDK:

slovdahl@ubuntu2204:~/reproducer$ \
/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/jcmd \
$(pgrep -f Reproducer.java) VM.version 2546431:
OpenJDK 64-Bit Server VM version 23-internal-adhoc.slovdahl.jdk
JDK 23.0.0


Attaching to a JVM inside a Docker container works as before with vanilla OpenJDK 17 \
and my locally built one (always requires `root`):

slovdahl@ubuntu2204:~/reproducer$ sudo systemctl stop reproducer-custom-jvm.service
slovdahl@ubuntu2204:~/reproducer$ docker run eclipse-temurin:17 java -version
openjdk version "17.0.10" 2024-01-16
OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)
OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode, sharing)

slovdahl@ubuntu2204:~/reproducer$ docker run --rm -v .:/app -w /app \
eclipse-temurin:17 java Reproducer.java Hello, World!
Bound to port 81

slovdahl@ubuntu2204:~/reproducer$ /usr/lib/jvm/java-17-openjdk-amd64/bin/jcmd $(pgrep \
--newest -f Reproducer.java) VM.version 2553682:
java.io.IOException: Permission denied
	at java.base/java.io.UnixFileSystem.createFileExclusively(Native Method)
	at java.base/java.io.File.createNewFile(File.java:1043)
	at jdk.attach/sun.tools.attach.VirtualMachineImpl.createAttachFile(VirtualMachineImpl.java:308)
  at jdk.attach/sun.tools.attach.VirtualMachineImpl.<init>(VirtualMachineImpl.java:80)
  at jdk.attach/sun.tools.attach.AttachProviderImpl.attachVirtualMachine(AttachProviderImpl.java:58)
  at jdk.attach/com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:207)
	at jdk.jcmd/sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:113)
	at jdk.jcmd/sun.tools.jcmd.JCmd.main(JCmd.java:97)
slovdahl@ubuntu2204:~/reproducer$ sudo /usr/lib/jvm/java-17-openjdk-amd64/bin/jcmd \
$(pgrep --newest -f Reproducer.java) VM.version 2553682:
OpenJDK 64-Bit Server VM version 17.0.10+7
JDK 17.0.10

slovdahl@ubuntu2204:~/reproducer$ \
/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/jcmd \
$(pgrep --newest -f Reproducer.java) VM.version 2553682:
java.io.IOException: Unable to access root directory /proc/2553682/root of target \
process 2553682  at jdk.attach/sun.tools.attach.VirtualMachineImpl.findTargetProcessTmpDirectory(VirtualMachineImpl.java:247)
  at jdk.attach/sun.tools.attach.VirtualMachineImpl.findSocketFile(VirtualMachineImpl.java:214)
  at jdk.attach/sun.tools.attach.VirtualMachineImpl.<init>(VirtualMachineImpl.java:71)
  at jdk.attach/sun.tools.attach.AttachProviderImpl.attachVirtualMachine(AttachProviderImpl.java:58)
  at jdk.attach/com.sun.tools.attach.VirtualMachine.attach(VirtualMachine.java:207)
	at jdk.jcmd/sun.tools.jcmd.JCmd.executeCommandForPid(JCmd.java:113)
	at jdk.jcmd/sun.tools.jcmd.JCmd.main(JCmd.java:97)
slovdahl@ubuntu2204:~/reproducer$ sudo \
/home/slovdahl/dev/external/jdk/build/linux-x86_64-server-release/images/jdk/bin/jcmd \
$(pgrep --newest -f Reproducer.java) VM.version 2553682:
OpenJDK 64-Bit Server VM version 17.0.10+7
JDK 17.0.10

-------------

PR Comment: https://git.openjdk.org/jdk/pull/17628#issuecomment-1916589081


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic