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)

Reply via email to