SIGFPE with gcc 7.3.0 in sqlite3: fldl instruction underflow

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

SIGFPE with gcc 7.3.0 in sqlite3: fldl instruction underflow

Thomas De Schampheleire
Hello,

I have a problem with a program that links with sqlite3 and executes
an SQL statement. The program is executed under Qemu, with an 32-bit
x86 emulated machine, and Qemu is running on a 64-bit x86 host.

Originally, the program worked fine, when it was compiled with a
toolchain composed out of gcc 4.9.2, glibc 2.21, binutils 2.24.

Now I am rebuilding the system with a toolchain composed out of gcc
7.3, glibc 2.27, binutils 2.30. In this case, the program receives a
SIGFPE (Arithmetic Error) in Sqlite code. Analysis of the coredump
reveals that the error occurs on an 'fldl' instruction (Floating-point
Load Long) and that the exact cause of the error is an underflow.

The problem does not appear when Qemu is started without KVM support.
In this case, floating point handling is covered by Qemu itself. The
problem does occur when passing '-enable-kvm' which causes the host
machine to execute most instructions.

However, it is unclear to me why gcc is emitting an 'fldl' instruction
here (which wasn't present with the old toolchain) and why it is
causing an underflow. The part of the code being executed is not
handling a floating-point value.
There is a print of a real a bit further down, which is conditional on
a flag indicating that the union indeed holds a real value. Could this
'fldl' instruction be part of that code, moved upwards? In that case,
who is at fault here? Because the loading of the real should only
happen when we know that it is indeed a real, and not an integer.

I tried reproducing the problem with a simple program from the Sqlite
'Getting started' guide, running the same SQL query, and linking to
the exact same binary copy of the sqlite shared library file, but this
worked fine.

The source code in question is:

SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
  int fg = pMem->flags;
  const int nByte = 32;

  assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
  assert( !(fg&MEM_ZerAny help is much appreciated.uio) );
  assert( !(fg&(MEM_Str|MEM_Blob)) );
  assert( fg&(MEM_Int|MEM_Real) );
  assert( (pMem->flags&MEM_RowSet)==0 );
  assert( EIGHT_BYTE_ALIGNMENT(pMem) );


  if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
    pMem->enc = 0;
    return SQLITE_NOMEM_BKPT;
  }

  /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
  ** string representation of the value. Then, if the required encoding
  ** is UTF-16le or UTF-16be do a translation.
  **
  ** FIX ME: It would be better if sqlite3_snprintf() could do UTF-16.
  */
  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);
  }
  pMem->n = sqlite3Strlen30(pMem->z);
  pMem->enc = SQLITE_UTF8;
  pMem->flags |= MEM_Str|MEM_Term;
  if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
  sqlite3VdbeChangeEncoding(pMem, enc);
  return SQLITE_OK;
}


The disassembly with the new toolchain (gcc 7.3.0) is below.
I marked with leading # signs the part of the disassembly I do not
understand and where the problem occurs.
When the problem occurs, the value pointed to in %esi is '3'.

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>  --> exit

#   1c209:    89 c7                    mov    %eax,%edi
#   1c20b:    dd 06                    fldl   (%esi)
#   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



while the disassembly with the old toolchain is:

0001cdff <sqlite3VdbeMemStringify>:
   1cdff:    55                       push   %ebp
   1ce00:    89 e5                    mov    %esp,%ebp
   1ce02:    57                       push   %edi
   1ce03:    56                       push   %esi
   1ce04:    53                       push   %ebx
   1ce05:    83 ec 1c                 sub    $0x1c,%esp
   1ce08:    e8 fc ff ff ff           call   1ce09 <sqlite3VdbeMemStringify+0xa>
   1ce0d:    81 c3 02 00 00 00        add    $0x2,%ebx
   1ce13:    89 c7                    mov    %eax,%edi
   1ce15:    89 55 e0                 mov    %edx,-0x20(%ebp)
   1ce18:    8b 45 08                 mov    0x8(%ebp),%eax
   1ce1b:    89 45 dc                 mov    %eax,-0x24(%ebp)
   1ce1e:    8b 47 08                 mov    0x8(%edi),%eax
   1ce21:    66 89 45 e6              mov    %ax,-0x1a(%ebp)
   1ce25:    ba 20 00 00 00           mov    $0x20,%edx

   1ce2a:    89 f8                    mov    %edi,%eax
   1ce2c:    e8 be b8 ff ff           call   186ef
<sqlite3VdbeMemClearAndResize>
   1ce31:    89 c6                    mov    %eax,%esi
   1ce33:    85 c0                    test   %eax,%eax
   1ce35:    74 0b                    je     1ce42
<sqlite3VdbeMemStringify+0x43>
   1ce37:    c6 47 0a 00              movb   $0x0,0xa(%edi)
   1ce3b:    be 07 00 00 00           mov    $0x7,%esi
   1ce40:    eb 60                    jmp    1cea2
