Skip to content

fix: allow filehandle duplication after tie #1165

Description

@fglock

Summary

Duplicating a tied filehandle with open(..., '>&HANDLE') fails with EBADF.
This prevents CPAN::Test::Reporter 0.02 from loading because bundled
CPAN.pm duplicates STDOUT during initialization.

Minimal reproducer

package Catch;
sub TIEHANDLE { bless {}, shift }
sub PRINT { }

package main;
tie *STDOUT, 'Catch' or die "tie failed: $!\\n";
my $saved = *SAVEOUT;
open($saved, '>&STDOUT') or die "dup failed: $!\\n";

Expected behavior

System Perl 5.42 exits successfully. Duplicating a tied handle is valid; the
duplicate preserves the tied-handle behavior.

Actual behavior

Both PerlOnJava backends fail:

dup failed: Bad file descriptor

The process exits with status 9.

CPAN impact

In CPAN tester run 20260827-102602-1611, CPAN::Test::Reporter 0.02 fails its
only test program while loading its embedded test. That test ties STDOUT to
capture TAP output, then use CPAN loads bundled CPAN.pm. CPAN.pm runs:

my $x = *SAVEOUT;
open($x, '>&STDOUT') or die "dup failed";

and dies at the duplicate operation.

The target suite was independently verified under system Perl using an
isolated installation of its missing MailTools dependency: all 7 tests pass.

Likely cause

IOOperator.openFileHandleDup obtains the current STDOUT as a TieHandle,
but rejects it because it has no direct ioHandle. The duplication path needs
to preserve/delegate the tied handle instead of requiring a native or Java
backing descriptor.

Impact

This is broader than CPAN::Test::Reporter: any output-capture or logging code
that ties a handle and later duplicates it can fail. It is particularly likely
to affect test infrastructure such as Capture::Tiny-style workflows.

Acceptance criteria

  • The minimal reproducer succeeds on both JVM and interpreter backends.
  • open($saved, '>&STDOUT') preserves tied PRINT dispatch on the duplicate.
  • CPAN::Test::Reporter 0.02's embedded test passes with its pure-Perl
    dependencies available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions