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