Bug #21765 Illegal Instruction crash on pre-pentium when using YASSL
Submitted: 21 Aug 2006 22:45 Modified: 27 Apr 2007 4:29
Reporter: Christian Hammers (Silver Quality Contributor) (OCA) Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:5.0.24, 5.0.32 OS:Any (all)
Assigned to: Magnus Blåudd CPU Architecture:Any
Tags: Contribution, qc

[21 Aug 2006 22:45] Christian Hammers
Description:
Hello

YaSSL does not seem to work with pre-pentium CPUs due to the used assembler opcodes.

In case you care about older CPUs, this is a backtrace from the Debian bug report
at http://bugs.debian.org/383759 that was made on a 120MHz Cyrix CPU:

(gdb) r -C /dev/null -bV
Starting program: /usr/sbin/exim4 -C /dev/null -bV
[Thread debugging using libthread_db enabled]
[New Thread -1486767808 (LWP 25270)]

Program received signal SIGILL, Illegal instruction.
[Switching to Thread -1486767808 (LWP 25270)]
0xa7c4dbb0 in CpuId (input=0, output=0xafd6ab60) at integer.cpp:1025
1025    integer.cpp: No such file or directory.
        in integer.cpp
Current language:  auto; currently c++
(gdb) bt
#0  0xa7c4dbb0 in CpuId (input=0, output=0xafd6ab60) at integer.cpp:1025
#1  0xa7c4e82c in SetPentiumFunctionPointers () at integer.cpp:1092
#2  0xa7c4e8d5 in __static_initialization_and_destruction_0 (
    __initialize_p=<value optimized out>, __priority=-1344885920)
    at integer.cpp:1173
#3  0xa7c5c4e2 in __do_global_ctors_aux () from /usr/lib/libmysqlclient.so.15
#4  0xa7be7e11 in _init () from /usr/lib/libmysqlclient.so.15
#5  0xa7f63bd4 in _dl_rtld_di_serinfo () from /lib/ld-linux.so.2
#6  0xa7f63d11 in _dl_rtld_di_serinfo () from /lib/ld-linux.so.2
#7  0xa7f587ef in ?? () from /lib/ld-linux.so.2

The offending peace of code is:

  static void CpuId(word32 input, word32 *output)
  {
  #ifdef __GNUC__
    __asm__
    (
        // save ebx in case -fPIC is being used
        "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
        : "=a" (output[0]), "=D" (output[1]), "=c" (output[2]), "=d"(output[3])
        : "a" (input)
    );  
  #else   

The "cpuid" instruction was introduced with the Intel Pentium according to the
Wikipedia article.

bye,

-christian-

How to repeat:
Run on pre-pentium host.

Suggested fix:
Don't know. Maybe just document it or let MySQL exit with a sane error message somewhen during the startup? Or just fix it by returning a handcrafted CPUID-like
return value for pre-pentium CPUs?
[21 Aug 2006 22:49] Christian Hammers
Oh, can you at least confirm or deny that my analysis is right? Because the next Debian release was IIRC officially supposed to run on i486 and upwards and if MySQL does need a pentium I guess I have to note it somewhere...
thanks!
[21 Aug 2006 23:15] Christian Hammers
Some more info:

This is what Linux and the Debian bug reporter say about the Cyrix CPU on
which MySQL crashed with the SIGILL:

"Yes, it is a Pentium clone that should be equivalent to Pentium 120 MHz.
It's not really a Cyrix, but ST (the Canadian company that had a
license to produce them once upon a time).  AFAIK, it is 100% equal to
the original Cyrix."

$ cat /proc/cpuinfo
processor       : 0
vendor_id       : unknown
cpu family      : 4
model           : 0
model name      : 486
stepping        : unknown
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : no
cpuid level     : -1
wp              : yes
flags           :
bogomips        : 98.81

On the net I found further information regarding the CPUID instruction that are
maybe relevant. http://www.pcguide.com/ref/cpu/fam/g5C6x86-c.html

"Processor Identification Problems: In addition to programs that won't work with the 6x86, there are those that would work with the 6x86 if only they knew what it was. There is a special instruction called "CPUID" that is used to identify the make and model of the processor. Some programs use this to detect if the computer is running on a Pentium, and will refuse to run (or run in a reduced mode) if they don't find one. These tests were originally used by software requiring a lot of power, to distinguish between Pentiums and 486s (which the software would be too slow to run on). The 6x86 by default does not respond to the "CPUID" instruction so some programs think it is a 486."

