]> git.proxmox.com Git - mirror_edk2.git/blame - CryptoPkg/Library/BaseCryptLib/SysCall/RuntimeMemAllocation.c
CryptoPkg/Crt: import "inet_pton.c" (CVE-2019-14553)
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / SysCall / RuntimeMemAllocation.c
CommitLineData
97f98500
HT
1/** @file\r
2 Light-weight Memory Management Routines for OpenSSL-based Crypto\r
3 Library at Runtime Phase.\r
4\r
269f3b51 5Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>\r
2009f6b4 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
97f98500
HT
7\r
8**/\r
9\r
fc9fa685 10#include <CrtLibSupport.h>\r
97f98500
HT
11#include <Library/UefiBootServicesTableLib.h>\r
12#include <Library/UefiRuntimeLib.h>\r
fc9fa685 13#include <Library/MemoryAllocationLib.h>\r
97f98500
HT
14#include <Guid/EventGroup.h>\r
15\r
16//----------------------------------------------------------------\r
17// Initial version. Needs further optimizations.\r
18//----------------------------------------------------------------\r
19\r
20//\r
21// Definitions for Runtime Memory Operations\r
22//\r
23#define RT_PAGE_SIZE 0x200\r
24#define RT_PAGE_MASK 0x1FF\r
25#define RT_PAGE_SHIFT 9\r
26\r
27#define RT_SIZE_TO_PAGES(a) (((a) >> RT_PAGE_SHIFT) + (((a) & RT_PAGE_MASK) ? 1 : 0))\r
28#define RT_PAGES_TO_SIZE(a) ((a) << RT_PAGE_SHIFT)\r
29\r
30//\r
31// Page Flag Definitions\r
32//\r
33#define RT_PAGE_FREE 0x00000000\r
34#define RT_PAGE_USED 0x00000001\r
35\r
b7d320f8 36#define MIN_REQUIRED_BLOCKS 600\r
97f98500
HT
37\r
38//\r
39// Memory Page Table\r
40//\r
41typedef struct {\r
42 UINTN StartPageOffset; // Offset of the starting page allocated.\r
43 // Only available for USED pages.\r
44 UINT32 PageFlag; // Page Attributes.\r
45} RT_MEMORY_PAGE_ENTRY;\r
46\r
47typedef struct {\r
48 UINTN PageCount;\r
49 UINTN LastEmptyPageOffset;\r
50 UINT8 *DataAreaBase; // Pointer to data Area.\r
51 RT_MEMORY_PAGE_ENTRY Pages[1]; // Page Table Entries.\r
52} RT_MEMORY_PAGE_TABLE;\r
53\r
54//\r
55// Global Page Table for Runtime Cryptographic Provider.\r
56//\r
57RT_MEMORY_PAGE_TABLE *mRTPageTable = NULL;\r
58\r
59//\r
60// Event for Runtime Address Conversion.\r
61//\r
e01f529e 62STATIC EFI_EVENT mVirtualAddressChangeEvent;\r
97f98500
HT
63\r
64\r
65/**\r
66 Initializes pre-allocated memory pointed by ScratchBuffer for subsequent\r
67 runtime use.\r
68\r
69 @param[in, out] ScratchBuffer Pointer to user-supplied memory buffer.\r
70 @param[in] ScratchBufferSize Size of supplied buffer in bytes.\r
71\r
72 @retval EFI_SUCCESS Successful initialization.\r
73\r
74**/\r
75EFI_STATUS\r
76InitializeScratchMemory (\r
77 IN OUT UINT8 *ScratchBuffer,\r
78 IN UINTN ScratchBufferSize\r
79 )\r
80{\r
81 UINTN Index;\r
82 UINTN MemorySize;\r
83\r
84 //\r
85 // Parameters Checking\r
86 //\r
87 if (ScratchBuffer == NULL) {\r
88 return EFI_INVALID_PARAMETER;\r
89 }\r
90\r
91 if (ScratchBufferSize < MIN_REQUIRED_BLOCKS * 1024) {\r
92 return EFI_BUFFER_TOO_SMALL;\r
93 }\r
94\r
95 mRTPageTable = (RT_MEMORY_PAGE_TABLE *)ScratchBuffer;\r
96\r
97 //\r
98 // Initialize Internal Page Table for Memory Management\r
99 //\r
100 SetMem (mRTPageTable, ScratchBufferSize, 0xFF);\r
101 MemorySize = ScratchBufferSize - sizeof (RT_MEMORY_PAGE_TABLE) + sizeof (RT_MEMORY_PAGE_ENTRY);\r
102\r
103 mRTPageTable->PageCount = MemorySize / (RT_PAGE_SIZE + sizeof (RT_MEMORY_PAGE_ENTRY));\r
104 mRTPageTable->LastEmptyPageOffset = 0x0;\r
105\r
106 for (Index = 0; Index < mRTPageTable->PageCount; Index++) {\r
107 mRTPageTable->Pages[Index].PageFlag = RT_PAGE_FREE;\r
108 mRTPageTable->Pages[Index].StartPageOffset = 0;\r
109 }\r
110\r
111 mRTPageTable->DataAreaBase = ScratchBuffer + sizeof (RT_MEMORY_PAGE_TABLE) +\r
112 (mRTPageTable->PageCount - 1) * sizeof (RT_MEMORY_PAGE_ENTRY);\r
113\r
114 return EFI_SUCCESS;\r
115}\r
116\r
117\r
118/**\r
119 Look-up Free memory Region for object allocation.\r
120\r
121 @param[in] AllocationSize Bytes to be allocated.\r
122\r
123 @return Return available page offset for object allocation.\r
124\r
125**/\r
126UINTN\r
127LookupFreeMemRegion (\r
128 IN UINTN AllocationSize\r
129 )\r
130{\r
131 UINTN StartPageIndex;\r
132 UINTN Index;\r
133 UINTN SubIndex;\r
134 UINTN ReqPages;\r
135\r
136 StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->LastEmptyPageOffset);\r
137 ReqPages = RT_SIZE_TO_PAGES (AllocationSize);\r
269f3b51
LQ
138 if (ReqPages > mRTPageTable->PageCount) {\r
139 //\r
140 // No enough region for object allocation.\r
141 //\r
142 return (UINTN)(-1);\r
143 }\r
97f98500
HT
144\r
145 //\r
146 // Look up the free memory region with in current memory map table.\r
147 //\r
148 for (Index = StartPageIndex; Index <= (mRTPageTable->PageCount - ReqPages); ) {\r
149 //\r
150 // Check consecutive ReqPages pages.\r
151 //\r
152 for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
153 if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
154 break;\r
155 }\r
156 }\r
157\r
158 if (SubIndex == ReqPages) {\r
159 //\r
160 // Succeed! Return the Starting Offset.\r
161 //\r
162 return RT_PAGES_TO_SIZE (Index);\r
163 }\r
164\r
165 //\r
166 // Failed! Skip current free memory pages and adjacent Used pages\r
167 //\r
168 while ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
169 SubIndex++;\r
170 }\r
171\r
172 Index += SubIndex;\r
173 }\r
174\r
175 //\r
176 // Look up the free memory region from the beginning of the memory table\r
177 // until the StartCursorOffset\r
178 //\r
269f3b51
LQ
179 if (ReqPages > StartPageIndex) {\r
180 //\r
181 // No enough region for object allocation.\r
182 //\r
183 return (UINTN)(-1);\r
184 }\r
97f98500
HT
185 for (Index = 0; Index < (StartPageIndex - ReqPages); ) {\r
186 //\r
187 // Check Consecutive ReqPages Pages.\r
188 //\r
189 for (SubIndex = 0; SubIndex < ReqPages; SubIndex++) {\r
190 if ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0) {\r
191 break;\r
192 }\r
193 }\r
194\r
195 if (SubIndex == ReqPages) {\r
196 //\r
197 // Succeed! Return the Starting Offset.\r
198 //\r
199 return RT_PAGES_TO_SIZE (Index);\r
200 }\r
201\r
202 //\r
203 // Failed! Skip current adjacent Used pages\r
204 //\r
205 while ((SubIndex < (StartPageIndex - ReqPages)) &&\r
206 ((mRTPageTable->Pages[SubIndex + Index].PageFlag & RT_PAGE_USED) != 0)) {\r
207 SubIndex++;\r
208 }\r
209\r
210 Index += SubIndex;\r
211 }\r
212\r
213 //\r
ab6cee31 214 // No available region for object allocation!\r
97f98500
HT
215 //\r
216 return (UINTN)(-1);\r
217}\r
218\r
219\r
220/**\r
221 Allocates a buffer at runtime phase.\r
222\r
223 @param[in] AllocationSize Bytes to be allocated.\r
224\r
225 @return A pointer to the allocated buffer or NULL if allocation fails.\r
226\r
227**/\r
228VOID *\r
229RuntimeAllocateMem (\r
230 IN UINTN AllocationSize\r
231 )\r
232{\r
233 UINT8 *AllocPtr;\r
234 UINTN ReqPages;\r
235 UINTN Index;\r
236 UINTN StartPage;\r
237 UINTN AllocOffset;\r
238\r
239 AllocPtr = NULL;\r
240 ReqPages = 0;\r
241\r
242 //\r
243 // Look for available consecutive memory region starting from LastEmptyPageOffset.\r
244 // If no proper memory region found, look up from the beginning.\r
245 // If still not found, return NULL to indicate failed allocation.\r
246 //\r
247 AllocOffset = LookupFreeMemRegion (AllocationSize);\r
248 if (AllocOffset == (UINTN)(-1)) {\r
249 return NULL;\r
250 }\r
251\r
252 //\r
253 // Allocates consecutive memory pages with length of Size. Update the page\r
254 // table status. Returns the starting address.\r
255 //\r
256 ReqPages = RT_SIZE_TO_PAGES (AllocationSize);\r
257 AllocPtr = mRTPageTable->DataAreaBase + AllocOffset;\r
258 StartPage = RT_SIZE_TO_PAGES (AllocOffset);\r
259 Index = 0;\r
260 while (Index < ReqPages) {\r
261 mRTPageTable->Pages[StartPage + Index].PageFlag |= RT_PAGE_USED;\r
262 mRTPageTable->Pages[StartPage + Index].StartPageOffset = AllocOffset;\r
263\r
264 Index++;\r
265 }\r
266\r
267 mRTPageTable->LastEmptyPageOffset = AllocOffset + RT_PAGES_TO_SIZE (ReqPages);\r
268\r
269 ZeroMem (AllocPtr, AllocationSize);\r
270\r
271 //\r
272 // Returns a void pointer to the allocated space\r
273 //\r
274 return AllocPtr;\r
275}\r
276\r
277\r
278/**\r
279 Frees a buffer that was previously allocated at runtime phase.\r
280\r
281 @param[in] Buffer Pointer to the buffer to free.\r
282\r
283**/\r
284VOID\r
285RuntimeFreeMem (\r
286 IN VOID *Buffer\r
287 )\r
288{\r
289 UINTN StartOffset;\r
290 UINTN StartPageIndex;\r
291\r
6e4489d8 292 StartOffset = (UINTN)Buffer - (UINTN)mRTPageTable->DataAreaBase;\r
97f98500
HT
293 StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES(StartOffset)].StartPageOffset);\r
294\r
295 while (StartPageIndex < mRTPageTable->PageCount) {\r
296 if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
297 (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
298 //\r
299 // Free this page\r
300 //\r
301 mRTPageTable->Pages[StartPageIndex].PageFlag &= ~RT_PAGE_USED;\r
302 mRTPageTable->Pages[StartPageIndex].PageFlag |= RT_PAGE_FREE;\r
303 mRTPageTable->Pages[StartPageIndex].StartPageOffset = 0;\r
304\r
305 StartPageIndex++;\r
306 } else {\r
307 break;\r
308 }\r
309 }\r
310\r
311 return;\r
312}\r
313\r
314\r
315/**\r
316 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.\r
317\r
318 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE\r
319 event. It converts a pointer to a new virtual address.\r
320\r
321 @param[in] Event The event whose notification function is being invoked.\r
322 @param[in] Context The pointer to the notification function's context.\r
323\r
324**/\r
325VOID\r
326EFIAPI\r
327RuntimeCryptLibAddressChangeEvent (\r
328 IN EFI_EVENT Event,\r
329 IN VOID *Context\r
330 )\r
331{\r
332 //\r
333 // Converts a pointer for runtime memory management to a new virtual address.\r
334 //\r
335 EfiConvertPointer (0x0, (VOID **) &mRTPageTable->DataAreaBase);\r
336 EfiConvertPointer (0x0, (VOID **) &mRTPageTable);\r
337}\r
338\r
339\r
340/**\r
341 Constructor routine for runtime crypt library instance.\r
342\r
343 The constructor function pre-allocates space for runtime cryptographic operation.\r
344\r
345 @param ImageHandle The firmware allocated handle for the EFI image.\r
346 @param SystemTable A pointer to the EFI System Table.\r
347\r
348 @retval EFI_SUCCESS The construction succeeded.\r
349 @retval EFI_OUT_OF_RESOURCE Failed to allocate memory.\r
350\r
351**/\r
352EFI_STATUS\r
353EFIAPI\r
354RuntimeCryptLibConstructor (\r
355 IN EFI_HANDLE ImageHandle,\r
356 IN EFI_SYSTEM_TABLE *SystemTable\r
357 )\r
358{\r
359 EFI_STATUS Status;\r
360 VOID *Buffer;\r
361\r
362 //\r
363 // Pre-allocates runtime space for possible cryptographic operations\r
364 //\r
365 Buffer = AllocateRuntimePool (MIN_REQUIRED_BLOCKS * 1024);\r
366 Status = InitializeScratchMemory (Buffer, MIN_REQUIRED_BLOCKS * 1024);\r
367 if (EFI_ERROR (Status)) {\r
368 return Status;\r
369 }\r
370\r
371 //\r
372 // Create address change event\r
373 //\r
374 Status = gBS->CreateEventEx (\r
375 EVT_NOTIFY_SIGNAL,\r
376 TPL_NOTIFY,\r
377 RuntimeCryptLibAddressChangeEvent,\r
378 NULL,\r
379 &gEfiEventVirtualAddressChangeGuid,\r
380 &mVirtualAddressChangeEvent\r
381 );\r
382 ASSERT_EFI_ERROR (Status);\r
383\r
384 return Status;\r
385}\r
386\r
387\r
388//\r
389// -- Memory-Allocation Routines Wrapper for UEFI-OpenSSL Library --\r
390//\r
391\r
392/* Allocates memory blocks */\r
393void *malloc (size_t size)\r
394{\r
6b8ebcb8 395 return RuntimeAllocateMem ((UINTN) size);\r
97f98500
HT
396}\r
397\r
398/* Reallocate memory blocks */\r
399void *realloc (void *ptr, size_t size)\r
400{\r
401 VOID *NewPtr;\r
402 UINTN StartOffset;\r
403 UINTN StartPageIndex;\r
404 UINTN PageCount;\r
405\r
e89d6721
LE
406 if (ptr == NULL) {\r
407 return malloc (size);\r
408 }\r
409\r
97f98500
HT
410 //\r
411 // Get Original Size of ptr\r
412 //\r
6e4489d8 413 StartOffset = (UINTN)ptr - (UINTN)mRTPageTable->DataAreaBase;\r
97f98500
HT
414 StartPageIndex = RT_SIZE_TO_PAGES (mRTPageTable->Pages[RT_SIZE_TO_PAGES (StartOffset)].StartPageOffset);\r
415 PageCount = 0;\r
416 while (StartPageIndex < mRTPageTable->PageCount) {\r
417 if (((mRTPageTable->Pages[StartPageIndex].PageFlag & RT_PAGE_USED) != 0) &&\r
418 (mRTPageTable->Pages[StartPageIndex].StartPageOffset == StartOffset)) {\r
419 StartPageIndex++;\r
420 PageCount++;\r
421 } else {\r
422 break;\r
423 }\r
424 }\r
425\r
426 if (size <= RT_PAGES_TO_SIZE (PageCount)) {\r
427 //\r
428 // Return the original pointer, if Caller try to reduce region size;\r
429 //\r
430 return ptr;\r
431 }\r
432\r
433 NewPtr = RuntimeAllocateMem ((UINTN) size);\r
434 if (NewPtr == NULL) {\r
435 return NULL;\r
436 }\r
437\r
438 CopyMem (NewPtr, ptr, RT_PAGES_TO_SIZE (PageCount));\r
439\r
440 RuntimeFreeMem (ptr);\r
441\r
442 return NewPtr;\r
443}\r
444\r
445/* Deallocates or frees a memory block */\r
446void free (void *ptr)\r
447{\r
1246dde5
LE
448 //\r
449 // In Standard C, free() handles a null pointer argument transparently. This\r
450 // is not true of RuntimeFreeMem() below, so protect it.\r
451 //\r
452 if (ptr != NULL) {\r
453 RuntimeFreeMem (ptr);\r
454 }\r
97f98500 455}\r