爲什麼OPENSSL_cleanse看起來如此複雜和線程不安全?
該函數很複雜,試圖阻止優化器將其作爲死代碼移除。
C標準沒有提供像pin
這樣的關鍵字來確保不刪除語句。如果零化器被移除,那麼編譯器人員會告訴你「...但你要求優化」。
C11在附錄K中提供memset_s
,保證不會被刪除。但是Drepper和朋友反對「安全」的功能,所以它們在GNU Linux上不可用。例如,參見glibc library is missing memset_s。
OpenSSL也避免了volatile
,因爲GCC人員將標準解釋爲硬件支持的內存。就是說,易失性存儲器可以通過硬件來改變,但不能通過另一個線程來改變這與微軟對限定詞的解釋形成鮮明對比。
另請注意,在Windows平臺上(OpenSSL是跨平臺的),OpenSSL可以使用SecureZeroMemory
。微軟解決了優化器提前刪除代碼的問題。
EDIT(二月2016):它看起來像OpenSSL的1.1.0簡化了清洗功能:RT4116: Change cleanse to just memset。下面是關於mem_clr.c
的DIFF:
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()上OpenSSL的GitHub上。
來源
2015-05-14 11:54:14
jww
我知道的一個目的是避免它被編譯器優化掉。 – updogliu 2014-10-17 21:38:40