Summary
The per-session identity switch used around dataset OPEN
(racf_set_acee(sess->acee) … fopen … racf_set_acee(oldacee) in
ftpd#mvs.c) writes the address-space-wide ASXBSENV field, while FTP
sessions run as concurrent subtasks (separate TCBs) in one address space.
The OPEN-path switch is not serialized, so a concurrent worker's ASXBSENV
write can bleed into an in-flight OPEN's internal RACF check.
This is a pre-existing latent bug, independent of the ABEND-recovery work in the
linked PR (that PR only re-establishes the same field's normal resting value and
does not change the steady-state switch).
Evidence
libc370/src/racf/racsacee.c:34,53 — racf_set_acee() stores into
ASXBSENV (PSA → ASCB+0x224 → ASXB+0x6C → ASXBSENV+0xC8); racf_get_acee()
reads the same field. TCBSENV is never touched.
libc370/src/clib/@@ctcrtx.c:87 — workers are created with
ATTACH EP=CTHREAD; each session is a distinct TCB in the same AS
(@@cminit.c: pool pre-spawns mintask, grows to maxtask = max_sessions).
libc370/src/racf/racauth.c:68,120,135,148 — racf_auth() does serialize
its own set/RACHECK/restore under lock(asxb)/unlock(asxb), so the
primary authorization decision (FTPD's check_dataset_access() pre-check)
is race-free. The OPEN-path switch in ftpd#mvs.c does not take that lock.
Severity
Hardening / defense-in-depth. Entitlement is gated race-free by the primary
racf_auth() pre-check under the correct identity, so for a pre-checked open the
OPEN-race yields a spurious OPEN failure (availability), not privilege
escalation. (Audit note: any fopen not preceded by a racf_auth pre-check
would rely on the racy OPEN check directly — higher concern; enumerate such
sites.)
Remedy — VERIFY FIRST, do not assume
The fix direction is not decided. First step:
- Verify whether MVS 3.8j OPEN + RAKF actually consult TCBSENV.
"RACROUTE honors TCBSENV over ASXBSENV" is z/OS semantics; under RAKF it is
the security layer that decides whether TCBSENV is consulted at all, and the
MVS 3.8j OPEN path must actually propagate it. If it is honored, a per-task
TCBSENV switch removes the race with no serialization cost.
- Alternative: serialize the
set_acee/OPEN/restore sequence under the ASXB
lock. Note the catch: racf_auth() proves the lock pattern for the fast
RACHECK, but OPEN is I/O-bound — holding an AS-wide ENQ across every
dataset OPEN would serialize all opens AS-wide and risks a throughput
collapse / long holds. This is precisely why the current code locks only the
RACHECK, not the OPEN.
Whichever path is chosen, ABEND recovery's ASXBSENV reset (linked PR) should be
revisited to match.
Note
Under a multi-CP Hercules configuration the interleaving becomes true
parallelism, which additionally removes any store-ordering guarantee on the
shared ASXBSENV field.
Security invariant dependency
The linked PR documents a SECURITY INVARIANT in doc/FTPD_RAKF_SETUP.md:
FTPD/USER must remain least-privilege so recovery's ASXBSENV reset stays
fail-closed. A fix here should preserve or supersede that invariant.
Summary
The per-session identity switch used around dataset OPEN
(
racf_set_acee(sess->acee)…fopen…racf_set_acee(oldacee)inftpd#mvs.c) writes the address-space-wide ASXBSENV field, while FTPsessions run as concurrent subtasks (separate TCBs) in one address space.
The OPEN-path switch is not serialized, so a concurrent worker's ASXBSENV
write can bleed into an in-flight OPEN's internal RACF check.
This is a pre-existing latent bug, independent of the ABEND-recovery work in the
linked PR (that PR only re-establishes the same field's normal resting value and
does not change the steady-state switch).
Evidence
libc370/src/racf/racsacee.c:34,53—racf_set_acee()stores intoASXBSENV (
PSA → ASCB+0x224 → ASXB+0x6C → ASXBSENV+0xC8);racf_get_acee()reads the same field. TCBSENV is never touched.
libc370/src/clib/@@ctcrtx.c:87— workers are created withATTACH EP=CTHREAD; each session is a distinct TCB in the same AS(
@@cminit.c: pool pre-spawnsmintask, grows tomaxtask = max_sessions).libc370/src/racf/racauth.c:68,120,135,148—racf_auth()does serializeits own set/RACHECK/restore under
lock(asxb)/unlock(asxb), so theprimary authorization decision (FTPD's
check_dataset_access()pre-check)is race-free. The OPEN-path switch in
ftpd#mvs.cdoes not take that lock.Severity
Hardening / defense-in-depth. Entitlement is gated race-free by the primary
racf_auth()pre-check under the correct identity, so for a pre-checked open theOPEN-race yields a spurious OPEN failure (availability), not privilege
escalation. (Audit note: any
fopennot preceded by aracf_authpre-checkwould rely on the racy OPEN check directly — higher concern; enumerate such
sites.)
Remedy — VERIFY FIRST, do not assume
The fix direction is not decided. First step:
"RACROUTE honors TCBSENV over ASXBSENV" is z/OS semantics; under RAKF it is
the security layer that decides whether TCBSENV is consulted at all, and the
MVS 3.8j OPEN path must actually propagate it. If it is honored, a per-task
TCBSENV switch removes the race with no serialization cost.
set_acee/OPEN/restoresequence under the ASXBlock. Note the catch:
racf_auth()proves the lock pattern for the fastRACHECK, but OPEN is I/O-bound — holding an AS-wide ENQ across every
dataset OPEN would serialize all opens AS-wide and risks a throughput
collapse / long holds. This is precisely why the current code locks only the
RACHECK, not the OPEN.
Whichever path is chosen, ABEND recovery's ASXBSENV reset (linked PR) should be
revisited to match.
Note
Under a multi-CP Hercules configuration the interleaving becomes true
parallelism, which additionally removes any store-ordering guarantee on the
shared ASXBSENV field.
Security invariant dependency
The linked PR documents a SECURITY INVARIANT in
doc/FTPD_RAKF_SETUP.md:FTPD/USERmust remain least-privilege so recovery's ASXBSENV reset staysfail-closed. A fix here should preserve or supersede that invariant.