]> Git Repo - VerusCoin.git/blob - src/allocators.h
Merge pull request #3138 from super3/master
[VerusCoin.git] / src / allocators.h
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #ifndef BITCOIN_ALLOCATORS_H
7 #define BITCOIN_ALLOCATORS_H
8
9 #include <map>
10 #include <string>
11 #include <string.h>
12
13 #include <boost/thread/mutex.hpp>
14 #include <boost/thread/once.hpp>
15 #include <openssl/crypto.h> // for OPENSSL_cleanse()
16
17 /**
18  * Thread-safe class to keep track of locked (ie, non-swappable) memory pages.
19  *
20  * Memory locks do not stack, that is, pages which have been locked several times by calls to mlock()
21  * will be unlocked by a single call to munlock(). This can result in keying material ending up in swap when
22  * those functions are used naively. This class simulates stacking memory locks by keeping a counter per page.
23  *
24  * @note By using a map from each page base address to lock count, this class is optimized for
25  * small objects that span up to a few pages, mostly smaller than a page. To support large allocations,
26  * something like an interval tree would be the preferred data structure.
27  */
28 template <class Locker> class LockedPageManagerBase
29 {
30 public:
31     LockedPageManagerBase(size_t page_size):
32         page_size(page_size)
33     {
34         // Determine bitmask for extracting page from address
35         assert(!(page_size & (page_size-1))); // size must be power of two
36         page_mask = ~(page_size - 1);
37     }
38
39     ~LockedPageManagerBase()
40     {
41         assert(this->GetLockedPageCount() == 0);
42     }
43
44
45     // For all pages in affected range, increase lock count
46     void LockRange(void *p, size_t size)
47     {
48         boost::mutex::scoped_lock lock(mutex);
49         if(!size) return;
50         const size_t base_addr = reinterpret_cast<size_t>(p);
51         const size_t start_page = base_addr & page_mask;
52         const size_t end_page = (base_addr + size - 1) & page_mask;
53         for(size_t page = start_page; page <= end_page; page += page_size)
54         {
55             Histogram::iterator it = histogram.find(page);
56             if(it == histogram.end()) // Newly locked page
57             {
58                 locker.Lock(reinterpret_cast<void*>(page), page_size);
59                 histogram.insert(std::make_pair(page, 1));
60             }
61             else // Page was already locked; increase counter
62             {
63                 it->second += 1;
64             }
65         }
66     }
67
68     // For all pages in affected range, decrease lock count
69     void UnlockRange(void *p, size_t size)
70     {
71         boost::mutex::scoped_lock lock(mutex);
72         if(!size) return;
73         const size_t base_addr = reinterpret_cast<size_t>(p);
74         const size_t start_page = base_addr & page_mask;
75         const size_t end_page = (base_addr + size - 1) & page_mask;
76         for(size_t page = start_page; page <= end_page; page += page_size)
77         {
78             Histogram::iterator it = histogram.find(page);
79             assert(it != histogram.end()); // Cannot unlock an area that was not locked
80             // Decrease counter for page, when it is zero, the page will be unlocked
81             it->second -= 1;
82             if(it->second == 0) // Nothing on the page anymore that keeps it locked
83             {
84                 // Unlock page and remove the count from histogram
85                 locker.Unlock(reinterpret_cast<void*>(page), page_size);
86                 histogram.erase(it);
87             }
88         }
89     }
90
91     // Get number of locked pages for diagnostics
92     int GetLockedPageCount()
93     {
94         boost::mutex::scoped_lock lock(mutex);
95         return histogram.size();
96     }
97
98 private:
99     Locker locker;
100     boost::mutex mutex;
101     size_t page_size, page_mask;
102     // map of page base address to lock count
103     typedef std::map<size_t,int> Histogram;
104     Histogram histogram;
105 };
106
107
108 /**
109  * OS-dependent memory page locking/unlocking.
110  * Defined as policy class to make stubbing for test possible.
111  */
112 class MemoryPageLocker
113 {
114 public:
115     /** Lock memory pages.
116      * addr and len must be a multiple of the system page size
117      */
118     bool Lock(const void *addr, size_t len);
119     /** Unlock memory pages.
120      * addr and len must be a multiple of the system page size
121      */
122     bool Unlock(const void *addr, size_t len);
123 };
124
125 /**
126  * Singleton class to keep track of locked (ie, non-swappable) memory pages, for use in
127  * std::allocator templates.
128  *
129  * Some implementations of the STL allocate memory in some constructors (i.e., see
130  * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
131  * Due to the unpredictable order of static initializers, we have to make sure the
132  * LockedPageManager instance exists before any other STL-based objects that use
133  * secure_allocator are created. So instead of having LockedPageManager also be
134  * static-intialized, it is created on demand.
135  */
136 class LockedPageManager: public LockedPageManagerBase<MemoryPageLocker>
137 {
138 public:
139     static LockedPageManager& Instance() 
140     {
141         boost::call_once(LockedPageManager::CreateInstance, LockedPageManager::init_flag);
142         return *LockedPageManager::_instance;
143     }
144
145 private:
146     LockedPageManager();
147
148     static void CreateInstance()
149     {
150         // Using a local static instance guarantees that the object is initialized
151         // when it's first needed and also deinitialized after all objects that use
152         // it are done with it.  I can think of one unlikely scenario where we may
153         // have a static deinitialization order/problem, but the check in
154         // LockedPageManagerBase's destructor helps us detect if that ever happens.
155         static LockedPageManager instance;
156         LockedPageManager::_instance = &instance;
157     }
158
159     static LockedPageManager* _instance;
160     static boost::once_flag init_flag;
161 };
162
163 //
164 // Functions for directly locking/unlocking memory objects.
165 // Intended for non-dynamically allocated structures.
166 //
167 template<typename T> void LockObject(const T &t) {
168     LockedPageManager::Instance().LockRange((void*)(&t), sizeof(T));
169 }
170
171 template<typename T> void UnlockObject(const T &t) {
172     OPENSSL_cleanse((void*)(&t), sizeof(T));
173     LockedPageManager::Instance().UnlockRange((void*)(&t), sizeof(T));
174 }
175
176 //
177 // Allocator that locks its contents from being paged
178 // out of memory and clears its contents before deletion.
179 //
180 template<typename T>
181 struct secure_allocator : public std::allocator<T>
182 {
183     // MSVC8 default copy constructor is broken
184     typedef std::allocator<T> base;
185     typedef typename base::size_type size_type;
186     typedef typename base::difference_type  difference_type;
187     typedef typename base::pointer pointer;
188     typedef typename base::const_pointer const_pointer;
189     typedef typename base::reference reference;
190     typedef typename base::const_reference const_reference;
191     typedef typename base::value_type value_type;
192     secure_allocator() throw() {}
193     secure_allocator(const secure_allocator& a) throw() : base(a) {}
194     template <typename U>
195     secure_allocator(const secure_allocator<U>& a) throw() : base(a) {}
196     ~secure_allocator() throw() {}
197     template<typename _Other> struct rebind
198     { typedef secure_allocator<_Other> other; };
199
200     T* allocate(std::size_t n, const void *hint = 0)
201     {
202         T *p;
203         p = std::allocator<T>::allocate(n, hint);
204         if (p != NULL)
205             LockedPageManager::Instance().LockRange(p, sizeof(T) * n);
206         return p;
207     }
208
209     void deallocate(T* p, std::size_t n)
210     {
211         if (p != NULL)
212         {
213             OPENSSL_cleanse(p, sizeof(T) * n);
214             LockedPageManager::Instance().UnlockRange(p, sizeof(T) * n);
215         }
216         std::allocator<T>::deallocate(p, n);
217     }
218 };
219
220
221 //
222 // Allocator that clears its contents before deletion.
223 //
224 template<typename T>
225 struct zero_after_free_allocator : public std::allocator<T>
226 {
227     // MSVC8 default copy constructor is broken
228     typedef std::allocator<T> base;
229     typedef typename base::size_type size_type;
230     typedef typename base::difference_type  difference_type;
231     typedef typename base::pointer pointer;
232     typedef typename base::const_pointer const_pointer;
233     typedef typename base::reference reference;
234     typedef typename base::const_reference const_reference;
235     typedef typename base::value_type value_type;
236     zero_after_free_allocator() throw() {}
237     zero_after_free_allocator(const zero_after_free_allocator& a) throw() : base(a) {}
238     template <typename U>
239     zero_after_free_allocator(const zero_after_free_allocator<U>& a) throw() : base(a) {}
240     ~zero_after_free_allocator() throw() {}
241     template<typename _Other> struct rebind
242     { typedef zero_after_free_allocator<_Other> other; };
243
244     void deallocate(T* p, std::size_t n)
245     {
246         if (p != NULL)
247             OPENSSL_cleanse(p, sizeof(T) * n);
248         std::allocator<T>::deallocate(p, n);
249     }
250 };
251
252 // This is exactly like std::string, but with a custom allocator.
253 typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;
254
255 #endif
This page took 0.036602 seconds and 4 git commands to generate.