<sqlite3VdbeMemStringify+0xa3>

   1ce42:    f6 45 e6 04              testb  $0x4,-0x1a(%ebp)
              (+0x43)
   1ce46:    74 10                    je     1ce58
<sqlite3VdbeMemStringify+0x59>
   1ce48:    83 ec 0c                 sub    $0xc,%esp
   1ce4b:    ff 77 04                 pushl  0x4(%edi)
   1ce4e:    ff 37                    pushl  (%edi)
   1ce50:    8d 83 00 00 00 00        lea    0x0(%ebx),%eax
   1ce56:    eb 0e                    jmp    1ce66
<sqlite3VdbeMemStringify+0x67>
   1ce58:    83 ec 0c                 sub    $0xc,%esp
   1ce5b:    ff 77 04                 pushl  0x4(%edi)
   1ce5e:    ff 37                    pushl  (%edi)
   1ce60:    8d 83 00 00 00 00        lea    0x0(%ebx),%eax
   1ce66:    50                       push   %eax
   1ce67:    ff 77 10                 pushl  0x10(%edi)
   1ce6a:    6a 20                    push   $0x20
   1ce6c:    e8 fc ff ff ff           call   1ce6d
<sqlite3VdbeMemStringify+0x6e>
   1ce71:    83 c4 20                 add    $0x20,%esp
   1ce74:    8b 47 10                 mov    0x10(%edi),%eax
   1ce77:    e8 e3 a2 fe ff           call   715f <sqlite3Strlen30>
   1ce7c:    89 47 0c                 mov    %eax,0xc(%edi)
   1ce7f:    c6 47 0a 01              movb   $0x1,0xa(%edi)
   1ce83:    8b 47 08                 mov    0x8(%edi),%eax
   1ce86:    80 7d dc 00              cmpb   $0x0,-0x24(%ebp)
   1ce8a:    74 03                    je     1ce8f
<sqlite3VdbeMemStringify+0x90>
   1ce8c:    83 e0 f3                 and    $0xfffffff3,%eax
   1ce8f:    66 0d 02 02              or     $0x202,%ax
   1ce93:    66 89 47 08              mov    %ax,0x8(%edi)
   1ce97:    0f b6 55 e0              movzbl -0x20(%ebp),%edx
   1ce9b:    89 f8                    mov    %edi,%eax
   1ce9d:    e8 eb ce ff ff           call   19d8d <sqlite3VdbeChangeEncoding>
   1cea2:    89 f0                    mov    %esi,%eax
            + 0xa3
   1cea4:    8d 65 f4                 lea    -0xc(%ebp),%esp
   1cea7:    5b                       pop    %ebx
   1cea8:    5e                       pop    %esi
   1cea9:    5f                       pop    %edi
   1ceaa:    5d                       pop    %ebp
   1ceab:    c3                       ret


The backtrace from the coredump analysis is:

(gdb) bt
#0  0xb7513e3d in sqlite3VdbeMemStringify (pMem=0xb6b1a900,
enc=<optimized out>, bForce=<optimized out>) at sqlite3.c:70739
#1  0xb7513f36 in valueToText (pVal=0xb6b1a900, enc=enc@entry=0x1) at
sqlite3.c:71501
#2  0xb751405e in sqlite3ValueText (pVal=pVal@entry=0xb6b1a900,
enc=enc@entry=0x1) at sqlite3.c:71534
#3  0xb7514074 in sqlite3_value_text (pVal=0xb6b1a900) at sqlite3.c:77123
#4  0xb75143dd in sqlite3_column_text (pStmt=0xb6b1a978, i=0x1) at
sqlite3.c:77989
#5  0xb754a6a0 in sqlite3_exec (db=0xb6b02f78, zSql=<optimized out>,
xCallback=0xb7558033 <sqlite3InitCallback>, pArg=0xb6c74c28,
pzErrMsg=0x0) at sqlite3.c:112209
#6  0xb754697d in sqlite3VdbeExec (p=p@entry=0xb6b23508) at sqlite3.c:84727
#7  0xb7549bd4 in sqlite3Step (p=0xb6b23508) at sqlite3.c:77535
#8  sqlite3_step (pStmt=0xb6b23508) at sqlite3.c:12062
#9  0xb754a606 in sqlite3_exec (db=0xb6b02f78, zSql=<optimized out>,
zSql@entry=0xb6b23200 "CREATE TABLE
InterfaceTable(gem_port_obj_index_list varchar ,obj_index unsigned int
,ont_and_slot unsigned smallint ,pbit_tc_mapping unsigned int ,port
unsigned tinyint ,sub_itf_obj_index_list varchar ,uni unsigned tinyint
,CONSTRAINT KEY_InterfaceTable PRIMARY KEY (obj_index))",
xCallback=xCallback@entry=0x0, pArg=pArg@entry=0x0,
pzErrMsg=pzErrMsg@entry=0xb6c74e6c) at sqlite3.c:112187
[...]
#17 0xb76275eb in start_thread (arg=0xb6c75b40) at pthread_create.c:463
#18 0xb6fb6e96 in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:108

