[Bug tree-optimization/88240] Potential optimization bug: invalid pre-load of floating-point value could cause SIGFPE-underflow if value is integer
patrickdepinguin at gmail dot com
gcc-bugzilla@gcc.gnu.org
Thu Dec 6 20:11:00 GMT 2018
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
More information about the Gcc-bugs
mailing list