SQLite Forum

Two integer overflows in sqlite3VdbeMemSetStr() when built with -DSQLITE_MAX_LENGTH=2147483647
Login

Two integer overflows in sqlite3VdbeMemSetStr() when built with -DSQLITE_MAX_LENGTH=2147483647

(1.1) By Even Rouault (rouault) on 2021-05-06 15:53:49 edited from 1.0

Two issues:
- Binding a UTF-16 string of size 0x80000000U results in a integer overflow in sqlite3VdbeMemSetStr()
- Binding a UTF-8 string of size 0x7fffffff in SQLITE_TRANSIENT mode results in a integer overflow in sqlite3VdbeMemSetStr()

1) Build sqlite3 (3.35.5 here), on 64 bit, with CFLAGS="-DSQLITE_MAX_LENGTH=2147483647 -ftrapv -g" ./configure

2) Build the following test program:

#include <assert.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    sqlite3* db = NULL;
    sqlite3_stmt* stmt = NULL;
    char* bigtext;
    int rc;
    rc = sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READWRITE, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_exec(db, "CREATE TABLE t(c)", NULL, NULL, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_prepare_v2(db, "INSERT INTO t VALUES (?)", -1, &stmt, NULL);
    assert(rc == SQLITE_OK);
    bigtext = malloc( 0x80000000U + 2 );
    assert(bigtext);
    memset(bigtext, 1, 0x80000000U );
    bigtext[0x80000000U + 0] = 0;
    bigtext[0x80000000U + 1] = 0;
    rc = sqlite3_bind_text16(stmt, 1, bigtext, -1, SQLITE_STATIC);
    assert(rc == SQLITE_OK);
    return 0;
}

3) LD_LIBRARY_PATH=$PWD/.libs gdb ./test
(gdb) r
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50	../sysdeps/unix/sysv/linux/raise.c: Aucun fichier ou dossier de ce type.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7c4c859 in __GI_abort () at abort.c:79
#2  0x00007ffff7e539da in __addvsi3.cold () from /home/even/sqlite-autoconf-3350500/.libs/libsqlite3.so.0
#3  0x00007ffff7e8c905 in sqlite3VdbeMemSetStr (pMem=0x555555567350, z=0x7fff77aac010 '\001' <repeats 200 times>..., n=-1, enc=2 '\002', xDel=0x0) at sqlite3.c:77568
#4  0x00007ffff7e96e14 in bindText (pStmt=0x555555566b08, i=1, zData=0x7fff77aac010, nData=-1, xDel=0x0, encoding=2 '\002') at sqlite3.c:85016
#5  0x00007ffff7e97295 in sqlite3_bind_text16 (pStmt=0x555555566b08, i=1, zData=0x7fff77aac010, nData=-1, xDel=0x0) at sqlite3.c:85145
#6  0x00005555555553b9 in main ()
(gdb) up
#1  0x00007ffff7c4c859 in __GI_abort () at abort.c:79
79	abort.c: Aucun fichier ou dossier de ce type.
(gdb) 
#2  0x00007ffff7e539da in __addvsi3.cold () from /home/even/sqlite-autoconf-3350500/.libs/libsqlite3.so.0
(gdb) 
#3  0x00007ffff7e8c905 in sqlite3VdbeMemSetStr (pMem=0x555555567350, z=0x7fff77aac010 '\001' <repeats 200 times>..., n=-1, enc=2 '\002', xDel=0x0) at sqlite3.c:77568
77568	      for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
(gdb) print nByte
$1 = 2147483646

Proposed fix:
u32 nByteU;
for(nByteU=0; nByteU<=(u32)iLimit && (z[nByteU] | z[nByteU+1]); nByteU+=2){}
nByte = (int)MIN((u32)iLimit, nByteU);

4) Build the test program:

