https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126340

            Bug ID: 126340
           Summary: cobol: program with 'USE FOR DEBUGGING ON ALL
                    PROCEDURES' hangs.
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: cobol
          Assignee: unassigned at gcc dot gnu.org
          Reporter: peeterjoot at protonmail dot com
  Target Milestone: ---

Created attachment 65102
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65102&action=edit
debug-all-procedures-reenter.cob (also inline in the report)

# Summary

A program with `WITH DEBUGGING MODE` and `USE FOR DEBUGGING ON ALL PROCEDURES`,
built with gcobol **hangs** (CPU busy, RSS grows without bound) as soon as the
first non-declarative procedure is entered. GnuCOBOL (`cobc`) runs the same
program correctly.

Minimized from NIST COBOL85 `DB/DB105A.CBL` (Debug module — stack
`DEBUG-NAME` for all procedures).

# Background: `USE FOR DEBUGGING ON ALL PROCEDURES`

The Debug module registers a declarative that runs **immediately before**
each named procedure (and after `ALTER` of that name). `ALL PROCEDURES`
means every procedure-name in the outermost program **except** those that
appear in a debugging section (ISO / IBM / Micro Focus wording).

Entering the debugging section itself must **not** re-invoke the same
declarative; otherwise `PERFORM` of the debug section nests forever.

# Environment

- Compiler: gcobol from GCC trunk (`gcobol (GCC) 17.0.0 20260717
(experimental)`)
- Contrast: GnuCOBOL 3.2-rc2 `cobc` — passes (`HIT=2`)
- Platform: Linux aarch64 (expected elsewhere too)
- Options: `-ffixed-form` only (`WITH DEBUGGING MODE` enables the Debug
  module for gcobol; cobc also needs `COB_SET_DEBUG=Y` at run time)

# Reproducer

`debug-all-procedures-reenter.cob` (in this directory):

```cobol
       IDENTIFICATION DIVISION.
       PROGRAM-ID. DBGREENT.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SOURCE-COMPUTER. LINUX WITH DEBUGGING MODE.
       OBJECT-COMPUTER. LINUX.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  HIT PIC 9 VALUE 0.
       PROCEDURE DIVISION.
       DECLARATIVES.
       DBG-SEC SECTION.
           USE FOR DEBUGGING ON ALL PROCEDURES.
       DBG-PARA.
           ADD 1 TO HIT
           .
       END DECLARATIVES.
       MAIN-SEC SECTION.
       MAIN-PARA.
           DISPLAY "HIT=" HIT
           STOP RUN.
```

```sh
gcobol -ffixed-form -o /tmp/dbgreent debug-all-procedures-reenter.cob
timeout 3 /tmp/dbgreent   # hangs; RSS climbs (pseudo-return stack)
```

# Observed (gcobol)

- No `DISPLAY` output; process does not exit.
- Under `timeout`, exit status 124.
- RSS / VSZ grow by gigabytes per second.
- Interrupted under gdb, typical frames:

```
#0  __gg__pseudo_return_push (...) at libgcobol.cc
#1  ... in dbgreent / db105a
#2  main
```

(Each re-entrant `PERFORM` of the debug section pushes another
pseudo-return frame; the vector never shrinks.)

Contrast: `debug-on-named.cob` is the same program with
`USE FOR DEBUGGING ON MAIN-PARA` instead of `ALL PROCEDURES`. That variant
**completes** under both gcobol and cobc and prints `HIT=1`. So the hang is
specific to **`ALL PROCEDURES`** treating declarative procedures as triggers,
not to `USE FOR DEBUGGING` in general.

# Expected (and cobc behavior)

```
HIT=2
```

exit 0. (`MAIN-SEC` and `MAIN-PARA` each fire the declarative once;
`DBG-SEC` / `DBG-PARA` do not.)

```sh
cobc -std=cobol85 -x -o /tmp/dbgreent-cobc debug-all-procedures-reenter.cob
COB_SET_DEBUG=Y /tmp/dbgreent-cobc
```

# Root cause (frontend)

In `gcc/cobol/parse.y`, `apply_declaratives()` runs at every section and
paragraph entry:

```c
void apply_declaratives() {
  bool tf[2] = { false, true };
  for( bool *yn = tf; yn < tf + COUNT_OF(tf); yn++ ) {
    auto declaratives = current.debugging_declaratives(*yn);
    for( auto p = declaratives.begin(); p != declaratives.end(); p++ ) {
      cbl_label_t *label = symbol_label(PROGRAM, LblNone, 0, p->c_str());
      parser_perform(label);
    }
  }
}
```

`debugging_declaratives(true)` returns the `:all:` client list for
`USE … ON ALL PROCEDURES`. There is no exclusion for labels that live in
debugging sections.

So:

1. Enter `MAIN-PARA` → `apply_declaratives` → `PERFORM DBG-SEC`
2. Enter `DBG-SEC` / `DBG-PARA` → `apply_declaratives` again → `PERFORM
DBG-SEC`
3. Unbounded nesting via `parser_perform` → `__gg__pseudo_return_push`

A program-local `BYPASS` flag (as in NIST `DB105A`) cannot help: the
declarative body never runs; re-entry happens at procedure **entry** before
any statement in `DBG-PARA`.

# Suggested fix

When applying `:all:` debugging declaratives, skip procedure-names that
belong to a debugging section (or otherwise suppress `apply_declaratives`
while already inside a USE FOR DEBUGGING declarative). Matches the
standard rule that `ALL PROCEDURES` excludes debugging-section procedures.

# Impact

Any program with `USE FOR DEBUGGING ON ALL PROCEDURES` hangs under gcobol.
NIST COBOL85 `DB/DB105A` is the suite instance that surfaces this
(cobc: all PASS; gcobol: hang before first report line).

gcobol can build and run (perhaps successfully -- not yet checked) most of the
NIST suite (with some "sorry, ..." compile time exceptions).  This one is an
exception, in that it hangs, instead of running to completion, successful or
otherwise (that hang is a bit annoying, since it prevents running all the
successfully built programs in a loop).

# Disclaimer

The root cause analysis above was generated by Cursor (I asked it to build me a
minimal reproducer for the hang, and it went above and beyond) -- I can't vouch
for the correctness of it's analysis.
  • [Bug cobol/126340] New: cobo... peeterjoot at protonmail dot com via Gcc-bugs

Reply via email to