bye,

-christian-
[31 Aug 2006 13:40] Magnus Blåudd
Christian, is there a patch?
[31 Aug 2006 13:58] Domas Mituzas
Per http://www.paradicesoftware.com/specs/cpuid/index.htm Intel's 486's have the instruction (except some early releases). It is various clones (and 386) which miss it.
[31 Aug 2006 14:19] Christian Hammers
@Magnus: I do not have a patch.
[31 Aug 2006 14:55] Magnus Blåudd
There is the options the disable X86 assembler by defining TAOCRYPT_DISABLE_X86ASM when building. But that is not somthing we will do.

You can download the source code for our SSL library yaSSL from http://www.yassl.com/ unpack it on the Pentium machine and try to run the testsuite. Then submit a patch.

What is needed is apparently a check wheter the "cpuid" instruction is supported or not. But before writing that code it's best to check that it would work by making the 'IsP4' function return false. That will install the PentiumOptimized functions.

Integer.cpp>>

static bool IsP4()
{
    word32 cpuid[4];

return false;

    CpuId(0, cpuid);
    mySTL::swap(cpuid[2], cpuid[3]);
    if (memcmp(cpuid+1, "GenuineIntel", 12) != 0)
        return false;

    CpuId(1, cpuid);
    return ((cpuid[0] >> 8) & 0xf) == 0xf;
}
[31 Aug 2006 14:56] Magnus Blåudd
There is the options the disable X86 assembler by defining TAOCRYPT_DISABLE_X86ASM when building. But that is not somthing we will do.

You can download the source code for our SSL library yaSSL from http://www.yassl.com/ unpack it on the Pentium machine and try to run the testsuite. Then submit a patch.

What is needed is apparently a check wheter the "cpuid" instruction is supported or not. But before writing that code it's best to check that it would work by making the 'IsP4' function return false. That will install the PentiumOptimized functions.

Integer.cpp>>

static bool IsP4()
{
    word32 cpuid[4];

    return false;  <<<< Add this line
}

That's the first step. :)
[5 Oct 2006 20:48] Magnus Blåudd
Will be fixed as soon as version 1.4.4 if yaSSL has been imported. See below commit mail.

Update of /cvsroot/yassl/yassl
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv29015

Modified Files:
	configure configure.in 
Log Message:
remove use of cpuid if not pentium and remove use of mmx if not mmx, mysql bug 21765

Index: configure
===================================================================
RCS file: /cvsroot/yassl/yassl/configure,v
retrieving revision 1.10
retrieving revision 1.11
diff -C2 -d -r1.10 -r1.11
*** configure	26 Sep 2006 21:09:43 -0000	1.10
--- configure	5 Oct 2006 03:18:12 -0000	1.11
***************
*** 1671,1675 ****
  # Define the identity of the package.
   PACKAGE=yassl
!  VERSION=1.4.3
  
  
--- 1671,1675 ----
  # Define the identity of the package.
   PACKAGE=yassl
!  VERSION=1.4.4
[29 Nov 2006 17:05] Magnus Blåudd
Pushed to 5.0-maint and 5.1-maint
[13 Dec 2006 19:43] Paul DuBois
Noted in 5.0.32, 5.1.15 changelogs.

yaSSL crashed on pre-Pentium Intel CPUs.
[16 Feb 2007 1:06] Christian Hammers
Hello

Please check this again, according to the original (Debian) bug reported MySQL 5.0.32 still crashes on his old PC. Below is his recent E-Mail.

bye,

-christian-

http://bugs.debian.org/410474 
Jamie Thompson <debian-bugs@jamie-thompson.co.uk>

> But can you have a look at this bug report:
>   http://bugs.mysql.com/bug.php?id=21765
> It is supposed to be fixed in 5.0.32. If your "cat /proc/cpuinfo" looks
> exactly the same then we should tell MySQL that their fix did not work.  

My /proc/cpuinfo is identical, except I get more bogomips.

I meant ot send this along but I guess I missed it off somehow.

mrlinux:~# gdb mysqladmin
GNU gdb 6.4.90-debian
...
Using host libthread_db library "/lib/tls/libthread_db.so.1".

