Fix heap overflow when preparsing SQL statements with excessive placeholders (CVE-2026-14739)

Resolves: RHEL-193298
This commit is contained in:
Michal Josef Špaček 2026-07-28 10:43:15 +02:00
parent 9a1f6bd5d2
commit b0d0d60e8a
2 changed files with 135 additions and 1 deletions

View File

@ -0,0 +1,128 @@
From 877297ada6974a4f35db2cb30ee3f32a8c50bca3 Mon Sep 17 00:00:00 2001
From: "H.Merijn Brand - Tux" <linux@tux.freedom.nl>
Date: Sat, 4 Jul 2026 11:38:24 +0200
Subject: [PATCH] Set a hard limit of 99999 on '?' placeholders
(CVE-2026-14739)
---
DBI.pm | 6 ++++--
DBI.xs | 53 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 37 insertions(+), 22 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<ParamValues>
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<ParamArrays> 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<RowsInCache>
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 */

View File

@ -34,13 +34,15 @@
Name: perl-DBI
Version: 1.641
Release: 5%{?dist}
Release: 6%{?dist}
Summary: A database access API for perl
License: GPL+ or Artistic
URL: http://dbi.perl.org/
Source0: http://www.cpan.org/authors/id/T/TI/TIMB/DBI-%{version}.tar.gz
# RHEL-184974, CVE-2026-9698, Fix stack overflow and buffer overflow in DBI.xs
Patch0: DBI-1.641-Fix-CVE-2026-9698.patch
# RHEL-193298, CVE-2026-14739, Heap overflow when preparsing SQL statements with excessive placeholders
Patch1: DBI-1.643-Fix-CVE-2026-14739.patch
BuildRequires: coreutils
BuildRequires: findutils
BuildRequires: gcc
@ -220,6 +222,10 @@ make test
%endif
%changelog
* Tue Jul 28 2026 Michal Josef Špaček <mspacek@redhat.com> - 1.641-6
- Fix heap overflow when preparsing SQL statements with excessive placeholders (CVE-2026-14739)
Resolves: RHEL-193298
* Mon Jun 22 2026 Michal Josef Špaček <mspacek@redhat.com> - 1.641-5
- Fix stack overflow and buffer overflow in DBI.xs (CVE-2026-9698)
Resolves: RHEL-184974