SQLite Forum

Two integer overflows in sqlite3VdbeMemSetStr() when built with -DSQLITE_MAX_LENGTH=2147483647
Login
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 ){"