(gdb) start
Breakpoint 1 at 0x804a2b3
Starting program: /usr/bin/mysqladmin
Failed to read a valid object file image from memory.
(no debugging symbols found)
(no debugging symbols found)
[Thread debugging using libthread_db enabled]
[New Thread -1212933920 (LWP 10253)]
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)

Program received signal SIGILL, Illegal instruction.
[Switching to Thread -1212933920 (LWP 10253)]
0xb7ea198c in TaoCrypt::HaveCpuId () from /usr/lib/libmysqlclient.so.15

Dump of assembler code for function _ZN8TaoCrypt9HaveCpuIdEv:
0xb7ea1920 <_ZN8TaoCrypt9HaveCpuIdEv+0>:        push   %ebp
0xb7ea1921 <_ZN8TaoCrypt9HaveCpuIdEv+1>:        mov    %esp,%ebp
0xb7ea1923 <_ZN8TaoCrypt9HaveCpuIdEv+3>:        sub    $0x18,%esp
0xb7ea1926 <_ZN8TaoCrypt9HaveCpuIdEv+6>:        mov    %ebx,0xfffffff8(%ebp)
0xb7ea1929 <_ZN8TaoCrypt9HaveCpuIdEv+9>:        call   0xb7e2f005 <_ZN5yaSSL3DSS7DSSImpl9SetPublicEPKhj@plt+213>
0xb7ea192e <_ZN8TaoCrypt9HaveCpuIdEv+14>:       add    $0xeb09a,%ebx
0xb7ea1934 <_ZN8TaoCrypt9HaveCpuIdEv+20>:       mov    %esi,0xfffffffc(%ebp)
0xb7ea1937 <_ZN8TaoCrypt9HaveCpuIdEv+23>:       xor    %esi,%esi
0xb7ea1939 <_ZN8TaoCrypt9HaveCpuIdEv+25>:       movl   $0x4,(%esp)
0xb7ea1940 <_ZN8TaoCrypt9HaveCpuIdEv+32>:       lea    0xfff150d8(%ebx),%eax
0xb7ea1946 <_ZN8TaoCrypt9HaveCpuIdEv+38>:       mov    %eax,0x4(%esp)
0xb7ea194a <_ZN8TaoCrypt9HaveCpuIdEv+42>:       call   0xb7e2b070 <signal@plt>
0xb7ea194f <_ZN8TaoCrypt9HaveCpuIdEv+47>:       mov    %eax,0xfffffff4(%ebp)
0xb7ea1952 <_ZN8TaoCrypt9HaveCpuIdEv+50>:       inc    %eax
0xb7ea1953 <_ZN8TaoCrypt9HaveCpuIdEv+51>:       je     0xb7ea197a <_ZN8TaoCrypt9HaveCpuIdEv+90>
0xb7ea1955 <_ZN8TaoCrypt9HaveCpuIdEv+53>:       lea    0x41998(%ebx),%eax
0xb7ea195b <_ZN8TaoCrypt9HaveCpuIdEv+59>:       mov    %eax,(%esp)
0xb7ea195e <_ZN8TaoCrypt9HaveCpuIdEv+62>:       call   0xb7e2e5b0 <_setjmp@plt>
0xb7ea1963 <_ZN8TaoCrypt9HaveCpuIdEv+67>:       test   %eax,%eax
0xb7ea1965 <_ZN8TaoCrypt9HaveCpuIdEv+69>:       je     0xb7ea1986 <_ZN8TaoCrypt9HaveCpuIdEv+102>
0xb7ea1967 <_ZN8TaoCrypt9HaveCpuIdEv+71>:       mov    0xfffffff4(%ebp),%eax
0xb7ea196a <_ZN8TaoCrypt9HaveCpuIdEv+74>:       movl   $0x4,(%esp)
0xb7ea1971 <_ZN8TaoCrypt9HaveCpuIdEv+81>:       mov    %eax,0x4(%esp)
0xb7ea1975 <_ZN8TaoCrypt9HaveCpuIdEv+85>:       call   0xb7e2b070 <signal@plt>
0xb7ea197a <_ZN8TaoCrypt9HaveCpuIdEv+90>:       mov    %esi,%eax
0xb7ea197c <_ZN8TaoCrypt9HaveCpuIdEv+92>:       mov    0xfffffff8(%ebp),%ebx
0xb7ea197f <_ZN8TaoCrypt9HaveCpuIdEv+95>:       mov    0xfffffffc(%ebp),%esi
0xb7ea1982 <_ZN8TaoCrypt9HaveCpuIdEv+98>:       mov    %ebp,%esp
0xb7ea1984 <_ZN8TaoCrypt9HaveCpuIdEv+100>:      pop    %ebp
0xb7ea1985 <_ZN8TaoCrypt9HaveCpuIdEv+101>:      ret
0xb7ea1986 <_ZN8TaoCrypt9HaveCpuIdEv+102>:      push   %ebx
0xb7ea1987 <_ZN8TaoCrypt9HaveCpuIdEv+103>:      mov    $0x0,%eax

