OpenCore  1.0.4
OpenCore Bootloader
1.0.4
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
OcValidateLib.c
Go to the documentation of this file.
1
16#include "ocvalidate.h"
17#include "OcValidateLib.h"
18
19INT64
21 VOID
22 )
23{
24 struct timeval Time;
25
26 //
27 // Get current time.
28 //
29 gettimeofday (&Time, NULL);
30 //
31 // Return milliseconds.
32 //
33 return Time.tv_sec * 1000LL + Time.tv_usec / 1000LL;
34}
35
36BOOLEAN
38 IN CONST CHAR8 *Path
39 )
40{
41 UINTN Index;
42 UINTN PathLength;
43
44 PathLength = AsciiStrLen (Path);
45
46 for (Index = 0; Index < PathLength; ++Index) {
47 //
48 // Skip allowed characters (0-9, A-Z, a-z, '_', '-', '.', '/', and '\').
49 //
50 if ( IsAsciiNumber (Path[Index])
51 || IsAsciiAlpha (Path[Index])
52 || (Path[Index] == '_')
53 || (Path[Index] == '-')
54 || (Path[Index] == '.')
55 || (Path[Index] == '/')
56 || (Path[Index] == '\\'))
57 {
58 continue;
59 }
60
61 //
62 // Disallowed characters matched.
63 //
64 return FALSE;
65 }
66
67 return TRUE;
68}
69
70BOOLEAN
72 IN CONST CHAR8 *Comment
73 )
74{
75 UINTN Index;
76 UINTN CommentLength;
77
78 CommentLength = AsciiStrLen (Comment);
79
80 for (Index = 0; Index < CommentLength; ++Index) {
81 //
82 // Unprintable characters matched.
83 //
84 if (IsAsciiPrint (Comment[Index]) == 0) {
85 return FALSE;
86 }
87 }
88
89 return TRUE;
90}
91
92BOOLEAN
94 IN CONST CHAR8 *Identifier,
95 IN BOOLEAN IsKernelIdentifier
96 )
97{
98 UINTN Index;
99 UINTN IdentifierLength;
100
101 if (IsKernelIdentifier) {
102 //
103 // Kernel patch only requires Kernel->Patch->Identifier set to kernel.
104 //
105 if (AsciiStrCmp (Identifier, "kernel") == 0) {
106 return TRUE;
107 }
108 } else {
109 //
110 // Any and Apple are two fixed values accepted by Booter->Patch.
111 // TODO: Drop empty string support in OC.
112 //
113 if ( (AsciiStrCmp (Identifier, "Any") == 0)
114 || (AsciiStrCmp (Identifier, "Apple") == 0))
115 {
116 return TRUE;
117 }
118
119 //
120 // For customised bootloader, it must have .efi suffix.
121 //
122 if (!OcAsciiEndsWith (Identifier, ".efi", TRUE)) {
123 return FALSE;
124 }
125 }
126
127 //
128 // There must be a dot for a sane Identifier.
129 //
130 if (OcAsciiStrChr (Identifier, '.') == NULL) {
131 return FALSE;
132 }
133
134 IdentifierLength = AsciiStrLen (Identifier);
135
136 for (Index = 0; Index < IdentifierLength; ++Index) {
137 //
138 // Skip allowed characters (0-9, A-Z, a-z, '_', '-', and '.').
139 // FIXME: Discuss what exactly is legal for identifiers, or update the allowed list on request.
140 //
141 if ( IsAsciiNumber (Identifier[Index])
142 || IsAsciiAlpha (Identifier[Index])
143 || (Identifier[Index] == '_')
144 || (Identifier[Index] == '-')
145 || (Identifier[Index] == '.'))
146 {
147 continue;
148 }
149
150 //
151 // Disallowed characters matched.
152 //
153 return FALSE;
154 }
155
156 return TRUE;
157}
158
159BOOLEAN
161 IN CONST CHAR8 *Arch,
162 IN BOOLEAN IsKernelArch
163 )
164{
165 //
166 // Special mode for Kernel->Scheme->KernelArch.
167 //
168 if (IsKernelArch) {
169 //
170 // Auto and i386-user32 are two special values allowed in KernelArch.
171 //
172 if ( (AsciiStrCmp (Arch, "Auto") == 0)
173 || (AsciiStrCmp (Arch, "i386-user32") == 0))
174 {
175 return TRUE;
176 }
177 } else {
178 //
179 // Any is only allowed in non-KernelArch mode.
180 //
181 if (AsciiStrCmp (Arch, "Any") == 0) {
182 return TRUE;
183 }
184 }
185
186 //
187 // i386 and x86_64 are allowed in both modes.
188 // TODO: Do not allow empty string in OC.
189 //
190 if ( (AsciiStrCmp (Arch, "i386") != 0)
191 && (AsciiStrCmp (Arch, "x86_64") != 0))
192 {
193 return FALSE;
194 }
195
196 return TRUE;
197}
198
199BOOLEAN
201 IN CONST CHAR8 *Property
202 )
203{
204 //
205 // Like comments, properties can be anything printable.
206 // Calling sanitiser for comments to reduce code duplication.
207 //
208 return AsciiCommentIsLegal (Property);
209}
210
211BOOLEAN
213 IN CONST CHAR8 *Driver,
214 IN CONST UINTN DriverIndex
215 )
216{
217 UINTN Index;
218 UINTN DriverLength;
219
220 DriverLength = AsciiStrLen (Driver);
221 if (DriverLength == 0) {
222 DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path value is missing!\n", DriverIndex));
223 return FALSE;
224 }
225
226 //
227 // If an EFI driver does not have .efi suffix,
228 // then it must be illegal.
229 //
230 if (!OcAsciiEndsWith (Driver, ".efi", TRUE)) {
231 DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path does not end with \"%a\"!\n", DriverIndex, ".efi"));
232 return FALSE;
233 }
234
235 for (Index = 0; Index < DriverLength; ++Index) {
236 //
237 // Skip allowed characters (0-9, A-Z, a-z, '_', '-', '.', '/').
238 //
239 if ( IsAsciiNumber (Driver[Index])
240 || IsAsciiAlpha (Driver[Index])
241 || (Driver[Index] == '_')
242 || (Driver[Index] == '-')
243 || (Driver[Index] == '.')
244 || (Driver[Index] == '/'))
245 {
246 continue;
247 }
248
249 //
250 // Disallowed characters matched.
251 //
252 DEBUG ((DEBUG_WARN, "UEFI->Drivers[%u].Path contains illegal character!\n", DriverIndex));
253 return FALSE;
254 }
255
256 return TRUE;
257}
258
259BOOLEAN
261 IN CONST CHAR8 *AsciiDevicePath
262 )
263{
264 BOOLEAN RetVal;
265 CHAR16 *UnicodeDevicePath;
266 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
267 CHAR16 *TextualDevicePath;
268
269 RetVal = TRUE;
270
271 //
272 // Convert ASCII device path to Unicode format.
273 //
274 UnicodeDevicePath = AsciiStrCopyToUnicode (AsciiDevicePath, 0);
275 if (UnicodeDevicePath != NULL) {
276 //
277 // Firstly, convert Unicode device path to binary.
278 //
279 DevicePath = ConvertTextToDevicePath (UnicodeDevicePath);
280 if (DevicePath != NULL) {
281 //
282 // Secondly, convert binary back to Unicode device path.
283 //
284 TextualDevicePath = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
285 if (TextualDevicePath != NULL) {
286 //
287 // If the results before and after conversion do not match,
288 // then the original device path is borked.
289 //
290 if (OcStriCmp (UnicodeDevicePath, TextualDevicePath) != 0) {
291 DEBUG ((
292 DEBUG_WARN,
293 "Original path: %s\nPath after internal conversion: %s\n",
294 UnicodeDevicePath,
295 TextualDevicePath
296 ));
297 //
298 // Do not return immediately in order to free properly.
299 //
300 RetVal = FALSE;
301 }
302
303 FreePool (TextualDevicePath);
304 }
305
306 FreePool (DevicePath);
307 }
308
309 FreePool (UnicodeDevicePath);
310 }
311
312 return RetVal;
313}
314
315BOOLEAN
317 IN CONST CHAR8 *AsciiGuid
318 )
319{
320 EFI_STATUS Status;
321 GUID Guid;
322
323 Status = AsciiStrToGuid (AsciiGuid, &Guid);
324
325 //
326 // AsciiStrToGuid should never return errors on valid GUID.
327 //
328 return !EFI_ERROR (Status);
329}
330
331BOOLEAN
333 IN CONST VOID *Data,
334 IN CONST VOID *Mask,
335 IN UINTN DataSize,
336 IN UINTN MaskSize
337 )
338{
339 CONST UINT8 *ByteData;
340 CONST UINT8 *ByteMask;
341 UINTN Index;
342
343 if (DataSize != MaskSize) {
344 return FALSE;
345 }
346
347 ByteData = (CONST UINT8 *)Data;
348 ByteMask = (CONST UINT8 *)Mask;
349
350 for (Index = 0; Index < DataSize; ++Index) {
351 //
352 // Mask should only be set when corresponding bits on Data are inactive.
353 //
354 if ((ByteData[Index] & ~ByteMask[Index]) != 0) {
355 return FALSE;
356 }
357 }
358
359 return TRUE;
360}
361
362UINT32
364 IN CONST CHAR8 *PatchSection,
365 IN UINT32 PatchIndex,
366 IN BOOLEAN FindSizeCanBeZero,
367 IN CONST UINT8 *Find,
368 IN UINT32 FindSize,
369 IN CONST UINT8 *Replace,
370 IN UINT32 ReplaceSize,
371 IN CONST UINT8 *Mask,
372 IN UINT32 MaskSize,
373 IN CONST UINT8 *ReplaceMask,
374 IN UINT32 ReplaceMaskSize
375 )
376{
377 UINT32 ErrorCount;
378
379 ErrorCount = 0;
380
381 //
382 // If size of Find cannot be zero and it is different from that of Replace, then error.
383 //
384 if (!FindSizeCanBeZero && (FindSize != ReplaceSize)) {
385 DEBUG ((
386 DEBUG_WARN,
387 "%a[%u] has different Find and Replace size (%u vs %u)!\n",
388 PatchSection,
389 PatchIndex,
390 FindSize,
391 ReplaceSize
392 ));
393 ++ErrorCount;
394 }
395
396 if (MaskSize > 0) {
397 //
398 // If Mask is set, but its size is different from that of Find, then error.
399 //
400 if (MaskSize != FindSize) {
401 DEBUG ((
402 DEBUG_WARN,
403 "%a[%u] has Mask set but its size is different from Find (%u vs %u)!\n",
404 PatchSection,
405 PatchIndex,
406 MaskSize,
407 FindSize
408 ));
409 ++ErrorCount;
410 } else if (!DataHasProperMasking (Find, Mask, FindSize, MaskSize)) {
411 //
412 // If Mask is set without corresponding bits being active for Find, then error.
413 //
414 DEBUG ((
415 DEBUG_WARN,
416 "%a[%u]->Find requires Mask to be active for corresponding bits!\n",
417 PatchSection,
418 PatchIndex
419 ));
420 ++ErrorCount;
421 }
422 }
423
424 if (ReplaceMaskSize > 0) {
425 //
426 // If ReplaceMask is set, but its size is different from that of Replace, then error.
427 //
428 if (ReplaceMaskSize != ReplaceSize) {
429 DEBUG ((
430 DEBUG_WARN,
431 "%a[%u] has ReplaceMask set but its size is different from Replace (%u vs %u)!\n",
432 PatchSection,
433 PatchIndex,
434 ReplaceMaskSize,
435 ReplaceSize
436 ));
437 ++ErrorCount;
438 } else if (!DataHasProperMasking (Replace, ReplaceMask, ReplaceSize, ReplaceMaskSize)) {
439 //
440 // If ReplaceMask is set without corresponding bits being active for Replace, then error.
441 //
442 DEBUG ((
443 DEBUG_WARN,
444 "%a[%u]->Replace requires ReplaceMask to be active for corresponding bits!\n",
445 PatchSection,
446 PatchIndex
447 ));
448 ++ErrorCount;
449 }
450 }
451
452 return ErrorCount;
453}
454
455UINT32
457 IN VOID *First,
458 IN UINTN Number,
459 IN UINTN Size,
460 IN DUPLICATION_CHECK DupChecker
461 )
462{
463 UINT32 ErrorCount;
464 UINTN Index;
465 UINTN Index2;
466 CONST UINT8 *PrimaryEntry;
467 CONST UINT8 *SecondaryEntry;
468
469 ErrorCount = 0;
470
471 for (Index = 0; Index < Number; ++Index) {
472 for (Index2 = Index + 1; Index2 < Number; ++Index2) {
473 //
474 // As First is now being read byte-by-byte after casting to UINT8*,
475 // its next element is now First + Size * Index.
476 //
477 PrimaryEntry = (UINT8 *)First + Size * Index;
478 SecondaryEntry = (UINT8 *)First + Size * Index2;
479 if (DupChecker (PrimaryEntry, SecondaryEntry)) {
480 //
481 // DupChecker prints what is duplicated, and here the index is printed.
482 //
483 DEBUG ((DEBUG_WARN, "at Index %u and %u!\n", Index, Index2));
484 ++ErrorCount;
485 }
486 }
487 }
488
489 return ErrorCount;
490}
491
492BOOLEAN
494 IN CONST CHAR8 *EntrySection,
495 IN CONST CHAR8 *FirstString,
496 IN CONST CHAR8 *SecondString
497 )
498{
499 if (AsciiStrCmp (FirstString, SecondString) == 0) {
500 //
501 // Print duplicated entries whose index will be printed in the parent function (FindArrayDuplication).
502 //
503 DEBUG ((DEBUG_WARN, "%a: %a is duplicated ", EntrySection, FirstString[0] != '\0' ? FirstString : "<empty string>"));
504 return TRUE;
505 }
506
507 return FALSE;
508}
509
510UINT32
512 IN CONST CHAR8 *FuncName,
513 IN UINT32 ErrorCount
514 )
515{
516 if (ErrorCount != 0) {
517 DEBUG ((DEBUG_WARN, "%a returns %u %a!\n", FuncName, ErrorCount, ErrorCount > 1 ? "errors" : "error"));
518 } else {
519 DEBUG ((DEBUG_VERBOSE, "%a returns no errors!\n", FuncName));
520 }
521
522 return ErrorCount;
523}
DMG_SIZE_DEVICE_PATH Size
INTN IsAsciiAlpha(IN CHAR8 Char)
Definition OcAsciiLib.c:49
BOOLEAN IsAsciiNumber(IN CHAR8 Char)
Definition OcAsciiLib.c:78
BOOLEAN IsAsciiPrint(IN CHAR8 Char)
Definition OcAsciiLib.c:33
CHAR8 *EFIAPI OcAsciiStrChr(IN CONST CHAR8 *String, IN CHAR8 Char)
Definition OcAsciiLib.c:369
BOOLEAN EFIAPI OcAsciiEndsWith(IN CONST CHAR8 *String, IN CONST CHAR8 *SearchString, IN BOOLEAN CaseInsensitiveMatch)
Definition OcAsciiLib.c:265
INTN EFIAPI OcStriCmp(IN CONST CHAR16 *FirstString, IN CONST CHAR16 *SecondString)
CHAR16 * AsciiStrCopyToUnicode(IN CONST CHAR8 *String, IN UINTN Length)
Definition OcAsciiLib.c:119
BOOLEAN AsciiDevicePathIsLegal(IN CONST CHAR8 *AsciiDevicePath)
BOOLEAN DataHasProperMasking(IN CONST VOID *Data, IN CONST VOID *Mask, IN UINTN DataSize, IN UINTN MaskSize)
UINT32 ReportError(IN CONST CHAR8 *FuncName, IN UINT32 ErrorCount)
BOOLEAN AsciiArchIsLegal(IN CONST CHAR8 *Arch, IN BOOLEAN IsKernelArch)
UINT32 ValidatePatch(IN CONST CHAR8 *PatchSection, IN UINT32 PatchIndex, IN BOOLEAN FindSizeCanBeZero, IN CONST UINT8 *Find, IN UINT32 FindSize, IN CONST UINT8 *Replace, IN UINT32 ReplaceSize, IN CONST UINT8 *Mask, IN UINT32 MaskSize, IN CONST UINT8 *ReplaceMask, IN UINT32 ReplaceMaskSize)
BOOLEAN StringIsDuplicated(IN CONST CHAR8 *EntrySection, IN CONST CHAR8 *FirstString, IN CONST CHAR8 *SecondString)
UINT32 FindArrayDuplication(IN VOID *First, IN UINTN Number, IN UINTN Size, IN DUPLICATION_CHECK DupChecker)
INT64 GetCurrentTimestamp(VOID)
BOOLEAN AsciiFileSystemPathIsLegal(IN CONST CHAR8 *Path)
BOOLEAN AsciiIdentifierIsLegal(IN CONST CHAR8 *Identifier, IN BOOLEAN IsKernelIdentifier)
BOOLEAN AsciiGuidIsLegal(IN CONST CHAR8 *AsciiGuid)
BOOLEAN AsciiCommentIsLegal(IN CONST CHAR8 *Comment)
BOOLEAN AsciiUefiDriverIsLegal(IN CONST CHAR8 *Driver, IN CONST UINTN DriverIndex)
BOOLEAN AsciiPropertyIsLegal(IN CONST CHAR8 *Property)
BOOLEAN(* DUPLICATION_CHECK)(IN CONST VOID *PrimaryEntry, IN CONST VOID *SecondaryEntry)