RFR: 8335610: DiagnosticFramework: CmdLine::is_executable() correction
Johan Sjölen
jsjolen at openjdk.org
Sun Jul 28 09:50:30 UTC 2024
On Wed, 3 Jul 2024 13:58:51 GMT, Kevin Walls <kevinw at openjdk.org> wrote:
> CmdLine::is_executable() looks wrong, surely an empty line is not executable.
>
> With this change, in DCmd::parse_and_execute() we will avoid needlessly entering the code block to try and execute the command.
>
> DCmd tests all good:
> make images test TEST="test/hotspot/jtreg/serviceability/dcmd test/jdk/sun/tools/jcmd"
At the point where this is checked, the commandline string is still not parsed. I do not want any `assert`s regarding the structure of untrusted input. Anyway, PoC of getting "empty" lines such that `is_empty()` is true.
I read the code:
```c++
// diagnosticFramework.cpp:387
DCmdIter iter(cmdline, '\n'); // <--- Delimiter right there
Hypothesis: Multiple newlines in a message will lead to empty lines.
First attempt: I used `jcmd` directly, this failed, as `jcmd` does some clean up on its own. So I had to go with connecting to the PID using Python.
My Python 'attacker':
import socket
import os
# My Java PID, connect with JCMD ordinarily to your Java process first to create the PID file, then
# replace the numbers with your Java PID
socket_path = "/proc/2603121/root/tmp/.java_pid2603121"
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
print("Connect")
print(client.connect(socket_path))
# // The request is:
# // <ver>0<cmd>0<arg>0<arg>0<arg>0
# // where <ver> is the protocol version (1), <cmd> is the command
# // name ("load", "datadump", ...), and <arg> is an argument
connect_payload = "1\x00jcmd\x00\n\n\n\x00\n\n\n\x00\n\n\n\x00"
print("Sending payload")
print(client.sendall(connect_payload.encode()))
print("Sent")
# Receive a response from the server
response = client.recv(4096)
print(f'Received response: {response.decode()}')
Later on, in a gdb session for my Java program and where I've connected to the Java process using my Python program:
(gdb) p line.is_empty()
$6 = true
(gdb) p cmdline
$7 = 0x7fff68240fc9 "\n\n\n"
(gdb) p line
$8 = {<StackObj> = {<No data fields>}, _cmd = 0x7fff68240fc9 "\n\n\n", _cmd_len = 0,
_args = 0x7fff68240fc9 "\n\n\n", _args_len = 0}
(gdb) p bt
No symbol "bt" in current context.
(gdb) bt
#0 DCmd::parse_and_execute (source=DCmd_Source_AttachAPI, out=0x7fffaa62ecc0, cmdline=0x7fff68240fc9 "\n\n\n",
delim=32 ' ', __the_thread__=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/share/services/diagnosticFramework.cpp:399
#1 0x00007ffff58f8748 in jcmd (op=0x7fff68240fb0, out=0x7fffaa62ecc0)
at /home/johan/jdk/open/src/hotspot/share/services/attachListener.cpp:209
#2 0x00007ffff58f91ae in AttachListenerThread::thread_entry (thread=0x7fff8c001010, __the_thread__=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/share/services/attachListener.cpp:418
#3 0x00007ffff6036294 in JavaThread::thread_main_inner (this=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/share/runtime/javaThread.cpp:757
#4 0x00007ffff6036129 in JavaThread::run (this=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/share/runtime/javaThread.cpp:742
#5 0x00007ffff67c5fb7 in Thread::call_run (this=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/share/runtime/thread.cpp:225
#6 0x00007ffff6549e28 in thread_native_entry (thread=0x7fff8c001010)
at /home/johan/jdk/open/src/hotspot/os/linux/os_linux.cpp:858
#7 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#8 0x00007ffff7d26850 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb)
Oh, and also, the output of my Python running:
Connect
None
Sending payload
None
Sent
Received response: -1
java.lang.IllegalArgumentException: Unknown diagnostic command
-------------
PR Comment: https://git.openjdk.org/jdk/pull/20006#issuecomment-2254454215
PR Comment: https://git.openjdk.org/jdk/pull/20006#issuecomment-2254454540
More information about the serviceability-dev
mailing list