Skip to content

Commit

Permalink
Refactor CPUID code
Browse files Browse the repository at this point in the history
We were using CPUID coded in several modules, but it was unclear how
it actually got there, and could fail randomly.

To remedy that, this change separates the CPUID C code from the rest
of cryptlib.c, and ensures the right modules get both that and the
assembler sources explicitly.

Fixes #11281

Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from #14755)
  • Loading branch information
levitte committed Apr 2, 2021
1 parent baf0279 commit ef83daf
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 211 deletions.
44 changes: 30 additions & 14 deletions crypto/build.info
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,31 @@ IF[{- !$disabled{asm} && $config{processor} ne '386' -}]
ENDIF
ENDIF

# CPUID support. We need to add that explicitly in every shared library and
# provider module that uses it. ctype.c is included here because the CPUID
# uses functions from there to parse magic environment variables.
$CPUID_COMMON=$CPUIDASM cpuid.c ctype.c
INCLUDE[cpuid.o]=..

SOURCE[../libcrypto]=$CPUID_COMMON
DEFINE[../libcrypto]=$CPUIDDEF
SOURCE[../providers/fips]=$CPUID_COMMON
DEFINE[../providers/fips]=$CPUIDDEF
# We only need to include the CPUID stuff in the legacy provider when it's a
# separate module and it's dynamically linked with libcrypto. Otherwise, it
# already gets everything that the static libcrypto.a has, and doesn't need it
# added again.
IF[{- !$disabled{module} && !$disabled{shared} -}]
SOURCE[../providers/liblegacy.a]=$CPUID_COMMON
DEFINE[../providers/liblegacy.a]=$CPUIDDEF
ENDIF

# Implementations are now spread across several libraries, so the CPUID define
# need to be applied to all affected libraries and modules.
DEFINE[../providers/libfips.a]=$CPUIDDEF
DEFINE[../providers/libimplementations.a]=$CPUIDDEF
DEFINE[../providers/libcommon.a]=$CPUIDDEF

# The Core
$CORE_COMMON=provider_core.c provider_predefined.c \
core_fetch.c core_algorithm.c core_namemap.c self_test_core.c
Expand All @@ -69,28 +94,19 @@ SOURCE[../providers/libfips.a]=$CORE_COMMON
# Central utilities
$UTIL_COMMON=\
cryptlib.c params.c params_from_text.c bsearch.c ex_data.c o_str.c \
ctype.c threads_pthread.c threads_win.c threads_none.c initthread.c \
context.c sparse_array.c asn1_dsa.c packet.c param_build.c $CPUIDASM \
threads_pthread.c threads_win.c threads_none.c initthread.c \
context.c sparse_array.c asn1_dsa.c packet.c param_build.c \
param_build_set.c der_writer.c passphrase.c threads_lib.c
$UTIL_DEFINE=$CPUIDDEF

SOURCE[../libcrypto]=$UTIL_COMMON \
mem.c mem_sec.c \
cversion.c info.c cpt_err.c ebcdic.c uid.c o_time.c o_dir.c \
o_fopen.c getenv.c o_init.c init.c trace.c provider.c \
punycode.c \
$UPLINKSRC
punycode.c
SOURCE[../providers/libfips.a]=$UTIL_COMMON
SOURCE[../providers/liblegacy.a]=cryptlib.c $CPUIDASM ctype.c

# Implementations are now spread across several libraries, so the defines
# need to be applied to all affected libraries and modules.
DEFINE[../libcrypto]=$UTIL_DEFINE $UPLINKDEF
DEFINE[../providers/libfips.a]=$UTIL_DEFINE
DEFINE[../providers/fips]=$UTIL_DEFINE
DEFINE[../providers/libimplementations.a]=$UTIL_DEFINE
DEFINE[../providers/liblegacy.a]=$UTIL_DEFINE
DEFINE[../providers/libcommon.a]=$UTIL_DEFINE
SOURCE[../libcrypto]=$UPLINKSRC
DEFINE[../libcrypto]=$UPLINKDEF

DEPEND[info.o]=buildinf.h
DEPEND[cversion.o]=buildinf.h
Expand Down
214 changes: 214 additions & 0 deletions crypto/cpuid.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*
* Copyright 1998-2021 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/

#include "e_os.h"
#include "crypto/cryptlib.h"

#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || \
defined(_M_AMD64) || defined(_M_X64)

extern unsigned int OPENSSL_ia32cap_P[4];

# if defined(OPENSSL_CPUID_OBJ)

/*
* Purpose of these minimalistic and character-type-agnostic subroutines
* is to break dependency on MSVCRT (on Windows) and locale. This makes
* OPENSSL_cpuid_setup safe to use as "constructor". "Character-type-
* agnostic" means that they work with either wide or 8-bit characters,
* exploiting the fact that first 127 characters can be simply casted
* between the sets, while the rest would be simply rejected by ossl_is*
* subroutines.
*/
# ifdef _WIN32
typedef WCHAR variant_char;

