https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88240

--- Comment #10 from Thomas De Schampheleire <patrickdepinguin at gmail dot 
com> ---
I was able to further investigate and reduce the problem.
Qemu is now out of the picture, I can reproduce the issue directly on a real
CPU. All I need to do is enable the 'underflow' exception bit using
feenableexcept. Note that, as you will see below in the gdb output, the
denormal exception is not enabled.

Toolchain is still the same: gcc 7.3.0, glibc 2.27, binutils 2.30. See details
in bug description.

Below is the simplified test program, based on [1] with the addition of the
exception enabling code.


#define _GNU_SOURCE
#include <stdio.h>
#include <sqlite3.h>
#include <fenv.h>

static int callback(void *NotUsed, int argc, char **argv, char **azColName){
  int i;
  for(i=0; i<argc; i++){
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}

int main(int argc, char **argv){
  sqlite3 *db;
  char *zErrMsg = 0;
  int rc;

  if( argc!=4 ){
    fprintf(stderr, "Usage: %s EXCEPTIONS DATABASE SQL-STATEMENT\n", argv[0]);
    return(1);
  }

  // <<<<<<<<<<<< This code added to allow enabling exceptions

  unsigned short fctrl = 0;
  if (argv[1][0] == '1') {
      printf("Enabling exception: FE_UNDERFLOW\n");
      feenableexcept(FE_UNDERFLOW);
  }
  else {
      printf("Not enabling exceptions.\n");
  }
  asm volatile("fstcw %0" : "=m" (fctrl) );
  printf("fctrl = %04x\n", fctrl);

  // >>>>>>>>>>>>>>>>>>>>> end of added code

  rc = sqlite3_open(argv[2], &db);
  if( rc ){
    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
    sqlite3_close(db);
    return(1);
  }
  rc = sqlite3_exec(db, argv[3], callback, 0, &zErrMsg);
  if( rc!=SQLITE_OK ){
    fprintf(stderr, "SQL error: %s\n", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  sqlite3_close(db);
  return 0;
}


I compiled this code and sqlite3 (version 3210000, but I don't think the exact
version matters much) with the mentioned toolchain.

Compilation command used for the test program:
/home/tdescham/repo/isam/buildroot-qemu/output/host/opt/ext-toolchain/bin/i686-pc-linux-gnu-gcc
--sysroot
/home/tdescham/repo/isam/buildroot-qemu/output/host/i686-buildroot-linux-gnu/sysroot
-march=pentiumpro -o sqlite-test sqlite-test.c -lsqlite3 -lm -g -O0

To reproduce, first create a simple database, not yet enabling exceptions:

$ env
LD_LIBRARY_PATH=/home/tdescham/repo/isam/buildroot-qemu/output/staging/usr/lib
./sqlite-test 0 /tmp/foo.db "create table foobar (id int primary key, name
text)"
Not enabling exceptions.
fctrl = 037f


Now, we can trigger the bug by showing some SQL output, if we enable the
underflow exception:

$ env
LD_LIBRARY_PATH=/home/tdescham/repo/isam/buildroot-qemu/output/staging/usr/lib
./sqlite-test 1 /tmp/foo.db "select sql from sqlite_master where sql not NULL;"
Enabling exception: FE_UNDERFLOW
fctrl = 036f
fish: Job 2, 'env LD_LIBRARY_PATH=/home/tdescā€¦' terminated by signal SIGFPE
(Floating point exception)


If we do not enable the underflow exception, everything is fine:

$ env
LD_LIBRARY_PATH=/home/tdescham/repo/isam/buildroot-qemu/output/staging/usr/lib
./sqlite-test 0 /tmp/foo.db "select sql from sqlite_master where sql not NULL;"
Not enabling exceptions.
fctrl = 037f
sql = CREATE TABLE foobar (id int primary key, name text)



Here is the output of a debug session using gdb:


$ env
LD_LIBRARY_PATH=/home/tdescham/repo/isam/buildroot-qemu/output/staging/usr/lib
gdb ./sqlite-test
GNU gdb (Gentoo 8.1 p1) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./sqlite-test...done.
(gdb) display $fctrl
1: $fctrl = <error: No registers.>
(gdb) display $fstat
2: $fstat = <error: No registers.>
(gdb) display $ftag
3: $ftag = <error: No registers.>
(gdb) display $fop
4: $fop = <error: No registers.>
(gdb) display $st0
5: $st0 = <error: No registers.>
(gdb) display $st1
6: $st1 = <error: No registers.>
(gdb) display $st2
7: $st2 = <error: No registers.>
(gdb) break sqlite3VdbeMemStringify
Function "sqlite3VdbeMemStringify" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (sqlite3VdbeMemStringify) pending.

(gdb) run 1 /tmp/test.db "select sql from sqlite_master where sql not NULL;"
Starting program: /tmp/x86/sqlite-test 1 /tmp/test.db "select sql from
sqlite_master where sql not NULL;"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Enabling exception: FE_UNDERFLOW
fctrl = 036f

Breakpoint 1, sqlite3VdbeMemStringify (pMem=0x8064290, enc=0x1, bForce=0x0)
    at sqlite3.c:70725
70725   SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8
bForce){
1: $fctrl = 0x36f
2: $fstat = 0x0
3: $ftag = 0xffff
4: $fop = 0x0
5: $st0 = 0
6: $st1 = 0
7: $st2 = 0

(gdb) break *(&sqlite3VdbeMemStringify + 68)
Breakpoint 2 at 0xf7f53bd4: file sqlite3.c, line 70748.

##
## NOTE: Showing disassembly of this function for context.
## Bug will occur at offset 68, but only after passing it the second time.
##

(gdb) disassemble /m
Dump of assembler code for function sqlite3VdbeMemStringify:
70725   SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8
bForce){
=> 0xf7f53b90 <+0>:     push   %ebp
   0xf7f53b91 <+1>:     mov    %esp,%ebp
   0xf7f53b93 <+3>:     push   %edi
   0xf7f53b94 <+4>:     push   %esi
   0xf7f53b95 <+5>:     mov    %eax,%esi
   0xf7f53b97 <+7>:     push   %ebx
   0xf7f53b98 <+8>:     sub    $0x1c,%esp
   0xf7f53b9f <+15>:    mov    %dl,-0x25(%ebp)
   0xf7f53ba7 <+23>:    mov    %cl,-0x26(%ebp)
   0xf7f53baa <+26>:    call   0xf7f37b10 <__x86.get_pc_thunk.bx>
   0xf7f53baf <+31>:    add    $0x7a451,%ebx

70726     int fg = pMem->flags;
   0xf7f53b9b <+11>:    movzwl 0x8(%eax),%eax
   0xf7f53bb5 <+37>:    mov    %eax,-0x24(%ebp)

70727     const int nByte = 32;
70728   
70729     assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
70730     assert( !(fg&MEM_Zero) );
70731     assert( !(fg&(MEM_Str|MEM_Blob)) );
70732     assert( fg&(MEM_Int|MEM_Real) );
70733     assert( (pMem->flags&MEM_RowSet)==0 );
70734     assert( EIGHT_BYTE_ALIGNMENT(pMem) );
70735   
70736   
70737     if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
   0xf7f53ba2 <+18>:    mov    $0x20,%edx
   0xf7f53bb8 <+40>:    mov    %esi,%eax
   0xf7f53bba <+42>:    call   0xf7f4f6d5 <sqlite3VdbeMemClearAndResize>
   0xf7f53bbf <+47>:    test   %eax,%eax
   0xf7f53bc1 <+49>:    je     0xf7f53bce <sqlite3VdbeMemStringify+62>

70738       pMem->enc = 0;
   0xf7f53bc3 <+51>:    movb   $0x0,0xa(%esi)

70739       return SQLITE_NOMEM_BKPT;
   0xf7f53bc7 <+55>:    mov    $0x7,%edi
   0xf7f53bcc <+60>:    jmp    0xf7f53c3a <sqlite3VdbeMemStringify+170>

70740     }
70741   
70742     /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
70743     ** string representation of the value. Then, if the required encoding
70744     ** is UTF-16le or UTF-16be do a translation.
70745     ** 
70746     ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
70747     */
70748     if( fg & MEM_Int ){
   0xf7f53bce <+62>:    testb  $0x4,-0x24(%ebp)
   0xf7f53bd2 <+66>:    mov    %eax,%edi
   0xf7f53bd4 <+68>:    fldl   (%esi)
   0xf7f53bd6 <+70>:    mov    0x10(%esi),%eax
   0xf7f53bd9 <+73>:    fstpl  -0x20(%ebp)
   0xf7f53bdc <+76>:    je     0xf7f53bef <sqlite3VdbeMemStringify+95>

70749       sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
   0xf7f53bde <+78>:    sub    $0xc,%esp
   0xf7f53be1 <+81>:    pushl  -0x1c(%ebp)
   0xf7f53be4 <+84>:    lea    -0x1d2b7(%ebx),%edx
   0xf7f53bea <+90>:    pushl  -0x20(%ebp)
   0xf7f53bed <+93>:    jmp    0xf7f53bfe <sqlite3VdbeMemStringify+110>

70750     }else{
70751       assert( fg & MEM_Real );
70752       sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
   0xf7f53bef <+95>:    sub    $0xc,%esp
   0xf7f53bf2 <+98>:    pushl  -0x1c(%ebp)
   0xf7f53bf5 <+101>:   lea    -0x2164a(%ebx),%edx
   0xf7f53bfb <+107>:   pushl  -0x20(%ebp)
   0xf7f53bfe <+110>:   push   %edx
   0xf7f53bff <+111>:   push   %eax
   0xf7f53c00 <+112>:   push   $0x20
   0xf7f53c02 <+114>:   call   0xf7f35af0 <sqlite3_snprintf@plt>
   0xf7f53c0a <+122>:   add    $0x20,%esp
   0xf7f53c0d <+125>:   movzbl -0x25(%ebp),%edx

70753     }
70754     pMem->n = sqlite3Strlen30(pMem->z);
   0xf7f53c07 <+119>:   mov    0x10(%esi),%eax
   0xf7f53c11 <+129>:   call   0xf7f3da71 <sqlite3Strlen30>
   0xf7f53c1e <+142>:   mov    %eax,0xc(%esi)

70755     pMem->enc = SQLITE_UTF8;
   0xf7f53c1a <+138>:   movb   $0x1,0xa(%esi)

70756     pMem->flags |= MEM_Str|MEM_Term;
   0xf7f53c21 <+145>:   movzwl 0x8(%esi),%eax

70757     if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
   0xf7f53c16 <+134>:   cmpb   $0x0,-0x26(%ebp)
   0xf7f53c25 <+149>:   je     0xf7f53c2a <sqlite3VdbeMemStringify+154>
   0xf7f53c27 <+151>:   and    $0xfffffff3,%eax
   0xf7f53c2a <+154>:   or     $0x202,%eax
   0xf7f53c2f <+159>:   mov    %ax,0x8(%esi)

70758     sqlite3VdbeChangeEncoding(pMem, enc);
   0xf7f53c33 <+163>:   mov    %esi,%eax
   0xf7f53c35 <+165>:   call   0xf7f50c73 <sqlite3VdbeChangeEncoding>

70759     return SQLITE_OK;
70760   }
   0xf7f53c3a <+170>:   lea    -0xc(%ebp),%esp
   0xf7f53c3d <+173>:   mov    %edi,%eax
   0xf7f53c3f <+175>:   pop    %ebx
   0xf7f53c40 <+176>:   pop    %esi
   0xf7f53c41 <+177>:   pop    %edi
   0xf7f53c42 <+178>:   pop    %ebp
   0xf7f53c43 <+179>:   ret    