0xb7ea198c <_ZN8TaoCrypt9HaveCpuIdEv+108>:      cpuid  <-- BANG!

0xb7ea198e <_ZN8TaoCrypt9HaveCpuIdEv+110>:      pop    %ebx
0xb7ea198f <_ZN8TaoCrypt9HaveCpuIdEv+111>:      mov    $0x1,%esi
0xb7ea1994 <_ZN8TaoCrypt9HaveCpuIdEv+116>:      jmp    0xb7ea1967 <_ZN8TaoCrypt9HaveCpuIdEv+71>
0xb7ea1996 <_ZN8TaoCrypt9HaveCpuIdEv+118>:      lea    0x0(%esi),%esi
0xb7ea1999 <_ZN8TaoCrypt9HaveCpuIdEv+121>:      lea    0x0(%edi),%edi
[16 Feb 2007 9:30] Magnus Blåudd
Sorry about that. We should double check that the latest yaSSL code really has been used and make a second attempt.
[27 Feb 2007 10:21] Magnus Blåudd
Added a comment to the debian bug but has not heard anything about attempt to download yaSSL and compile on the specific hardware or how we could get access to that machine.

We don't have any such machine.
[29 Mar 2007 13:25] Magnus Blåudd
Checked the Debian bug and apparently the problem has gone away by installing a new server. If the "old" server comes back online we could have a look at it.

Setting this bug back to closed - getting SIGILL in gdb is expected and should be caught by the SIGILL handler when run outside the debugger.
[5 Apr 2007 17:54] Lennart Sorensen
This problem still exists on my 486DX2/66.  My understanding is that all intel 486's before the DX4 lack the cpuid instruction (so anything less than 100MHz, which is probably most of the 486s ever sold).

It turns out that setjmp/longjmp don't save signal context on linux (it does on BSD) which seems to make it fail.  Using sigsetjmp and siglongjmp on the other hand with saving the signal context does work.

Here is the patch I used to make mysql work on my 486:

--- mysql-dfsg-5.0-5.0.36.ori/extra/yassl/taocrypt/src/misc.cpp 2007-02-20 12:49:38.000000000 -0500
+++ mysql-dfsg-5.0-5.0.36/extra/yassl/taocrypt/src/misc.cpp     2007-04-05 12:29:34.000000000 -0400
@@ -167,10 +167,10 @@
 #ifdef TAOCRYPT_X86ASM_AVAILABLE

 #ifndef _MSC_VER
-    static jmp_buf s_env;
+    static sigjmp_buf s_env;
     static void SigIllHandler(int)
     {
-        longjmp(s_env, 1);
+        siglongjmp(s_env, 1);
     }
 #endif

@@ -199,7 +199,7 @@
         return false;

     bool result = true;
-    if (setjmp(s_env))
+    if (sigsetjmp(s_env,1))
         result = false;
     else
         __asm__ __volatile

-------
With this patch I think you can actually close the bug legitimately.  As it is it is very much still broken.

Since it breaks any program that tries to load libmysql, for my 486 this is a critical bug, which I only encountered when I was upgrading my debian system to Etch, which no longer forces mysql to use the pure C implementation of yassl.
[5 Apr 2007 18:37] Magnus Blåudd
Thanks a lot! I just sent a mail to Todd  at yaSSL about the patch and if he approves I'll import it asap.
[5 Apr 2007 22:47] Lennart Sorensen
So it turns out that there is an official method from intel for detecting the presence of cpuid, and one of the debian developers upon seeing the setjmp/sigill handler provided it instead.  This matches how the linux kernel does the detection and does not involve any signal handlers or other improper methods, but instead simply checks if a specific cpu flag can be changed or not.  This is the new patch which I tested on my 486 where it too works just fine.  This is much cleaner and looks to be the correct way to solve this problem.