static variant_char *ossl_getenv(const char *name)
{
/*
* Since we pull only one environment variable, it's simpler to
* to just ignore |name| and use equivalent wide-char L-literal.
* As well as to ignore excessively long values...
*/
static WCHAR value[48];
DWORD len = GetEnvironmentVariableW(L"OPENSSL_ia32cap", value, 48);

return (len > 0 && len < 48) ? value : NULL;
}
# else
typedef char variant_char;
# define ossl_getenv getenv
# endif

# include "crypto/ctype.h"

static int todigit(variant_char c)
{
if (ossl_isdigit(c))
return c - '0';
else if (ossl_isxdigit(c))
return ossl_tolower(c) - 'a' + 10;

/* return largest base value to make caller terminate the loop */
return 16;
}

static uint64_t ossl_strtouint64(const variant_char *str)
{
uint64_t ret = 0;
unsigned int digit, base = 10;

if (*str == '0') {
base = 8, str++;
if (ossl_tolower(*str) == 'x')
base = 16, str++;
}

while((digit = todigit(*str++)) < base)
ret = ret * base + digit;

return ret;
}

static variant_char *ossl_strchr(const variant_char *str, char srch)
{ variant_char c;

while((c = *str)) {
if (c == srch)
return (variant_char *)str;
str++;
}

return NULL;
}

# define OPENSSL_CPUID_SETUP
typedef uint64_t IA32CAP;

void OPENSSL_cpuid_setup(void)
{
static int trigger = 0;
IA32CAP OPENSSL_ia32_cpuid(unsigned int *);
IA32CAP vec;
const variant_char *env;

if (trigger)
return;

trigger = 1;
if ((env = ossl_getenv("OPENSSL_ia32cap")) != NULL) {
int off = (env[0] == '~') ? 1 : 0;

vec = ossl_strtouint64(env + off);

if (off) {
IA32CAP mask = vec;
vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P) & ~mask;
if (mask & (1<<24)) {
/*
* User disables FXSR bit, mask even other capabilities
* that operate exclusively on XMM, so we don't have to
* double-check all the time. We mask PCLMULQDQ, AMD XOP,
* AES-NI and AVX. Formally speaking we don't have to
* do it in x86_64 case, but we can safely assume that
* x86_64 users won't actually flip this flag.
*/
vec &= ~((IA32CAP)(1<<1|1<<11|1<<25|1<<28) << 32);
}
} else if (env[0] == ':') {
vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
}

if ((env = ossl_strchr(env, ':')) != NULL) {
IA32CAP vecx;

env++;
off = (env[0] == '~') ? 1 : 0;
vecx = ossl_strtouint64(env + off);
if (off) {
OPENSSL_ia32cap_P[2] &= ~(unsigned int)vecx;
OPENSSL_ia32cap_P[3] &= ~(unsigned int)(vecx >> 32);
} else {
OPENSSL_ia32cap_P[2] = (unsigned int)vecx;
OPENSSL_ia32cap_P[3] = (unsigned int)(vecx >> 32);
}
} else {
OPENSSL_ia32cap_P[2] = 0;
OPENSSL_ia32cap_P[3] = 0;
}
} else {
vec = OPENSSL_ia32_cpuid(OPENSSL_ia32cap_P);
}

/*
* |(1<<10) sets a reserved bit to signal that variable
* was initialized already... This is to avoid interference
* with cpuid snippets in ELF .init segment.
*/
OPENSSL_ia32cap_P[0] = (unsigned int)vec | (1 << 10);
OPENSSL_ia32cap_P[1] = (unsigned int)(vec >> 32);
}
# else
unsigned int OPENSSL_ia32cap_P[4];
# endif
#endif

#ifndef OPENSSL_CPUID_OBJ
# ifndef OPENSSL_CPUID_SETUP
void OPENSSL_cpuid_setup(void)
{
}
# endif

/*
* The rest are functions that are defined in the same assembler files as
* the CPUID functionality.
*/

/*
* The volatile is used to to ensure that the compiler generates code that reads
* all values from the array and doesn't try to optimize this away. The standard
* doesn't actually require this behavior if the original data pointed to is
* not volatile, but compilers do this in practice anyway.
*
* There are also assembler versions of this function.
*/
# undef CRYPTO_memcmp
int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len)
{
size_t i;
const volatile unsigned char *a = in_a;
const volatile unsigned char *b = in_b;
unsigned char x = 0;

for (i = 0; i < len; i++)
x |= a[i] ^ b[i];

return x;
}

/*
* For systems that don't provide an instruction counter register or equivalent.
*/
uint32_t OPENSSL_rdtsc(void)
{
return 0;
}

size_t OPENSSL_instrument_bus(unsigned int *out, size_t cnt)
{
return 0;
}

size_t OPENSSL_instrument_bus2(unsigned int *out, size_t cnt, size_t max)
{
return 0;
}
#endif
Loading

0 comments on commit ef83daf

Please sign in to comment.