[PATCH] apr_dbd_sqlite3: prevent OOB read in blob binding (affects 1.6.3 and trunk)
Max Lane <[email protected]> Thu, 9 Jul 2026 21:22:51 +0300
| Newsgroups | gmane.comp.apache.apr.devel |
|---|---|
| Message-ID | <CAH+gvfRdFbJVKNgCSq3_b8g9gJByGSnsEqjCvO8FKBa+w25gag@mail.gmail.com> |
--000000000000f07fc2065631b608 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hello, I discovered a security issue in the SQLite3 DBD driver of apr-util (version 1.6.3, and it also exists in the current trunk). The function dbd_sqlite3_bind() in apr_dbd_sqlite3.c reads a blob size via atoi() from a user-supplied string and passes it directly to sqlite3_bind_blob() without any validation against the actual length of the data buffer. An attacker can supply a size larger than the real data length, causing an out=E2=80=91of=E2=80=91bounds read when SQLite copies the blob. = This may lead to information disclosure or a crash (DoS). The issue affects the text=E2=80=91based BLOB binding path (pquery/pselect family). Binary BLOB binding (pbquery/pbselect) is not affected by this specific code path, but it also lacks validation (a separate issue). The proposed patch adds a safety check: if the announced size exceeds strlen(data), the parameter is bound as NULL instead of a blob. This prevents the OOB read and falls back to a safe behavior. This change assumes that text=E2=80=91based BLOB bindings are only used for textual data (the common case). For genuine binary data, callers should migrate to the binary bind functions (pbquery/pbselect), which already expect a binary size argument. The patch follows the Apache C Language Style Guide (4=E2=80=91space indent= , no tabs, brace placement, spacing around operators). Please review and consider applying. Thank you, Max Lane <[email protected]> --- Index: dbd/apr_dbd_sqlite3.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- dbd/apr_dbd_sqlite3.c (working copy) +++ dbd/apr_dbd_sqlite3.c (working copy) @@ -458,7 +458,22 @@ char *data =3D (char *)values[j]; int size =3D atoi((char*)values[++j]); /* skip table and column */ j +=3D 2; - sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC); + /* + * Validate that the announced size does not exceed the actual + * length of the data. Otherwise an attacker could cause a= n + * out-of-bounds read in sqlite3_bind_blob(). + * + * For binary BLOB data the binary bind functions + * (pbquery/pbselect) should be used instead. + */ + if (size > (int)strlen(data)) { + sqlite3_bind_null(stmt, i + 1); + } + else { + sqlite3_bind_blob(stmt, i + 1, data, size, SQLITE_STATIC); + } } break; --000000000000f07fc2065631b608 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr">Hello,<br><br>I discovered a security issue in the SQLite3= DBD driver of apr-util<br>(version 1.6.3, and it also exists in the curren= t trunk).<br><br>The function dbd_sqlite3_bind() in apr_dbd_sqlite3.c reads= a blob size<br>via atoi() from a user-supplied string and passes it direct= ly to<br>sqlite3_bind_blob() without any validation against the actual leng= th<br>of the data buffer.<br><br>An attacker can supply a size larger than = the real data length,<br>causing an out=E2=80=91of=E2=80=91bounds read when= SQLite copies the blob. This may<br>lead to information disclosure or a cr= ash (DoS).<br><br>The issue affects the text=E2=80=91based BLOB binding pat= h (pquery/pselect<br>family). Binary BLOB binding (pbquery/pbselect) is not= affected by<br>this specific code path, but it also lacks validation (a se= parate<br>issue).<br><br>The proposed patch adds a safety check: if the ann= ounced size exceeds<br>strlen(data), the parameter is bound as NULL instead= of a blob. This<br>prevents the OOB read and falls back to a safe behavior= .<br><br>This change assumes that text=E2=80=91based BLOB bindings are only= used for<br>textual data (the common case). For genuine binary data, calle= rs<br>should migrate to the binary bind functions (pbquery/pbselect), which= <br>already expect a binary size argument.<br><br>The patch follows the Apa= che C Language Style Guide (4=E2=80=91space indent,<br>no tabs, brace place= ment, spacing around operators).<br><br>Please review and consider applying= .<br><br>Thank you,<br>Max Lane<br><<a href=3D"mailto:max.lane.dev@gmail= .com">[email protected]</a>><br><br>---<br>Index: dbd/apr_dbd_sqlit= e3.c<br>=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D<br>--- dbd/apr= _dbd_sqlite3.c (working copy)<br>+++ dbd/apr_dbd_sqlite3.c (working copy)<b= r>@@ -458,7 +458,22 @@<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0char *data =3D (char *)values[j];<br>=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int size =3D atoi((char*)values[++j])= ;<br>=C2=A0<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0/* skip table and column */<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0j +=3D 2;<br>=C2=A0<br>- =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3_bind_blob(stmt, i + 1, data, size, SQLIT= E_STATIC);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*<b= r>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * Validate that= the announced size does not exceed the actual<br>+ =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * length of the data. Otherwise an attac= ker could cause an<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 * out-of-bounds read in sqlite3_bind_blob().<br>+ =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 * For binary BLOB data the binary bind function= s<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * (pbquery/p= bselect) should be used instead.<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 */<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0if (size > (int)strlen(data)) {<br>+ =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3_bind_null(stmt, i + 1)= ;<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0}<br>+ =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0else {<br>+ =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0sqlite3_bind_blob(st= mt, i + 1, data, size, SQLITE_STATIC);<br>+ =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0}<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0}<br>=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0break;</div> --000000000000f07fc2065631b608--