2014-10-17 3 views
7

Это реализация OPENSSL_cleanse в OpenSSL 1.0.1iПочему OPENSSL_cleanse выглядит настолько сложным и небезопасным?

unsigned char cleanse_ctr = 0; 

void OPENSSL_cleanse(void *ptr, size_t len) 
{ 
    unsigned char *p = ptr; 
    size_t loop = len, ctr = cleanse_ctr; 
    while(loop--) 
    { 
     *(p++) = (unsigned char)ctr; 
     ctr += (17 + ((size_t)p & 0xF)); 
    } 
    p=memchr(ptr, (unsigned char)ctr, len); 
    if(p) 
     ctr += (63 + (size_t)p); 
    cleanse_ctr = (unsigned char)ctr; 
} 

Он выглядит сложным и поточно-небезопасных (путем чтения и записи глобальной переменной cleanse_ctr). Может кто-нибудь, пожалуйста, объясните немного об этой реализации? Должен ли пользователь беспокоиться о возможной гонке данных в нем?

+2

Одна из целей, о которой я знаю, заключается в том, чтобы избежать ее вызова от оптимизации компилятором. – updogliu

ответ

4

В коде есть гонка данных, но это не имеет значения, потому что точка переменной просто предназначена для предоставления различных данных мусора, с помощью которых можно заполнить кусок памяти. Другими словами, на самом деле не имеет значения, какое значение прочитает любой поток из этой переменной. Пользователям не нужно беспокоиться об этом. Фактически, гонка данных может даже сделать функцию более эффективной.

5

Почему OPENSSL_cleanse выглядит настолько сложным и небезопасным?

Эта функция сложна, чтобы оптимизатор не удалял ее как мертвый код.

В стандарте C не указано ключевое слово, например pin, чтобы убедиться, что инструкция не удалена. Если нулевой инструмент был удален, люди компилятора скажут вам «... но вы попросили оптимизацию».

C11 предлагает memset_s в Приложении K, который гарантированно не требуется. Но Drepper и друзья возражают против «более безопасных» функций, поэтому они недоступны в GNU Linux. См., Например, glibc library is missing memset_s.

OpenSSL также избегает volatile, потому что люди GCC интерпретируют стандарт как память, поддерживаемую аппаратным обеспечением. То есть, энергозависимая память может быть заменена аппаратным обеспечением, но не другим потоком. Это противоречит интерпретации Microsoft квалификатора.

Также обратите внимание, что на платформе Windows (OpenSSL - кросс-платформа) OpenSSL может использовать SecureZeroMemory. Microsoft обратилась к проблеме оптимизатора, удаляющего код раньше.


EDIT (февраль 2016): Похоже, OpenSSL 1.1.0 упростила функцию чистят: RT4116: Change cleanse to just memset. Вот дифференциал на mem_clr.c:

diff --git a/crypto/mem_clr.c b/crypto/mem_clr.c 
index e6450a1..3389919 100644 (file) 
--- a/crypto/mem_clr.c 
+++ b/crypto/mem_clr.c 
@@ -59,23 +59,16 @@ 
#include <string.h> 
#include <openssl/crypto.h> 

-extern unsigned char cleanse_ctr; 
-unsigned char cleanse_ctr = 0; 
+/* 
+ * Pointer to memset is volatile so that compiler must de-reference 
+ * the pointer and can't assume that it points to any function in 
+ * particular (such as memset, which it then might further "optimize") 
+ */ 
+typedef void *(*memset_t)(void *,int,size_t); 
+ 
+static volatile memset_t memset_func = memset; 

void OPENSSL_cleanse(void *ptr, size_t len) 
{ 
- unsigned char *p = ptr; 
- size_t loop = len, ctr = cleanse_ctr; 
- 
- if (ptr == NULL) 
-  return; 
- 
- while (loop--) { 
-  *(p++) = (unsigned char)ctr; 
-  ctr += (17 + ((size_t)p & 0xF)); 
- } 
- p = memchr(ptr, (unsigned char)ctr, len); 
- if (p) 
-  ctr += (63 + (size_t)p); 
- cleanse_ctr = (unsigned char)ctr; 
+ memset_func(ptr, 0, len); 
} 

Также см Issue 455: Reimplement non-asm OPENSSL_cleanse() на GitHub OpenSSL,.

Смежные вопросы