CVE-2026-7111
Description
Text::CSV_XS versions before 1.62 for Perl have a use-after-free when registered callbacks extend the Perl argument stack, which may enable type confusion or memory corruption.
The Parse, print, getline, and getline_all methods invoke registered callbacks (for example after_parse, before_print, or on_error) and cache the Perl argument stack pointer across the call. If a callback extends the argument stack enough to trigger a reallocation, the return value is written through the stale pointer into the freed buffer, and the caller reads the original $self argument as the return value instead.
Calling code that expects parsed data from getline_all receives the Text::CSV_XS object in its place, leading to logic errors or crashes. Text::CSV_XS objects used without any registered callbacks are not affected.
Affected products
2Patches
1c17f31a5f2bfFix possible stack corruption (thanks leont) (issue 67)
4 files changed · +58 −22
ChangeLog+1 −0 modified@@ -1,5 +1,6 @@ 1.62 - 2026-04-25, H.Merijn Brand * It is 2026 + * Fix possible stack corruption (thanks leont) (issue 67) 1.61 - 2025-07-26, H.Merijn Brand * Add love letter to CSV from xan project with reference
cpanfile+1 −1 modified@@ -6,7 +6,7 @@ recommends "Encode" => "3.21"; on "configure" => sub { requires "ExtUtils::MakeMaker"; - recommends "ExtUtils::MakeMaker" => "7.76"; + recommends "ExtUtils::MakeMaker" => "7.78"; }; on "build" => sub {
CSV_XS.xs+18 −18 modified@@ -122,6 +122,12 @@ static unsigned char ec, ebcdic2ascii[256] = { croak ("self is not a hash ref"); \ hv = (HV *)SvRV (self) +#define undef &PL_sv_undef +#define PUT_RETURN(x) \ + SPAGAIN; \ + ST (0) = x; \ + XSRETURN (1) + /* Keep in sync with .pm! */ #define CACHE_ID_quote_char 0 #define CACHE_ID_escape_char 1 @@ -2603,7 +2609,7 @@ BOOT: Perl_load_module (aTHX_ PERL_LOADMOD_NOIMPORT, newSVpvs ("IO::Handle"), NULL, NULL, NULL); void -SetDiag (SV *self, int xse, ...) +SetDiag (SV *self, int xse, SV *line = undef) PPCODE: HV *hv; @@ -2619,8 +2625,8 @@ SetDiag (SV *self, int xse, ...) ST (0) = sv_2mortal (SvDiag (xse)); } - if (xse && items > 2 && SvPOK (ST (2))) { - sv_setpvn (ST (0), SvPVX (ST (2)), SvCUR (ST (2))); + if (xse && SvPOK (line)) { + sv_setpvn (ST (0), SvPVX (line), SvCUR (line)); SvIOK_on (ST (0)); } @@ -2670,8 +2676,8 @@ Parse (SV *self, SV *src, SV *fields, SV *fflags) av = (AV *)SvRV (fields); avf = (AV *)SvRV (fflags); - ST (0) = xsParse (self, hv, av, avf, src, 0) ? &PL_sv_yes : &PL_sv_no; - XSRETURN (1); + int x = xsParse (self, hv, av, avf, src, 0); + PUT_RETURN (x ? &PL_sv_yes : &PL_sv_no); /* XS Parse */ void @@ -2691,8 +2697,8 @@ print (SV *self, SV *io, SV *fields) av = (AV *)SvRV (fields); } - ST (0) = xsCombine (self, hv, av, io, 1) ? &PL_sv_yes : &PL_sv_no; - XSRETURN (1); + int x = xsCombine (self, hv, av, io, 1); + PUT_RETURN (x ? &PL_sv_yes : &PL_sv_no); /* XS print */ void @@ -2706,26 +2712,20 @@ getline (SV *self, SV *io) CSV_XS_SELF; av = newAV (); avf = newAV (); - ST (0) = xsParse (self, hv, av, avf, io, 1) - ? sv_2mortal (newRV_noinc ((SV *)av)) - : &PL_sv_undef; - XSRETURN (1); + int x = xsParse (self, hv, av, avf, io, 1); + PUT_RETURN (x ? sv_2mortal (newRV_noinc ((SV *)av)) : undef); /* XS getline */ void -getline_all (SV *self, SV *io, ...) +getline_all (SV *self, SV *io, SV *offset = undef, SV *length = undef) PPCODE: HV *hv; - SV *offset, *length; CSV_XS_SELF; - offset = items > 2 ? ST (2) : &PL_sv_undef; - length = items > 3 ? ST (3) : &PL_sv_undef; - - ST (0) = xsParse_all (self, hv, io, offset, length); - XSRETURN (1); + SV *x = xsParse_all (self, hv, io, offset, length); + PUT_RETURN (x); /* XS getline_all */ void
sandbox/issue-65.pl+38 −3 modified@@ -6,7 +6,7 @@ use 5.014002; use warnings; -our $VERSION = "0.02 - 20251124"; +our $VERSION = "0.03 - 20260425"; our $CMD = $0 =~ s{.*/}{}r; sub usage { @@ -25,8 +25,10 @@ sub usage { "v|verbose:1" => \(my $opt_v = 0), ) or usage (1); +my $x = @ARGV ? + $ARGV[-1] =~ m/^[0-9]+/ ? pop @ARGV : + $ARGV[ 0] =~ m/^[0-9]+/ ? shift @ARGV : 0 : 0; my $fn = shift // (-d "t" ? "sandbox/issue-65.csv" : "issue-65.csv"); -my $x = shift; say "Index"; { my $aoa = csv ( @@ -35,18 +37,51 @@ sub usage { ); } -if ($x) { +if ($x == 1) { # fix say "AP City"; my $aoh = csv ( in => $fn, headers => "auto", after_parse => sub { $_{Rndrng_Prvdr_City} eq "Chicago" or return \"skip" }, ); } +elsif ($x == 2) { # fix + say "AP 1"; + my $aoh = csv ( + in => $fn, + headers => "auto", + after_parse => sub { 1; }, + ); + } +elsif ($x == 3) { # fix + say "AP undef"; + my $aoh = csv ( + in => $fn, + headers => "auto", + after_parse => sub { undef; }, + ); + } +elsif ($x == 4) { # No fix + say "BP undef"; + my $aoh = csv ( + in => $fn, + headers => "auto", + before_print => sub { undef; }, + ); + } +elsif ($x == 5) { # fix + say "AI undef"; + my $aoh = csv ( + in => $fn, + headers => "auto", + after_in => sub { undef; }, + ); + } say "Name NPI"; { my $aoh = csv ( in => $fn, +# headers => "auto", # -- does not fix the issue filter => { Rndrng_NPI => sub { $_ > 1 }, }, ); }
Vulnerability mechanics
AI mechanics synthesis has not run for this CVE yet.
References
3- github.com/cpan-authors/Text-CSV_XS/commit/c17f31a5f2bf36674748eb4b6e25672f0571a224.patchnvdMailing ListPatch
- www.openwall.com/lists/oss-security/2026/04/29/17nvdMailing ListThird Party Advisory
- metacpan.org/release/HMBRAND/Text-CSV_XS-1.62/changesnvdRelease NotesProduct
News mentions
0No linked articles in our index yet.