tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

NetBSD 8.0, OpenSSL and Geode CPU



Hi 

On Geode GX processors, ldapsearch cannot perform a TLS bind using a GCM
cipher. 

Short version for the impatient, we have a workaround:
export OPENSSL_ia32cap=~0x800000

Now the long version. It will crash like this:

TLS trace: SSL_connect:SSLv3 read server certificate request A
TLS trace: SSL_connect:SSLv3 read server done A
TLS trace: SSL_connect:SSLv3 write client certificate A
TLS trace: SSL_connect:SSLv3 write client key exchange A
TLS trace: SSL_connect:SSLv3 write change cipher spec A
Illegal instruction (core dumped) 

gdb tells me it happens in OpenSSL's libcrypto:

Program terminated with signal SIGILL, Illegal instruction.
#0  0xbb96ad50 in gcm_ghash_4bit_mmx () from ./libcrypto.so.12
(gdb) bt
#0  0xbb96ad50 in gcm_ghash_4bit_mmx () from ./libcrypto.so.12
#1  0x00000000 in ?? ()
(gdb) x/1i $pc
=> 0xbb96ad50 <gcm_ghash_4bit_mmx+1104>:        pinsrw
$0x2,(%esi,%ebx,2),%mm2

I see in src/crypto/external/bsd/openssl/dist/crypto/modes/gcm128.c
that gcm_ghash_4bit_mmx() is an optimization and we can just use
gcm_ghash_4bit_x86() instead:

#  if   defined(GHASH_ASM_X86)  /* x86 only */
#   if  defined(OPENSSL_IA32_SSE2)
    if (OPENSSL_ia32cap_P[0] & (1 << 25)) { /* check SSE bit */
#   else
    if (OPENSSL_ia32cap_P[0] & (1 << 23)) { /* check MMX bit */    
#   endif
        ctx->gmult = gcm_gmult_4bit_mmx; 
        ctx->ghash = gcm_ghash_4bit_mmx; 
    } else {            
        ctx->gmult = gcm_gmult_4bit_x86;
        ctx->ghash = gcm_ghash_4bit_x86; 
    }
#  else                 
    ctx->gmult = gcm_gmult_4bit;
    ctx->ghash = gcm_ghash_4bit;
#  endif 

And OPENSSL_ia32cap_P is populated in
src/crypto/external/bsd/openssl/dist/crypto/cryptlib.c

    if ((env = getenv("OPENSSL_ia32cap"))) {
        int off = (env[0] == '~') ? 1 : 0;
#  if defined(_WIN32)
        if (!sscanf(env + off, "%I64i", &vec))
            vec = strtoul(env + off, NULL, 0);
#  else
        if (!sscanf(env + off, "%lli", (long long *)&vec))
            vec = strtoul(env + off, NULL, 0);
#  endif
        if (off)
            vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P) & ~vec;
        else if (env[0] == ':')
            vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);

Documentation is here:
https://www.openssl.org/docs/man1.1.0/crypto/OPENSSL_ia32cap.html

MMX is bit 23, that is 0x800000

This disables MMX and lets ldapsearch bind with a GCM cipher:
export OPENSSL_ia32cap=~0x800000

Now why does it fail? OpenSSL considers pinsrw to be available with the
MMX feature, and Geode GX1 has it:

# cpuctl identify 0
cpu0: highest basic info 00000002
cpu0: highest extended info 80000005
cpu0: "Geode(TM) Integrated Processor by National Semi"
cpu0: National Semiconductor Geode GX1 (586-class)
cpu0: family 0x5 model 0x4 stepping 0 (id 0x540)
cpu0: features 0x808131<FPU,TSC,MSR,CX8,CMOV,MMX>
cpu0: ITLB 1 4KB entries 112-way
cpu0: Initial APIC ID 0

But looking up pinsrw on a search engine, all pages I find suggest is
should compe with SSE. Here is an example:
http://www.felixcloutier.com/x86/PINSRW.html

So is this a bug in OpenSSL? I would expect that if OPENSSL_IA32_SSE2 is
undefined, it would wrongly use MMX to decice when pinsrw is used, but I
see it is defined in
src/crypto/external/bsd/openssl/lib/libcrypto/arch/i386/Makefile

Is it somehow unused? And shouldn't OpenSSL abstain from using the MMX
version wih it finds MMX without SSE?

-- 
Emmanuel Dreyfus
http://hcpnet.free.fr/pubz
manu%netbsd.org@localhost


Home | Main Index | Thread Index | Old Index