Any help is much appreciated.

Best regards,
Thomas
Reply | Threaded
Open this post in threaded view
|

Re: SIGFPE with gcc 7.3.0 in sqlite3: fldl instruction underflow

Thomas De Schampheleire
Hello,

El mié., 21 nov. 2018 a las 15:46, Thomas De Schampheleire
(<[hidden email]>) escribió:

>
> Hello,
>
> I have a problem with a program that links with sqlite3 and executes
> an SQL statement. The program is executed under Qemu, with an 32-bit
> x86 emulated machine, and Qemu is running on a 64-bit x86 host.
>
> Originally, the program worked fine, when it was compiled with a
> toolchain composed out of gcc 4.9.2, glibc 2.21, binutils 2.24.
>
> Now I am rebuilding the system with a toolchain composed out of gcc
> 7.3, glibc 2.27, binutils 2.30. In this case, the program receives a
> SIGFPE (Arithmetic Error) in Sqlite code. Analysis of the coredump
> reveals that the error occurs on an 'fldl' instruction (Floating-point
> Load Long) and that the exact cause of the error is an underflow.
>
> The problem does not appear when Qemu is started without KVM support.
> In this case, floating point handling is covered by Qemu itself. The
> problem does occur when passing '-enable-kvm' which causes the host
> machine to execute most instructions.
>
> However, it is unclear to me why gcc is emitting an 'fldl' instruction
> here (which wasn't present with the old toolchain) and why it is
> causing an underflow. The part of the code being executed is not
> handling a floating-point value.
> There is a print of a real a bit further down, which is conditional on
> a flag indicating that the union indeed holds a real value. Could this
> 'fldl' instruction be part of that code, moved upwards? In that case,
> who is at fault here? Because the loading of the real should only
> happen when we know that it is indeed a real, and not an integer.

It seems I was right here: gcc optimization caused the floating-point
load to move upwards, before the checking if the union value is
actually of floating-point type. This optimization can cause a
floating-point exception.

If I add a compiler barrier between the checking of the flag and the
reading of the value, then the problem is gone. No floating-point
exception occurs (because the value is actually of type int).

Modified code is:

  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);
  }

My question now becomes: is gcc allowed to optimize here (without the barrier)?
Is gcc allowed to pre-load the floating point value pMem->u.r if it
does not know yet that the value in the union is a floating-point
value, knowing that a load of an invalid (integer) value could cause
floating-point exceptions like underflow?

Thanks,
Thomas
Reply | Threaded
Open this post in threaded view
|

Re: SIGFPE with gcc 7.3.0 in sqlite3: fldl instruction underflow

Segher Boessenkool
Hi,

On Mon, Nov 26, 2018 at 12:27:38PM +0100, Thomas De Schampheleire wrote:

> El mié., 21 nov. 2018 a las 15:46, Thomas De Schampheleire
> (<[hidden email]>) escribió:
> Modified code is:
>
>   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);
>   }
>
> My question now becomes: is gcc allowed to optimize here (without the barrier)?
> Is gcc allowed to pre-load the floating point value pMem->u.r if it
> does not know yet that the value in the union is a floating-point
> value, knowing that a load of an invalid (integer) value could cause
> floating-point exceptions like underflow?

It depends on many things.  We need to have some full, compilable source
code; know which target exactly; exact compiler flags used; what you saw
happen and why you think that is wrong.  And then file it on
https://gcc.gnu.org/bugzilla please.


Segher
Reply | Threaded
Open this post in threaded view
|

Re: SIGFPE with gcc 7.3.0 in sqlite3: fldl instruction underflow

Thomas De Schampheleire
El lun., 26 nov. 2018 a las 15:50, Segher Boessenkool
(<[hidden email]>) escribió:

>
> Hi,
>
> On Mon, Nov 26, 2018 at 12:27:38PM +0100, Thomas De Schampheleire wrote:
> > El mié., 21 nov. 2018 a las 15:46, Thomas De Schampheleire
> > (<[hidden email]>) escribió:
> > Modified code is:
> >
> >   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);
> >   }
> >
> > My question now becomes: is gcc allowed to optimize here (without the barrier)?
> > Is gcc allowed to pre-load the floating point value pMem->u.r if it
> > does not know yet that the value in the union is a floating-point
> > value, knowing that a load of an invalid (integer) value could cause
> > floating-point exceptions like underflow?
>
> It depends on many things.  We need to have some full, compilable source
> code; know which target exactly; exact compiler flags used; what you saw
> happen and why you think that is wrong.  And then file it on
> https://gcc.gnu.org/bugzilla please.
>

Thanks, I posted at:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88240