1 /*
2 * winmiscs.c: Windows-specific standalone functions. Has the same
3 * relationship to winmisc.c that utils.c does to misc.c, but the
4 * corresponding name 'winutils.c' was already taken.
5 */
6
7 #include "putty.h"
8
9 #ifndef NO_SECUREZEROMEMORY
10 /*
11 * Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
12 */
smemclr(void * b,size_t n)13 void smemclr(void *b, size_t n) {
14 if (b && n > 0)
15 SecureZeroMemory(b, n);
16 }
17 #endif
18
19 #ifdef MINEFIELD
20 /*
21 * Minefield - a Windows equivalent for Electric Fence
22 */
23
24 #define PAGESIZE 4096
25
26 /*
27 * Design:
28 *
29 * We start by reserving as much virtual address space as Windows
30 * will sensibly (or not sensibly) let us have. We flag it all as
31 * invalid memory.
32 *
33 * Any allocation attempt is satisfied by committing one or more
34 * pages, with an uncommitted page on either side. The returned
35 * memory region is jammed up against the _end_ of the pages.
36 *
37 * Freeing anything causes instantaneous decommitment of the pages
38 * involved, so stale pointers are caught as soon as possible.
39 */
40
41 static int minefield_initialised = 0;
42 static void *minefield_region = NULL;
43 static long minefield_size = 0;
44 static long minefield_npages = 0;
45 static long minefield_curpos = 0;
46 static unsigned short *minefield_admin = NULL;
47 static void *minefield_pages = NULL;
48
minefield_admin_hide(int hide)49 static void minefield_admin_hide(int hide)
50 {
51 int access = hide ? PAGE_NOACCESS : PAGE_READWRITE;
52 VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL);
53 }
54
minefield_init(void)55 static void minefield_init(void)
56 {
57 int size;
58 int admin_size;
59 int i;
60
61 for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) {
62 minefield_region = VirtualAlloc(NULL, size,
63 MEM_RESERVE, PAGE_NOACCESS);
64 if (minefield_region)
65 break;
66 }
67 minefield_size = size;
68
69 /*
70 * Firstly, allocate a section of that to be the admin block.
71 * We'll need a two-byte field for each page.
72 */
73 minefield_admin = minefield_region;
74 minefield_npages = minefield_size / PAGESIZE;
75 admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1);
76 minefield_npages = (minefield_size - admin_size) / PAGESIZE;
77 minefield_pages = (char *) minefield_region + admin_size;
78
79 /*
80 * Commit the admin region.
81 */
82 VirtualAlloc(minefield_admin, minefield_npages * 2,
83 MEM_COMMIT, PAGE_READWRITE);
84
85 /*
86 * Mark all pages as unused (0xFFFF).
87 */
88 for (i = 0; i < minefield_npages; i++)
89 minefield_admin[i] = 0xFFFF;
90
91 /*
92 * Hide the admin region.
93 */
94 minefield_admin_hide(1);
95
96 minefield_initialised = 1;
97 }
98
minefield_bomb(void)99 static void minefield_bomb(void)
100 {
101 div(1, *(int *) minefield_pages);
102 }
103
minefield_alloc(int size)104 static void *minefield_alloc(int size)
105 {
106 int npages;
107 int pos, lim, region_end, region_start;
108 int start;
109 int i;
110
111 npages = (size + PAGESIZE - 1) / PAGESIZE;
112
113 minefield_admin_hide(0);
114
115 /*
116 * Search from current position until we find a contiguous
117 * bunch of npages+2 unused pages.
118 */
119 pos = minefield_curpos;
120 lim = minefield_npages;
121 while (1) {
122 /* Skip over used pages. */
123 while (pos < lim && minefield_admin[pos] != 0xFFFF)
124 pos++;
125 /* Count unused pages. */
126 start = pos;
127 while (pos < lim && pos - start < npages + 2 &&
128 minefield_admin[pos] == 0xFFFF)
129 pos++;
130 if (pos - start == npages + 2)
131 break;
132 /* If we've reached the limit, reset the limit or stop. */
133 if (pos >= lim) {
134 if (lim == minefield_npages) {
135 /* go round and start again at zero */
136 lim = minefield_curpos;
137 pos = 0;
138 } else {
139 minefield_admin_hide(1);
140 return NULL;
141 }
142 }
143 }
144
145 minefield_curpos = pos - 1;
146
147 /*
148 * We have npages+2 unused pages starting at start. We leave
149 * the first and last of these alone and use the rest.
150 */
151 region_end = (start + npages + 1) * PAGESIZE;
152 region_start = region_end - size;
153 /* FIXME: could align here if we wanted */
154
155 /*
156 * Update the admin region.
157 */
158 for (i = start + 2; i < start + npages + 1; i++)
159 minefield_admin[i] = 0xFFFE; /* used but no region starts here */
160 minefield_admin[start + 1] = region_start % PAGESIZE;
161
162 minefield_admin_hide(1);
163
164 VirtualAlloc((char *) minefield_pages + region_start, size,
165 MEM_COMMIT, PAGE_READWRITE);
166 return (char *) minefield_pages + region_start;
167 }
168
minefield_free(void * ptr)169 static void minefield_free(void *ptr)
170 {
171 int region_start, i, j;
172
173 minefield_admin_hide(0);
174
175 region_start = (char *) ptr - (char *) minefield_pages;
176 i = region_start / PAGESIZE;
177 if (i < 0 || i >= minefield_npages ||
178 minefield_admin[i] != region_start % PAGESIZE)
179 minefield_bomb();
180 for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) {
181 minefield_admin[j] = 0xFFFF;
182 }
183
184 VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT);
185
186 minefield_admin_hide(1);
187 }
188
minefield_get_size(void * ptr)189 static int minefield_get_size(void *ptr)
190 {
191 int region_start, i, j;
192
193 minefield_admin_hide(0);
194
195 region_start = (char *) ptr - (char *) minefield_pages;
196 i = region_start / PAGESIZE;
197 if (i < 0 || i >= minefield_npages ||
198 minefield_admin[i] != region_start % PAGESIZE)
199 minefield_bomb();
200 for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++);
201
202 minefield_admin_hide(1);
203
204 return j * PAGESIZE - region_start;
205 }
206
minefield_c_malloc(size_t size)207 void *minefield_c_malloc(size_t size)
208 {
209 if (!minefield_initialised)
210 minefield_init();
211 return minefield_alloc(size);
212 }
213
minefield_c_free(void * p)214 void minefield_c_free(void *p)
215 {
216 if (!minefield_initialised)
217 minefield_init();
218 minefield_free(p);
219 }
220
221 /*
222 * realloc _always_ moves the chunk, for rapid detection of code
223 * that assumes it won't.
224 */
minefield_c_realloc(void * p,size_t size)225 void *minefield_c_realloc(void *p, size_t size)
226 {
227 size_t oldsize;
228 void *q;
229 if (!minefield_initialised)
230 minefield_init();
231 q = minefield_alloc(size);
232 oldsize = minefield_get_size(p);
233 memcpy(q, p, (oldsize < size ? oldsize : size));
234 minefield_free(p);
235 return q;
236 }
237
238 #endif /* MINEFIELD */
239
240 #if defined _MSC_VER && _MSC_VER < 1800
241
242 /*
243 * Work around lack of strtoumax in older MSVC libraries
244 */
strtoumax(const char * nptr,char ** endptr,int base)245 uintmax_t strtoumax(const char *nptr, char **endptr, int base)
246 {
247 return _strtoui64(nptr, endptr, base);
248 }
249
250 #endif
251
252 #if defined _M_ARM || defined _M_ARM64
253
platform_aes_hw_available(void)254 bool platform_aes_hw_available(void)
255 {
256 return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
257 }
258
platform_sha256_hw_available(void)259 bool platform_sha256_hw_available(void)
260 {
261 return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
262 }
263
platform_sha1_hw_available(void)264 bool platform_sha1_hw_available(void)
265 {
266 return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
267 }
268
platform_sha512_hw_available(void)269 bool platform_sha512_hw_available(void)
270 {
271 /* As of 2020-12-24, as far as I can tell from docs.microsoft.com,
272 * Windows on Arm does not yet provide a PF_ARM_V8_* flag for the
273 * SHA-512 architecture extension. */
274 return false;
275 }
276
277 #endif
278
is_console_handle(HANDLE handle)279 bool is_console_handle(HANDLE handle)
280 {
281 DWORD ignored_output;
282 if (GetConsoleMode(handle, &ignored_output))
283 return true;
284 return false;
285 }
286