2014-10-17 10 views
7

Questa è l'attuazione di OPENSSL_cleanse in OpenSSL 1.0.1iPerché OPENSSL_cleanse sembra così complesso e thread-safe?

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

Sembra complessa e filo-pericoloso (leggendo e scrivendo variabile globale cleanse_ctr). Qualcuno può spiegare un po 'di questa implementazione? Un utente deve preoccuparsi della possibile corsa di dati in esso contenuta?

+2

Uno degli scopi che so è evitare che la sua invocazione venga ottimizzata dal compilatore. – updogliu

risposta

4

C'è una corsa di dati nel codice, ma non importa perché il punto della variabile è solo per fornire vari dati inutili con cui riempire un pezzo di memoria. In altre parole, non ha mai veramente importanza quale valore un dato thread legge da quella variabile. Gli utenti non devono preoccuparsi di questo. In effetti, la corsa dei dati potrebbe persino rendere la funzione più efficace.

5

Perché OPENSSL_cleanse sembra così complesso e thread-non sicuro?

La funzione è complessa nel tentativo di impedire all'ottimizzatore di rimuoverlo come codice morto.

Lo standard C non fornisce una parola chiave come pin per garantire che una dichiarazione non venga rimossa. Se lo zeroizer è stato rimosso, i compilatori avrebbero detto "... ma hai chiesto delle ottimizzazioni".

C11 offre memset_s nell'allegato K, che è garantito non essere rimosso. Ma Drepper e gli amici contestano le funzioni "più sicure", quindi non sono disponibili su GNU Linux. Vedi, ad esempio, glibc library is missing memset_s.

OpenSSL evita anche volatile perché la gente GCC interpreta lo standard per indicare la memoria supportata dall'hardware. Cioè, la memoria volatile può essere cambiata dall'hardware, ma non da un altro thread. Questo è in contrasto con l'interpretazione di Microsoft del qualificatore.

Si noti inoltre che sulla piattaforma Windows (OpenSSL è multipiattaforma), OpenSSL potrebbe utilizzare SecureZeroMemory. Microsoft ha affrontato il problema dell'ottimizzatore rimuovendo il codice in anticipo.


EDIT (FEB 2016): Sembra che OpenSSL 1.1.0 semplificato la funzione di pulizia: RT4116: Change cleanse to just memset. Ecco il diff su 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); 
} 

vedere anche Issue 455: Reimplement non-asm OPENSSL_cleanse() su GitHub di OpenSSL.