https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88240
Bug ID: 88240 Summary: Potential optimization bug: invalid pre-load of floating-point value could cause SIGFPE-underflow if value is integer Product: gcc Version: 7.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: other Assignee: unassigned at gcc dot gnu.org Reporter: patrickdepinguin at gmail dot com Target Milestone: --- Created attachment 45111 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=45111&action=edit gzipped source file, part 1 gcc 7.3.0 optimizes below code in a way that may cause a floating-point underflow (SIGFPE with underflow flag) on x86. The underflow occurs on an 'fldl' instruction. It seems that no optimization should cause such behavior and as such it would be categorized as a compiler bug, but I may be wrong here. The code was originally compiled with gcc 4.9.2, no problem was seen in that case. This underflow behavior was observed when running the code in question in qemu with KVM enabled. This means that most instructions will be executed by the host machine, rather than being emulated by qemu itself. If KVM is not enabled, and thus qemu emulates the instructions, the problem is not seen. The emulated machine in qemu is a 32-bit PC, and qemu itself is running on a 64-bit x86_64 host. The full source file is in attachment (sqlite3.c). Because the file is very big, and due to attachment size restrictions, I had to gzip and split the file. Please cat the pieces together, then gunzip. The md5sum of the source file is: 4ff9898461684927dff175c06f0df24b . The source snippet in question is (inside function sqlite3VdbeMemStringify): if( fg & MEM_Int ){ sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i); }else{ assert( fg & MEM_Real ); sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r); } Here, fg is a flag value, pMem->u is a union with possible types integer (u.i) and real (u.r). The problem is that the loading of the real value is already done before the result of the if-check is known. If the union 'u' happens to be an integer, then the loading of the real can cause an underflow. The file is compiled with following command-line: /home/tdescham/repo/isam/buildroot-x86/output/host/opt/ext-toolchain/bin/i686-pc-linux-gnu-gcc --sysroot /home/tdescham/repo/isam/buildroot-x86/output/host/i686-buildroot-linux-gnu/sysroot -march=prescott -DPACKAGE_NAME="sqlite" -DPACKAGE_TARNAME="sqlite" -DPACKAGE_VERSION="3.21.0" -DPACKAGE_STRING="sqlite 3.21.0" -DPACKAGE_BUGREPORT="http://www.sqlite.org" -DPACKAGE_URL="" -DPACKAGE="sqlite" -DVERSION="3.21.0" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=".libs/" -DHAVE_FDATASYNC=1 -DHAVE_USLEEP=1 -DHAVE_LOCALTIME_R=1 -DHAVE_GMTIME_R=1 -DHAVE_DECL_STRERROR_R=1 -DHAVE_STRERROR_R=1 -DHAVE_POSIX_FALLOCATE=1 -I. -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_REENTRANT=1 -DSQLITE_THREADSAFE=1 -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_RTREE -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os -g2 -c sqlite3.c -fPIC -DPIC -o .libs/sqlite3.o Disassembly of the complete problematic function is below. I marked with '#' the lines that are the problem (offsets 1c209 - 1c20d). 0001c1cb <sqlite3VdbeMemStringify>: SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ 1c1cb: 55 push %ebp 1c1cc: 89 e5 mov %esp,%ebp 1c1ce: 57 push %edi 1c1cf: 56 push %esi 1c1d0: 53 push %ebx 1c1d1: 83 ec 2c sub $0x2c,%esp 1c1d4: e8 fc ff ff ff call 1c1d5 <sqlite3VdbeMemStringify+0xa> 1c1d9: 81 c3 02 00 00 00 add $0x2,%ebx 1c1df: 89 c6 mov %eax,%esi 1c1e1: 89 55 d8 mov %edx,-0x28(%ebp) 1c1e4: 89 4d d4 mov %ecx,-0x2c(%ebp) int fg = pMem->flags; 1c1e7: 8b 40 08 mov 0x8(%eax),%eax 1c1ea: 66 89 45 de mov %ax,-0x22(%ebp) if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){ 1c1ee: ba 20 00 00 00 mov $0x20,%edx 1c1f3: 89 f0 mov %esi,%eax 1c1f5: e8 5d bb ff ff call 17d57 <sqlite3VdbeMemClearAndResize> 1c1fa: 85 c0 test %eax,%eax 1c1fc: 74 0b je 1c209 <sqlite3VdbeMemStringify+0x3e> pMem->enc = 0; 1c1fe: c6 46 0a 00 movb $0x0,0xa(%esi) return SQLITE_NOMEM_BKPT; 1c202: bf 07 00 00 00 mov $0x7,%edi 1c207: eb 72 jmp 1c27b <sqlite3VdbeMemStringify+0xb0> 1c209: 89 c7 mov %eax,%edi 1c20b: dd 06 fldl (%esi) # PROBLEMATIC LOAD 1c20d: dd 5d e0 fstpl -0x20(%ebp) # 1c210: 8b 46 10 mov 0x10(%esi),%eax # if( fg & MEM_Int ){ 1c213: f6 45 de 04 testb $0x4,-0x22(%ebp) 1c217: 74 11 je 1c22a <sqlite3VdbeMemStringify+0x5f> sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i); 1c219: 83 ec 0c sub $0xc,%esp 1c21c: ff 75 e4 pushl -0x1c(%ebp) 1c21f: ff 75 e0 pushl -0x20(%ebp) 1c222: 8d 93 00 00 00 00 lea 0x0(%ebx),%edx 1c228: eb 0f jmp 1c239 <sqlite3VdbeMemStringify+0x6e> sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r); 1c22a: 83 ec 0c sub $0xc,%esp 1c22d: ff 75 e4 pushl -0x1c(%ebp) 1c230: ff 75 e0 pushl -0x20(%ebp) 1c233: 8d 93 00 00 00 00 lea 0x0(%ebx),%edx 1c239: 52 push %edx 1c23a: 50 push %eax 1c23b: 6a 20 push $0x20 1c23d: e8 fc ff ff ff call 1c23e <sqlite3VdbeMemStringify+0x73> 1c242: 83 c4 20 add $0x20,%esp 1c245: 0f b6 55 d8 movzbl -0x28(%ebp),%edx pMem->n = sqlite3Strlen30(pMem->z); 1c249: 8b 46 10 mov 0x10(%esi),%eax 1c24c: e8 a8 9d fe ff call 5ff9 <sqlite3Strlen30> 1c251: 89 46 0c mov %eax,0xc(%esi) pMem->enc = SQLITE_UTF8; 1c254: c6 46 0a 01 movb $0x1,0xa(%esi) pMem->flags |= MEM_Str|MEM_Term; 1c258: 8b 46 08 mov 0x8(%esi),%eax 1c25b: 89 c1 mov %eax,%ecx 1c25d: 83 e1 f3 and $0xfffffff3,%ecx 1c260: 66 81 c9 02 02 or $0x202,%cx 1c265: 66 0d 02 02 or $0x202,%ax 1c269: 80 7d d4 00 cmpb $0x0,-0x2c(%ebp) 1c26d: 0f 45 c1 cmovne %ecx,%eax 1c270: 66 89 46 08 mov %ax,0x8(%esi) sqlite3VdbeChangeEncoding(pMem, enc); 1c274: 89 f0 mov %esi,%eax 1c276: e8 5f d0 ff ff call 192da <sqlite3VdbeChangeEncoding> } 1c27b: 89 f8 mov %edi,%eax 1c27d: 8d 65 f4 lea -0xc(%ebp),%esp 1c280: 5b pop %ebx 1c281: 5e pop %esi 1c282: 5f pop %edi 1c283: 5d pop %ebp 1c284: c3 ret If I add a barrier to the code, as follows: if( fg & MEM_Int ){ sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i); }else{ assert( fg & MEM_Real ); asm volatile("" ::: "memory"); sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r); } then the problem is gone. The disassembly becomes: 0001c1cb <sqlite3VdbeMemStringify>: SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){ 1c1cb: 55 push %ebp 1c1cc: 89 e5 mov %esp,%ebp 1c1ce: 57 push %edi 1c1cf: 56 push %esi 1c1d0: 53 push %ebx 1c1d1: 83 ec 1c sub $0x1c,%esp 1c1d4: e8 fc ff ff ff call 1c1d5 <sqlite3VdbeMemStringify+0xa> 1c1d9: 81 c3 02 00 00 00 add $0x2,%ebx 1c1df: 89 c6 mov %eax,%esi 1c1e1: 89 55 e0 mov %edx,-0x20(%ebp) 1c1e4: 89 4d dc mov %ecx,-0x24(%ebp) int fg = pMem->flags; 1c1e7: 8b 40 08 mov 0x8(%eax),%eax 1c1ea: 66 89 45 e6 mov %ax,-0x1a(%ebp) if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){ 1c1ee: ba 20 00 00 00 mov $0x20,%edx 1c1f3: 89 f0 mov %esi,%eax 1c1f5: e8 5d bb ff ff call 17d57 <sqlite3VdbeMemClearAndResize> 1c1fa: 85 c0 test %eax,%eax 1c1fc: 74 0b je 1c209 <sqlite3VdbeMemStringify+0x3e> pMem->enc = 0; 1c1fe: c6 46 0a 00 movb $0x0,0xa(%esi) return SQLITE_NOMEM_BKPT; 1c202: bf 07 00 00 00 mov $0x7,%edi 1c207: eb 6a jmp 1c273 <sqlite3VdbeMemStringify+0xa8> 1c209: 89 c7 mov %eax,%edi if( fg & MEM_Int ){ 1c20b: f6 45 e6 04 testb $0x4,-0x1a(%ebp) 1c20f: 74 10 je 1c221 <sqlite3VdbeMemStringify+0x56> sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i); 1c211: 83 ec 0c sub $0xc,%esp 1c214: ff 76 04 pushl 0x4(%esi) 1c217: ff 36 pushl (%esi) 1c219: 8d 83 00 00 00 00 lea 0x0(%ebx),%eax 1c21f: eb 0e jmp 1c22f <sqlite3VdbeMemStringify+0x64> sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r); 1c221: 83 ec 0c sub $0xc,%esp 1c224: ff 76 04 pushl 0x4(%esi) 1c227: ff 36 pushl (%esi) 1c229: 8d 83 00 00 00 00 lea 0x0(%ebx),%eax 1c22f: 50 push %eax 1c230: ff 76 10 pushl 0x10(%esi) 1c233: 6a 20 push $0x20 1c235: e8 fc ff ff ff call 1c236 <sqlite3VdbeMemStringify+0x6b> 1c23a: 83 c4 20 add $0x20,%esp 1c23d: 0f b6 55 e0 movzbl -0x20(%ebp),%edx pMem->n = sqlite3Strlen30(pMem->z); 1c241: 8b 46 10 mov 0x10(%esi),%eax 1c244: e8 b0 9d fe ff call 5ff9 <sqlite3Strlen30> 1c249: 89 46 0c mov %eax,0xc(%esi) pMem->enc = SQLITE_UTF8; 1c24c: c6 46 0a 01 movb $0x1,0xa(%esi) pMem->flags |= MEM_Str|MEM_Term; 1c250: 8b 46 08 mov 0x8(%esi),%eax 1c253: 89 c1 mov %eax,%ecx 1c255: 83 e1 f3 and $0xfffffff3,%ecx 1c258: 66 81 c9 02 02 or $0x202,%cx 1c25d: 66 0d 02 02 or $0x202,%ax 1c261: 80 7d dc 00 cmpb $0x0,-0x24(%ebp) 1c265: 0f 45 c1 cmovne %ecx,%eax 1c268: 66 89 46 08 mov %ax,0x8(%esi) sqlite3VdbeChangeEncoding(pMem, enc); 1c26c: 89 f0 mov %esi,%eax 1c26e: e8 67 d0 ff ff call 192da <sqlite3VdbeChangeEncoding> } 1c273: 89 f8 mov %edi,%eax 1c275: 8d 65 f4 lea -0xc(%ebp),%esp 1c278: 5b pop %ebx 1c279: 5e pop %esi 1c27a: 5f pop %edi 1c27b: 5d pop %ebp 1c27c: c3 ret gcc is built via crosstool-ng, output of '-v': Using built-in specs. COLLECT_GCC=/home/tdescham/repo/isam/buildroot-x86-2/output/host/opt/ext-toolchain/bin/i686-pc-linux-gnu-gcc COLLECT_LTO_WRAPPER=/home/tdescham/repo/isam/buildroot-x86-2/output/host/opt/ext-toolchain/bin/../libexec/gcc/i686-pc-linux-gnu/7.3.0/lto-wrapper Target: i686-pc-linux-gnu Configured with: /repo/tdescham/reborn/ctng/crosstool-ng-bis/.build/i686-pc-linux-gnu/src/gcc/configure --build=x86_64-build_pc-linux-gnu --host=x86_64-build_pc-linux-gnu --target=i686-pc-linux-gnu --prefix=/repo/tdescham/reborn/ctng/crosstool-ng-bis/targets/i686-pc-linux-gnu --with-sysroot=/repo/tdescham/reborn/ctng/crosstool-ng-bis/targets/i686-pc-linux-gnu/i686-pc-linux-gnu/sysroot --enable-languages=c,c++ --with-arch=i686 --with-pkgversion='crosstool-NG crosstool-ng-1.23.0-321-g7c342267' --enable-__cxa_atexit --disable-libmudflap --disable-libgomp --disable-libssp --disable-libquadmath --disable-libquadmath-support --disable-libsanitizer --enable-libmpx --with-gmp=/repo/tdescham/reborn/ctng/crosstool-ng-bis/.build/i686-pc-linux-gnu/buildtools --with-mpfr=/repo/tdescham/reborn/ctng/crosstool-ng-bis/.build/i686-pc-linux-gnu/buildtools --with-mpc=/repo/tdescham/reborn/ctng/crosstool-ng-bis/.build/i686-pc-linux-gnu/buildtools --with-isl=/repo/tdescham/reborn/ctng/crosstool-ng-bis/.build/i686-pc-linux-gnu/buildtools --disable-lto --with-host-libstdcxx='-static-libgcc -Wl,-Bstatic,-lstdc++ -lm' --enable-threads=posix --enable-target-optspace --disable-plugin --disable-nls --disable-multilib --with-local-prefix=/repo/tdescham/reborn/ctng/crosstool-ng-bis/targets/i686-pc-linux-gnu/i686-pc-linux-gnu/sysroot --enable-long-long Thread model: posix gcc version 7.3.0 (crosstool-NG crosstool-ng-1.23.0-321-g7c342267)