Summary
stream_cleanup_all() maps the loop slot index to the wrong file descriptor, so an active redirection on stdout is never restored and its backup fd leaks.
Location
source/utils/stream.c:188 — stream_cleanup_all().
void stream_cleanup_all(void)
{
for (int i = 0; i < MAX_STREAMS; i++) {
stream_info_t *info = &streams[i];
if (info->is_active) {
int fd = (i == 0) ? STDOUT_FILENO : STDERR_FILENO; // <-- wrong mapping
stream_restore(fd);
}
}
}
Mechanism
The streams[] table is indexed by fd — is_valid_fd() accepts 0/1/2 and stream_redirect() uses &streams[fd]. So slot i is fd i. But cleanup remaps i == 0 → STDOUT_FILENO (1), else → STDERR_FILENO (2):
stdout redirected → active slot is i == 1 → calls stream_restore(STDERR_FILENO=2) → inspects streams[2] (inactive) → returns without doing anything. stdout is never restored; backup_fd leaks.
i == 0 (stdin slot) would restore STDOUT — also wrong.
Suggested fix
The index is already the fd:
if (info->is_active)
stream_restore(i);
Severity
Medium/Low — confined to the stdout/stderr capture utility (redirect_stdout() / redirect_stderr()), but leaves descriptors redirected and leaked on the cleanup path.
Summary
stream_cleanup_all()maps the loop slot index to the wrong file descriptor, so an active redirection onstdoutis never restored and its backup fd leaks.Location
source/utils/stream.c:188—stream_cleanup_all().Mechanism
The
streams[]table is indexed by fd —is_valid_fd()accepts0/1/2andstream_redirect()uses&streams[fd]. So slotiis fdi. But cleanup remapsi == 0 → STDOUT_FILENO (1), else→ STDERR_FILENO (2):stdoutredirected → active slot isi == 1→ callsstream_restore(STDERR_FILENO=2)→ inspectsstreams[2](inactive) → returns without doing anything.stdoutis never restored;backup_fdleaks.i == 0(stdin slot) would restoreSTDOUT— also wrong.Suggested fix
The index is already the fd:
Severity
Medium/Low — confined to the stdout/stderr capture utility (
redirect_stdout()/redirect_stderr()), but leaves descriptors redirected and leaked on the cleanup path.