When you execute a shell step, Jenkins runs a wrapper shell process that's responsible for saving the exit code of your script. If this process is killed, then Jenkins never discovers that your script has terminated, and the step hangs forever.
This can be easily demonstrated by killing the wrapper process yourself:
Run this pipeline and the sh step will never terminate.
I believe this happens due to a bug in the log-touching subshell spawned by the wrapper process. Specifically, the subshell checks whether the wrapper process is still alive with this expression:
But $$ expands to the PID of the parent shell, not the subshell. This means that $pid and $$ are the same thing, and the whole expression always evaluates to true.