--- ./extra/yassl/taocrypt/src/misc.cpp.orig    2007-04-06 00:02:10 +0200
+++ ./extra/yassl/taocrypt/src/misc.cpp 2007-04-06 00:04:49 +0200
@@ -192,27 +192,29 @@ bool HaveCpuId()
     }
     return true;
 #else
-    typedef void (*SigHandler)(int);
+    word32 eax, ebx;

-    SigHandler oldHandler = signal(SIGILL, SigIllHandler);
-    if (oldHandler == SIG_ERR)
-        return false;
+    __asm__ __volatile
+    (
+        "pushf\n\t"
+        "pushf\n\t"
+        "pop %0\n\t"
+        "movl %0,%1\n\t"
+        "xorl $0x200000,%0\n\t"
+        "push %0\n\t"
+        "popf\n\t"
+        "pushf\n\t"
+        "pop %0\n\t"
+        "popf"
+        : "=r" (eax), "=r" (ebx)
+        :
+        : "cc"
+    );

-    bool result = true;
-    if (setjmp(s_env))
-        result = false;
-    else
-        __asm__ __volatile
-        (
-            // save ebx in case -fPIC is being used
-            "push %%ebx; mov $0, %%eax; cpuid; pop %%ebx"
-            :
-            :
-            : "%eax", "%ecx", "%edx"
-        );
+    if (eax == ebx)
+        return false;

-    signal(SIGILL, oldHandler);
-    return result;
+    return true;
 #endif
 }
[11 Apr 2007 17:40] Magnus Blåudd
Patch commited to upstream yaSSL repository. Will be imported to MySQL 5.0 and up.

Update of /cvsroot/yassl/yassl/taocrypt/src
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv2191/taocrypt/src

Modified Files:
	misc.cpp 
Log Message:
Check if cpuid instruction is available by trying to toggle the cpuid bit in EFLAGS register

Index: misc.cpp
===================================================================
RCS file: /cvsroot/yassl/yassl/taocrypt/src/misc.cpp,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -d -r1.13 -r1.14
*** misc.cpp	15 Mar 2007 02:15:16 -0000	1.13
--- misc.cpp	11 Apr 2007 17:34:46 -0000	1.14
***************
*** 201,225 ****
      return true;
  #else
!     typedef void (*SigHandler)(int);
  
!     SigHandler oldHandler = signal(SIGILL, SigIllHandler);
!     if (oldHandler == SIG_ERR)
!         return false;
  
!     bool result = true;
!     if (setjmp(s_env))
!         result = false;
!     else 
!         __asm__ __volatile
!         (
!             // save ebx in case -fPIC is being used
!             "push %%ebx; mov $0, %%eax; cpuid; pop %%ebx"
!             : 
!             :
!             : "%eax", "%ecx", "%edx" 
!         );
  
!     signal(SIGILL, oldHandler);
!     return result;
  #endif
  }
--- 201,230 ----
      return true;
  #else
!     word32 eax, ebx;
!     __asm__ __volatile
!     (
!         /* Put EFLAGS in eax and ebx */
!         "pushf;"
!         "pushf;"
!         "pop %0;"
!         "movl %0,%1;"
  
!         /* Flip the cpuid bit and store back in EFLAGS */
!         "xorl $0x200000,%0;"
!         "push %0;"
!         "popf;"
  
!         /* Read EFLAGS again */
!         "pushf;"
!         "pop %0;"
!         "popf"
!         : "=r" (eax), "=r" (ebx)
!         :
!         : "cc"
!     );
  
!     if (eax == ebx)
!         return false;
!     return true;
  #endif
  }
[11 Apr 2007 20:07] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/24321

ChangeSet@1.2447, 2007-04-11 22:07:24+02:00, msvensson@pilot.blaudden +1 -0
  Bug#21765 Illegal Instruction crash on pre-pentium when using YASSL
   - Import patch with different method of detecting if machine has
     cpuid instruction
[26 Apr 2007 11:34] Bugs System
Pushed into 5.0.42
[26 Apr 2007 11:36] Bugs System
Pushed into 5.1.18-beta
[27 Apr 2007 4:29] Paul DuBois
Noted in 5.0.42, 5.1.18 changelogs.