Skip to content

Commit af62106

Browse files
committed
check the IO object exists when writing to IO magic variables
pp_select() ensures that the GV in PL_defoutgv has an IO object when it the default output is set, but can't prevent that GV being cleared afterwards, resulting in a seg fault when the variable is written. To prevent this, check PL_defoutgv has an IO object before trying to write to it. Fixes #20733
1 parent fa1fe46 commit af62106

File tree

2 files changed

+56
-12
lines changed

2 files changed

+56
-12
lines changed

mg.c

+41-11
Original file line numberDiff line numberDiff line change
@@ -3072,25 +3072,55 @@ Perl_magic_set(pTHX_ SV *sv, MAGIC *mg)
30723072
IoLINES(GvIOp(PL_last_in_gv)) = SvIV(sv);
30733073
break;
30743074
case '^':
3075-
Safefree(IoTOP_NAME(GvIOp(PL_defoutgv)));
3076-
IoTOP_NAME(GvIOp(PL_defoutgv)) = savesvpv(sv);
3077-
IoTOP_GV(GvIOp(PL_defoutgv)) = gv_fetchsv(sv, GV_ADD, SVt_PVIO);
3075+
{
3076+
IO * const io = GvIO(PL_defoutgv);
3077+
if (!io)
3078+
break;
3079+
3080+
Safefree(IoTOP_NAME(io));
3081+
IoTOP_NAME(io) = savesvpv(sv);
3082+
IoTOP_GV(io) = gv_fetchsv(sv, GV_ADD, SVt_PVIO);
3083+
}
30783084
break;
30793085
case '~':
3080-
Safefree(IoFMT_NAME(GvIOp(PL_defoutgv)));
3081-
IoFMT_NAME(GvIOp(PL_defoutgv)) = savesvpv(sv);
3082-
IoFMT_GV(GvIOp(PL_defoutgv)) = gv_fetchsv(sv, GV_ADD, SVt_PVIO);
3086+
{
3087+
IO * const io = GvIO(PL_defoutgv);
3088+
if (!io)
3089+
break;
3090+
3091+
Safefree(IoFMT_NAME(io));
3092+
IoFMT_NAME(io) = savesvpv(sv);
3093+
IoFMT_GV(io) = gv_fetchsv(sv, GV_ADD, SVt_PVIO);
3094+
}
30833095
break;
30843096
case '=':
3085-
IoPAGE_LEN(GvIOp(PL_defoutgv)) = (SvIV(sv));
3097+
{
3098+
IO * const io = GvIO(PL_defoutgv);
3099+
if (!io)
3100+
break;
3101+
3102+
IoPAGE_LEN(io) = (SvIV(sv));
3103+
}
30863104
break;
30873105
case '-':
3088-
IoLINES_LEFT(GvIOp(PL_defoutgv)) = (SvIV(sv));
3089-
if (IoLINES_LEFT(GvIOp(PL_defoutgv)) < 0L)
3090-
IoLINES_LEFT(GvIOp(PL_defoutgv)) = 0L;
3106+
{
3107+
IO * const io = GvIO(PL_defoutgv);
3108+
if (!io)
3109+
break;
3110+
3111+
IoLINES_LEFT(io) = (SvIV(sv));
3112+
if (IoLINES_LEFT(io) < 0L)
3113+
IoLINES_LEFT(io) = 0L;
3114+
}
30913115
break;
30923116
case '%':
3093-
IoPAGE(GvIOp(PL_defoutgv)) = (SvIV(sv));
3117+
{
3118+
IO * const io = GvIO(PL_defoutgv);
3119+
if (!io)
3120+
break;
3121+
3122+
IoPAGE(io) = (SvIV(sv));
3123+
}
30943124
break;
30953125
case '|':
30963126
{

t/io/defout.t

+15-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ BEGIN {
1414

1515
$|=0; # test.pl makes it 1, and that conflicts with the below.
1616

17-
plan tests => 16;
17+
plan tests => 22;
1818

1919

2020
my $stdout = *STDOUT;
@@ -48,6 +48,20 @@ $- = 1; pass '$- = 1';
4848
$% = 1; pass '$% = 1';
4949
$| = 1; pass '$| = 1';
5050

51+
# test a NULLed GV
52+
my $t = tempfile;
53+
open FOO, ">", $t or die;
54+
select(FOO);
55+
my $io = *FOO{IO};
56+
*FOO = 0;
57+
$^ = 1; pass 'empty GV: $^ = 1';
58+
$~ = 1; pass 'empty GV: $~ = 1';
59+
$= = 1; pass 'empty GV: $= = 1';
60+
$- = 1; pass 'empty GV: $- = 1';
61+
$% = 1; pass 'empty GV: $% = 1';
62+
$| = 1; pass 'empty GV: $| = 1';
63+
close $io;
64+
5165
# Switch to STDERR for this test, so we do not lose our test output
5266
my $stderr = *STDERR;
5367
select($stderr);

0 commit comments

Comments
 (0)