1 /*
2 * Derived from Botan's Mlock Allocator
3 * 
4 * This is a more advanced base allocator.
5 * 
6 * (C) 2012,2014 Jack Lloyd
7 * (C) 2014-2015 Etienne Cimon
8 * (C) 2014,2015 Etienne Cimon
9 *
10 * Distributed under the terms of the Simplified BSD License (see Botan's license.txt)
11 */
12 module memutils.cryptosafe;
13 import memutils.constants;
14 static if (HasCryptoSafe):
15 pragma(msg, "Enhanced memory security is enabled.");
16 
17 import memutils.allocators;
18 import memutils.securepool;
19 import memutils.debugger;
20 
21 final class SecureAllocator(Base : Allocator) : Allocator
22 {
23 private:	
24 	Base m_secondary;
25 	static if (HasSecurePool) {
26 		
27 		__gshared SecurePool ms_zeroise;
28 		__gshared bool ms_deinit;
29 		shared static this() { 
30 			//logDebug("Shared static this() SecurePool");
31 			if (!ms_zeroise) ms_zeroise = new SecurePool();
32 		}
33 		shared static ~this() { 
34 			if (ms_zeroise) { destroy(ms_zeroise); ms_zeroise = null; ms_deinit = true; }
35 		}
36 	}
37 
38 public:	
39 	this() {
40 		version(TLSGC) { } else {
41 			if (!mtx) mtx = new Mutex;
42 		}
43 		static if (HasSecurePool) {
44 			if (!ms_zeroise) ms_zeroise = new SecurePool();
45 		}
46 		m_secondary = getAllocator!Base();
47 	}
48 	
49 	void[] alloc(size_t n)
50 	{
51 		version(TLSGC) { } else {
52 			mtx.lock_nothrow();
53 			scope(exit) mtx.unlock_nothrow();
54 		}
55 		static if (HasSecurePool) {
56 			//logDebug("CryptoSafe alloc ", n);
57 			if (void[] p = ms_zeroise.alloc(n)) {
58 				//logDebug("alloc P: ", p.length, " & ", p.ptr);
59 				return p;
60 			}
61 		}
62 		//logDebug("secondary alloc");
63 		void[] p = m_secondary.alloc(n);
64 
65 		//logDebug("FALLBACK alloc P: ", p.length, " & ", p.ptr);
66 		return p;
67 	}
68 
69 	void[] realloc(void[] mem, size_t n)
70 	{
71 		version(TLSGC) { } else {
72 			mtx.lock_nothrow();
73 			scope(exit) mtx.unlock_nothrow();
74 		}
75 		//logTrace("realloc P: ", mem.length, " & ", mem.ptr);
76 		if (n <= mem.length)
77 			return mem;
78 		import core.stdc.string : memmove, memset;
79 
80 		static if (HasSecurePool) {
81 			if (ms_zeroise.has(mem)) {
82 				void[] p = ms_zeroise.alloc(n);
83 				if (!p) 
84 					p = m_secondary.alloc(n);
85 				memmove(p.ptr, mem.ptr, mem.length);
86 				memset(mem.ptr, 0, mem.length);
87 				ms_zeroise.free(mem);
88 				return p;
89 			}
90 		}
91 
92 		return m_secondary.realloc(mem, n);
93 	}
94 
95 	void free(void[] mem)
96 	{
97 		version(TLSGC) { } else {
98 			mtx.lock_nothrow();
99 			scope(exit) mtx.unlock_nothrow();
100 		}
101 		//logTrace("free P: ", mem.length, " & ", mem.ptr);
102 		import core.stdc.string : memset;
103 		bool skip_zero;
104 		if (mem.length > 1024) {
105 			skip_zero = true;
106 			ubyte* ptr = cast(ubyte*)mem.ptr;
107 			// check some random bytes for zero
108 			size_t j;
109 			foreach (i; 0 .. mem.length/128) {
110 				if (ptr[j]+4 == 0 && ptr[j]+8 == 0 && ptr[j]+50 == 0 && ptr[j]+80 == 0) {
111 					j += 128;
112 					continue;
113 				}
114 				skip_zero = false;
115 				break;
116 			}
117 		}
118 		if (!skip_zero)
119 			memset(mem.ptr, 0, mem.length);
120 		static if (HasSecurePool)
121 			if (ms_deinit || ms_zeroise.free(mem))
122 				return;
123 		m_secondary.free(mem);
124 	}
125 
126 }