Skip to content

Shutdown crash: S33E on worker DETACH + recovery ESTAE runs C runtime under a torn-down CRT #122

Description

@mgrossmann

Summary

On STC shutdown (after a CGI endpoint had been faulting during the session), the address space terminates with an S33E and an SVC dump. Two distinct problems appear in the log; this issue tracks the shutdown lifecycle one (the repeated MVSMF99E is tracked separately — see Follow-up).

Observed

HTTPD060I SHUTDOWN worker(11CC00) TCB(9BB9D8) ...
MVSMF99E Handler abend S0C4 U0000 for PUT /zosmf/restconsoles/consoles/defcn   (x8, same URI)
(~16 s silence)
__CRTGET CRT for TCB(009B29E8) was not found in PPA(000CEFD0)                  (repeated)
ABEND S33E detected for module 00000000  TCB=009B29E8
PSW:00000000 00000000 KEY(0) MODE(SUP) ILC(2) CC(0)
Traceback interrupted, Forward from PPA 002BEFF0
-> address space terminated, SVC dump taken

Root cause

1. Teardown DETACHes tasks without proving they terminated.
cthread_manager_termdispatch_thread_termcthread_worker_shutdowncthread_detach issues DETACH ...,STAE=YES on the escalation path even when the target is still active / already detached / already reclaimed → S33E. The ATTACH supplies ECB=(task->termecb) (posted 0x40000000 when the subtask actually ends), and the join loops wait on it, but the detach decision itself is not gated on it. @@cmterm.c already documents this failure mode:

force-DETACHes a still-active worker (S33E) and the worker's ESTAE then faults on the torn-down save area (nested S0C4).

The ~16 s of silence is the STIMER WAIT escalation loops (@@cmwshu.c waits 50 x 0.10 s per force-detached worker; @@cmterm.c join loops run tens of seconds) trying to drain workers that won't drain.

2. Recovery ESTAEs do runtime-dependent work at termination.
The worker ESTAE (libc370 @@abrpt.c recovery()) is created ESTAE ...,TERM=YES but never inspects SDWACLUP (clibsdwa.h:379 — "ON INDICATES RECOVERY EXIT ONLY TO CLEANUP AND NOT RETRY"), and it calls the C runtime (wtof, sprintf, and try()__crtget, estae, GETMAIN) for every register (dump_regs) and every save area (dump_sa). Driven during termination under a TCB whose libc370 CRT is already torn down, each try()__crtget() fails and logs __CRTGET ... not found in PPA — hence the repeated spam. (The zero PSW/regs is correct output for an RTM/DETACH-detected S33E, which carries no faulting-instruction PSW — not a second bug.) mvsMF's per-request ESTAE failed() in @@try.c has the same defect (ignores SDWACLUP, unconditionally requests retry).

This is the same class as mvslovers/mvsmf#101 (SE37 during fclose in ESTAE recovery): a cleanup path that assumes a healthy runtime and takes a secondary fault.

Ordering note

HTTPD100I CONS(0) STOP before the worker shutdowns is expected and correct — that CONS is the operator console / CIB interface, serviced only by the main thread (httpcons.c), and is the trigger for shutdown, not a resource the workers reference during cleanup. The real race is inside worker teardown (detach vs task termination), not the console.

Proposed fix

  • Gate DETACH on proof of death. Treat task->termecb & 0x40000000 as the single source of truth for "this TCB is gone"; issue DETACH only after it posts. Make cthread_detach idempotent and owner/termination-checked (verify the TCB is still a subtask of the caller and has posted termecb before detaching; skip otherwise).
  • Never STAE=YES-detach a live worker. If a worker won't drain, mark it STUCK, leave its storage, and let address-space termination reclaim it (log the leak) — the same "retain rather than risk use-after-free" tradeoff cthread_manager_term already makes for the dispatch thread.
  • Harden the recovery exits. In @@abrpt.c recovery() and @@try.c failed(): check SDWACLUP first; if set, emit at most one WTO from a pre-formatted buffer and return RC=0 ("continue with termination") — no retry, no dump/register walk, no malloc/try/__crtget. Guard the dump path against a missing CRT.

Notes / next steps

  • Confirm the detaching TCB (009B29E8) by matching against the HTTPD061I STARTING / HTTPD060I SHUTDOWN TCB(...) log lines (dispatch thread vs main task) — this distinguishes double-detach vs cross-owner vs detach-after-reclaim.
  • The recovery-exit hardening likely belongs in libc370 (@@abrpt.c, @@try.c); coordinate with mvsmf#101.
  • Follow-up (separate issue): the repeated MVSMF99E on the same URI is a faulting/wedged endpoint whose retries spread across the whole worker pool — the message data is per-client (httpc->env), not a shared field. It may be compounded by a shared grtapp2 handoff (the GRT is inherited/shared down the task tree — @@crtset.c). Instrument grt/&grtapp2/httpc per CGI invocation to decide: different httpc per worker = mvsMF endpoint bug; same grtapp2/httpc across concurrent workers = shared-slot stomp.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority:highUrgent — blocks progresstype:bugSomething is broken

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions