diff --git a/meta-oe/recipes-devtools/perl/libdbi-perl/CVE-2026-14739.patch b/meta-oe/recipes-devtools/perl/libdbi-perl/CVE-2026-14739.patch new file mode 100644 index 0000000000..b43e3a20ba --- /dev/null +++ b/meta-oe/recipes-devtools/perl/libdbi-perl/CVE-2026-14739.patch @@ -0,0 +1,170 @@ +From 57d0a1f66b9ed1b54f11af661da4719c43409497 Mon Sep 17 00:00:00 2001 +From: "H.Merijn Brand - Tux" +Date: Sat, 4 Jul 2026 11:38:24 +0200 +Subject: [PATCH] Set a hard limit of 99999 on '?' placeholders + (CVE-2026-14739) + +CVE: CVE-2026-14739 +Upstream-Status: Backport [https://github.com/perl5-dbi/dbi/commit/2b77c88b655e9539a592c71a61fb965fc0075395] + +Backport Changes: + - Omit ChangeLog, dbixs_rev.h, lib/DBI/Changes.pm, and the DBI.pm version-number hunks because the target remains DBI 1.643. + - Omit regenerated doc/DBI.3, doc/DBI.html, doc/DBI.man, and doc/DBI.md files; retain the DBI.pm source-documentation and DBI.xs security changes unchanged. + - Correct the upstream boundary check to accept exactly 99999 placeholders and reject values above that documented limit; add t/60preparse.t regression coverage for 99999 and 100000 placeholders. + +(cherry picked from commit 2b77c88b655e9539a592c71a61fb965fc0075395) +Signed-off-by: Hetvi Thakar +--- + DBI.pm | 6 ++++-- + DBI.xs | 53 +++++++++++++++++++++++++++++++++-------------------- + t/60preparse.t | 13 ++++++++++++- + 3 files changed, 49 insertions(+), 23 deletions(-) + +diff --git a/DBI.pm b/DBI.pm +index d62a32d..b988e40 100644 +--- a/DBI.pm ++++ b/DBI.pm +@@ -7123,7 +7123,8 @@ a ref to an empty hash because they can't pre-determine the names. + It is possible that the keys in the hash returned by C + are not exactly the same as those implied by the prepared statement. + For example, DBD::Oracle translates 'C' placeholders into 'C<:pN>' +-where N is a sequence number starting at 1. ++where N is a sequence number starting at C<1> with a hard limit of ++C<99999>. + + * Values: + +@@ -7225,7 +7226,8 @@ integer. + It is also possible that the keys in the hash returned by + C are not exactly the same as those implied by the + prepared statement. For example, DBD::Oracle translates 'C' +-placeholders into 'C<:pN>' where N is a sequence number starting at 1. ++placeholders into 'C<:pN>' where N is a sequence number starting at ++C<1> with a hard limit of C<99999>. + + =head3 C + +diff --git a/DBI.xs b/DBI.xs +index 8858e21..23ad34a 100644 +--- a/DBI.xs ++++ b/DBI.xs +@@ -4201,7 +4201,14 @@ preparse(SV *dbh, const char *statement, IV ps_return, IV ps_accept, void *foo) + } + + /* XXX this allocation strategy won't work when we get to more advanced stuff */ +- new_stmt_sv = newSV(strlen(statement) * 6 + 16); ++ /* The 7 is for length increase from '?' (length 1) to :p99999 (length 7) ++ * which imposes a limit of 99999 '?' placeholders POSIX style. Actual counts ++ * are a bit higher: ++ * using factor 5: :p1 .. :p1107 ++ * using factor 6: :p1 .. :p11106 ++ * using factor 7: :p1 .. :p111105 ++ * and that count is insane already */ ++ new_stmt_sv = newSV(strlen(statement) * 7 + 16); + sv_setpv(new_stmt_sv,""); + src = statement; + dest = SvPVX(new_stmt_sv); +@@ -4340,9 +4347,9 @@ preparse(SV *dbh, const char *statement, IV ps_return, IV ps_accept, void *foo) + continue; + } + +- if ( !(*src==':' && (PS_accept(DBIpp_ph_cn) || PS_accept(DBIpp_ph_cs))) +- && !(*src=='?' && PS_accept(DBIpp_ph_qm)) +- ){ ++ if ( !(*src==':' && (PS_accept(DBIpp_ph_cn) || PS_accept(DBIpp_ph_cs))) ++ && !(*src=='?' && PS_accept(DBIpp_ph_qm)) ++ ){ + if (*src == '\'' || *src == '"') + in_quote = *src; + *dest++ = *src++; +@@ -4361,12 +4368,18 @@ preparse(SV *dbh, const char *statement, IV ps_return, IV ps_accept, void *foo) + if (PS_return(DBIpp_ph_qm)) + ; + else if (PS_return(DBIpp_ph_cn)) { /* '?' -> ':p1' (etc) */ ++ if (idx > 99999) { ++ char buf[99]; ++ sprintf(buf, "preparse found more than 99999 '?' placeholders. Limit exceeded."); ++ set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); ++ return &PL_sv_undef; ++ } + sprintf(start,":p%d", idx++); + dest = start+strlen(start); + } + else if (PS_return(DBIpp_ph_sp)) { /* '?' -> '%s' */ +- *start = '%'; +- *dest++ = 's'; ++ *start = '%'; ++ *dest++ = 's'; + } + } + else if (isDIGIT(*src)) { /* :1 */ +@@ -4374,24 +4387,24 @@ preparse(SV *dbh, const char *statement, IV ps_return, IV ps_accept, void *foo) + style = ":1"; + + if (PS_return(DBIpp_ph_cn)) { /* ':1'->':p1' */ +- idx = pln; +- *dest++ = 'p'; +- while(isDIGIT(*src)) +- *dest++ = *src++; ++ idx = pln; ++ *dest++ = 'p'; ++ while(isDIGIT(*src)) ++ *dest++ = *src++; + } + else if (PS_return(DBIpp_ph_qm) /* ':1' -> '?' */ + || PS_return(DBIpp_ph_sp) /* ':1' -> '%s' */ + ) { +- PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s"); +- dest = start + strlen(start); +- if (pln != idx) { +- char buf[99]; +- sprintf(buf, "preparse found placeholder :%d out of sequence, expected :%d", pln, idx); +- set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); +- return &PL_sv_undef; +- } +- while(isDIGIT(*src)) src++; +- idx++; ++ PS_return(DBIpp_ph_qm) ? sprintf(start,"?") : sprintf(start,"%%s"); ++ dest = start + strlen(start); ++ if (pln != idx) { ++ char buf[99]; ++ sprintf(buf, "preparse found placeholder :%d out of sequence, expected :%d", pln, idx); ++ set_err_char(dbh, imp_xxh, "1", 1, buf, 0, "preparse"); ++ return &PL_sv_undef; ++ } ++ while(isDIGIT(*src)) src++; ++ idx++; + } + } + else if (isALNUM(*src)) /* :name */ +diff --git a/t/60preparse.t b/t/60preparse.t +index 6432feb..668dfa3 100755 +--- a/t/60preparse.t ++++ b/t/60preparse.t +@@ -11,7 +11,7 @@ BEGIN { + plan skip_all => 'preparse not supported for DBI::PurePerl'; + } + else { +- plan tests => 39; ++ plan tests => 43; + } + } + +@@ -71,6 +71,17 @@ is( pp($dbh, "a = :name", DBIpp_ph_sp, DBIpp_ph_cs), "a = %s" ); + + is( pp($dbh, "a = ? b = ? c = ?", DBIpp_ph_cn, DBIpp_ph_XX), "a = :p1 b = :p2 c = :p3" ); + ++my $max_placeholders = pp( ++ $dbh, "?" x 99999, DBIpp_ph_cn, DBIpp_ph_qm ++); ++ok(defined $max_placeholders, "accepts the documented limit of 99999 placeholders"); ++is(substr($max_placeholders, -7), ":p99999", "numbers the final allowed placeholder"); ++ ++is(pp($dbh, "?" x 100000, DBIpp_ph_cn, DBIpp_ph_qm), undef, ++ "rejects placeholders above the documented limit"); ++is($DBI::errstr, "preparse found more than 99999 '?' placeholders. Limit exceeded.", ++ "reports the placeholder limit"); ++ + ## Placeholders inside comments (should be ignored where comments style is accepted): + + is( pp( $dbh, diff --git a/meta-oe/recipes-devtools/perl/libdbi-perl_1.643.bb b/meta-oe/recipes-devtools/perl/libdbi-perl_1.643.bb index c604e46334..4733378700 100644 --- a/meta-oe/recipes-devtools/perl/libdbi-perl_1.643.bb +++ b/meta-oe/recipes-devtools/perl/libdbi-perl_1.643.bb @@ -17,6 +17,7 @@ SRC_URI = "http://search.cpan.org/CPAN/authors/id/T/TI/TIMB/DBI-${PV}.tar.gz \ file://CVE-2026-14380_p2.patch \ file://CVE-2026-14380_p3.patch \ file://CVE-2026-14380_p4.patch \ + file://CVE-2026-14739.patch \ " SRC_URI[md5sum] = "352f80b1e23769c116082a90905d7398" SRC_URI[sha256sum] = "8a2b993db560a2c373c174ee976a51027dd780ec766ae17620c20393d2e836fa"