#include <assert.h>
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    sqlite3* db = NULL;
    sqlite3_stmt* stmt = NULL;
    char* bigtext;
    int rc;
    rc = sqlite3_open_v2(":memory:", &db, SQLITE_OPEN_READWRITE, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_exec(db, "CREATE TABLE t(c)", NULL, NULL, NULL);
    assert(rc == SQLITE_OK);
    rc = sqlite3_prepare_v2(db, "INSERT INTO t VALUES (?)", -1, &stmt, NULL);
    assert(rc == SQLITE_OK);
    bigtext = malloc( 0x7FFFFFFFU + 1 );
    assert(bigtext);
    memset(bigtext, 1, 0x7FFFFFFFU );
    bigtext[0x7FFFFFFFU] = 0;
    rc = sqlite3_bind_text(stmt, 1, bigtext, -1, SQLITE_TRANSIENT);
    assert(rc == SQLITE_OK);
    return 0;
}

5) LD_LIBRARY_PATH=$PWD/.libs gdb ./test
(gdb) r
Program received signal SIGSEGV, Segmentation fault.
__memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:503
503	../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S: Aucun fichier ou dossier de ce type.
(gdb) bt
#0  __memmove_avx_unaligned_erms () at ../sysdeps/x86_64/multiarch/memmove-vec-unaligned-erms.S:503
#1  0x00007ffff7e8c9d9 in sqlite3VdbeMemSetStr (pMem=0x555555567350, z=0x7fff77aac010 '\001' <repeats 200 times>..., n=-1, enc=1 '\001', xDel=0xffffffffffffffff) at sqlite3.c:77591
#2  0x00007ffff7e96e14 in bindText (pStmt=0x555555566b08, i=1, zData=0x7fff77aac010, nData=-1, xDel=0xffffffffffffffff, encoding=1 '\001') at sqlite3.c:85016
#3  0x00007ffff7e971d1 in sqlite3_bind_text (pStmt=0x555555566b08, i=1, zData=0x7fff77aac010 '\001' <repeats 200 times>..., nData=-1, xDel=0xffffffffffffffff) at sqlite3.c:85119
#4  0x00005555555553a9 in main ()
(gdb) up
#1  0x00007ffff7e8c9d9 in sqlite3VdbeMemSetStr (pMem=0x555555567350, z=0x7fff77aac010 '\001' <repeats 200 times>..., n=-1, enc=1 '\001', xDel=0xffffffffffffffff) at sqlite3.c:77591
77591	    memcpy(pMem->z, z, nAlloc);
(gdb) print nAlloc
$1 = 2147483648
(gdb) print pMem->n
$4 = 0

The issue is that the (int)MAX(nAlloc,32) at line 77588 evaluates to 0. And the probably root cause if the "if( nByte>iLimit ){" check at line 77582 that should be "if( nAlloc>(u32)iLimit ){"

(2) By Larry Brasfield (larrybr) on 2021-05-01 15:04:11 in reply to 1.0 [link]

I think this should be treated as a documentation improvement opportunity, with SQLITE_MAX_LENGTH required to be very slightly less than now stated.

Your post reminds me of a bubble-gum wrapper cartoon I saw long ago. The patient tells his doctor, "It hurts when I bang my head like this." The doctor says, "Stop doing that." I'm sure you realize that no system existing now or likely to exist in the next century will be legitimately hindered by the issue you report.

BTW, there is no need to post multiple times just because your first post(s) do not immediately appear in the "Threads" display. Post from new participants go through moderation and there are no full-time moderators. So give it a few hours, particularly for non-pressing problems.

(3) By Even Rouault (rouault) on 2021-05-01 15:13:47 in reply to 2 [link]

I'm fine if it is dealt as a documentation fix to lower the max value for SQLITE_MAX_LENGTH. Just trying to identify where the limits can be pushed :-) That said 2 GB blobs in a DB that allows to grow to several TB doesn't seem completely invalid to me.

Regarding multiple posts, I'm not sure what you refer too. I did post another one but this is a different issue (still with -DSQLITE_MAX_LENGTH=2147483647)