End of assembler dump.

(gdb) continue
Continuing.

Breakpoint 2, 0xf7f53bd4 in sqlite3VdbeMemStringify (pMem=0x8064290, enc=0x1, 
    bForce=0x0) at sqlite3.c:70748
70748     if( fg & MEM_Int ){
1: $fctrl = 0x36f
2: $fstat = 0x0
3: $ftag = 0xffff
4: $fop = 0x0
5: $st0 = 0
6: $st1 = 0
7: $st2 = 0

(gdb) info reg
eax            0x0      0x0
ecx            0xf7fce000       0xf7fce000
edx            0x8065f28        0x8065f28
ebx            0xf7fce000       0xf7fce000
esp            0xffffba50       0xffffba50
ebp            0xffffba78       0xffffba78
esi            0x8064290        0x8064290
edi            0x0      0x0
eip            0xf7f53bd4       0xf7f53bd4 <sqlite3VdbeMemStringify+68>
eflags         0x202    [ IF ]
cs             0x23     0x23
ss             0x2b     0x2b
ds             0x2b     0x2b
es             0x2b     0x2b
fs             0x0      0x0
gs             0x63     0x63

(gdb) x $esi
0x8064290:      0x00000002

##
## Bug will occur at the next assembly instruction.
## but not yet this first iteration.
##

(gdb) stepi
0xf7f53bd6      70748     if( fg & MEM_Int ){
1: $fctrl = 0x36f
2: $fstat = 0x3802
3: $ftag = 0x3fff
4: $fop = 0x0
5: $st0 = 9.88131291682493088353e-324
6: $st1 = 0
7: $st2 = 0

##
## No SIGFPE happened this first time
##
## Note that if we step two instructions further, executing "fstpl 
-0x20(%ebp)"
## then the 'fstat' register changes value and actually gets the underflow bit:
##
(gdb) stepi
(gdb) stepi
1: $fctrl = 0x36f
2: $fstat = 0xb892
3: $ftag = 0x3fff
4: $fop = 0x55d
5: $st0 = 9.88131291682493088353e-324
6: $st1 = 0
7: $st2 = 0

##
## I think this phase is the trigger for the bug, on the next fldl instruction.
##

(gdb) continue
Continuing.

Breakpoint 1, sqlite3VdbeMemStringify (pMem=0x8064290, enc=0x1, bForce=0x0)
    at sqlite3.c:70725
70725   SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8
bForce){
1: $fctrl = 0x36f
2: $fstat = 0xb892
3: $ftag = 0x3fff
4: $fop = 0x55d
5: $st0 = 9.88131291682493088353e-324
6: $st1 = 0
7: $st2 = 0

(gdb) continue
Continuing.

##
## NOTE: bug will occur on next instruction.
## Note that meanwhile, fstat has changed from 0x3802 to 0xb892 and
## thus the underflow is already set, even though no SIGFPE occurred until now.
##

Breakpoint 2, 0xf7f53bd4 in sqlite3VdbeMemStringify (pMem=0x8064290, enc=0x1, 
    bForce=0x0) at sqlite3.c:70748
70748     if( fg & MEM_Int ){
1: $fctrl = 0x36f
2: $fstat = 0xb892
3: $ftag = 0x3fff
4: $fop = 0x55d
5: $st0 = 9.88131291682493088353e-324
6: $st1 = 0
7: $st2 = 0

(gdb) info reg
eax            0x0      0x0
ecx            0x0      0x0
edx            0x4      0x4
ebx            0xf7fce000       0xf7fce000
esp            0xffffba50       0xffffba50
ebp            0xffffba78       0xffffba78
esi            0x8064290        0x8064290
edi            0x0      0x0
eip            0xf7f53bd4       0xf7f53bd4 <sqlite3VdbeMemStringify+68>
eflags         0x202    [ IF ]
cs             0x23     0x23
ss             0x2b     0x2b
ds             0x2b     0x2b
es             0x2b     0x2b
fs             0x0      0x0
gs             0x63     0x63
(gdb) x $esi
0x8064290:      0x00000003

(gdb) stepi

Program received signal SIGFPE, Arithmetic exception.
0xf7f53bd4 in sqlite3VdbeMemStringify (pMem=0x8064290, enc=0x1, bForce=0x0)
    at sqlite3.c:70748
70748     if( fg & MEM_Int ){
1: $fctrl = 0x36f
2: $fstat = 0xb892
3: $ftag = 0x3fff
4: $fop = 0x55d
5: $st0 = 9.88131291682493088353e-324
6: $st1 = 0
7: $st2 = 0





[1] https://www.sqlite.org/quickstart.html

Reply via email to