Menu ▾ ▴

#2087 ADDRESS WITH: ERROR target same as INPUT is not buffered; wrong results, crash or hang

5.3.0
accepted
Erich
None
tests
3
10 hours ago
2026-09-23
No

Version: ooRexx 5.3.0 r13237, 64-bit, Linux

When the ERROR target of an ADDRESS ... WITH instruction is the same as the INPUT source, the error target is not buffered: it is initialised (emptied / truncated) before the input is read. Depending on the case this gives wrong results, a crash or a hang.

Case 1: wrong result

s.0 = 2; s.1 = "one"; s.2 = "two"
address system "cat; echo err >&2" with input stem s. output stem o. error stem s.
say "rc="rc "o.0="o.0 "o.1="o.1 "o.2="o.2
say "s.0="s.0 "s.1="s.1

Expected: rc=0 o.0=2 o.1=one o.2=two and s.0=1 s.1=err

Actual: rc=0 o.0=2 o.1=S. o.2=S. and s.0=1 s.1=err

The ERROR target empties s. before the input is read, so cat receives the stem's default value (its name) instead of the data.

Case 2: segmentation fault

s.0 = 2; s.1 = "one"; s.2 = "two"
address system "cat; echo err >&2" with input stem s. error stem s.
say "rc="rc "s.0="s.0 "s.1="s.1

Actual: the interpreter crashes with SIGSEGV (exit code 139) and prints nothing.

Case 3: hang

/* f.txt contains "one\ntwo\n" */
address system "cat; echo err >&2" with input stream "f.txt" output stem o. error stream "f.txt"
say "rc="rc "o.0="o.0 "o.1="o.1

Actual: hangs. The ERROR target opens f.txt WRITE REPLACE, which truncates it before the input is read. The input is then empty, which triggers bug #NNN (the stdin pipe is never closed when the input is empty).

Control: the same conflict between OUTPUT and INPUT is handled correctly:

s.0 = 2; s.1 = "one"; s.2 = "two"
address system "cat; echo err >&2" with input stem s. output stem s.

gives s.0=2 s.1=one s.2=two, as expected.

Cause

interpreter/instructions/CommandIOContext.cpp, CommandIOContext::resolveConflicts():

// now we still could have a conflict beteen the input ane error, so check that too
else if (error != OREF_NULL && error->needsBuffering(input))
{
    // make this buffered until the command returns
    output = new BufferingOutputTarget(output);
}

The branch that detects the INPUT/ERROR conflict wraps output instead of error. As a result:

  • error is not buffered, so its init() empties the stem, or truncates the file, before the input is read (cases 1 and 3);
  • when there is no OUTPUT redirection, a BufferingOutputTarget(NULL) is created, and its cleanup() calls target->init() on a NULL pointer (case 2).

Suggested fix

else if (error != OREF_NULL && error->needsBuffering(input))
{
    error = new BufferingOutputTarget(error);
}

This branch is only reached when OUTPUT and ERROR are not the same target (if they were, the previous branch would already have buffered both), so wrapping error alone is enough.

Discussion

  • Erich

    Erich - 10 hours ago
    • status: open --> accepted
    • assigned_to: Erich
    • Pending work items: none --> tests
    • Priority: 5 --> 3
     
  • Erich

    Erich - 10 hours ago

    Committed the suggested code fix and added a simple test with revision [r13262]

     

    Related

    Commit: [r13262]

Anonymous
Anonymous

Add attachments
Cancel