1) Remove buffer overflow when the number of Driver Binding Protocols increases in...
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Core / Dxe / Image / ImageFile.c
1 /*++\r
2 \r
3 Copyright (c) 2006, Intel Corporation                                                         \r
4 All rights reserved. This program and the accompanying materials                          \r
5 are licensed and made available under the terms and conditions of the BSD License         \r
6 which accompanies this distribution.  The full text of the license may be found at        \r
7 http://opensource.org/licenses/bsd-license.php                                            \r
8                                                                                           \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
11 \r
12 Module Name:\r
13   \r
14   ImageFile.c\r
15 \r
16 \r
17 Abstract:\r
18 \r
19   \r
20 \r
21 \r
22 Revision History\r
23 \r
24 --*/\r
25 \r
26 #include <DxeMain.h>\r
27 \r
28 EFI_STATUS\r
29 CoreOpenImageFile (\r
30   IN BOOLEAN                        BootPolicy,\r
31   IN VOID                           *SourceBuffer   OPTIONAL,\r
32   IN UINTN                          SourceSize,\r
33   IN EFI_DEVICE_PATH_PROTOCOL       *FilePath,\r
34   OUT EFI_HANDLE                    *DeviceHandle,\r
35   IN IMAGE_FILE_HANDLE              *ImageFileHandle,\r
36   OUT UINT32                        *AuthenticationStatus\r
37   )\r
38 /*++\r
39 \r
40 Routine Description:\r
41 \r
42     Opens a file for (simple) reading.  The simple read abstraction\r
43     will access the file either from a memory copy, from a file\r
44     system interface, or from the load file interface.\r
45 \r
46 Arguments:\r
47 \r
48   BootPolicy    - Policy for Open Image File.\r
49   SourceBuffer  - Pointer to the memory location containing copy\r
50                   of the image to be loaded.\r
51   SourceSize    - The size in bytes of SourceBuffer.\r
52   FilePath      - The specific file path from which the image is loaded\r
53   DeviceHandle  - Pointer to the return device handle.\r
54   ImageFileHandle      - Pointer to the image file handle.\r
55   AuthenticationStatus - Pointer to a caller-allocated UINT32 in which the authentication status is returned. \r
56     \r
57 Returns:\r
58 \r
59     EFI_SUCCESS     - Image file successfully opened.\r
60     \r
61     EFI_LOAD_ERROR  - If the caller passed a copy of the file, and SourceSize is 0.\r
62     \r
63     EFI_INVALID_PARAMETER   - File path is not valid.\r
64     \r
65     EFI_NOT_FOUND   - File not found.\r
66 \r
67 --*/\r
68 {\r
69   EFI_STATUS                        Status;\r
70   EFI_DEVICE_PATH_PROTOCOL          *TempFilePath;\r
71   FILEPATH_DEVICE_PATH              *FilePathNode;\r
72   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FwVolFilePathNode;\r
73   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL   *Volume;\r
74   EFI_FILE_HANDLE                   FileHandle;\r
75   EFI_FILE_HANDLE                   LastHandle;\r
76   EFI_LOAD_FILE_PROTOCOL            *LoadFile;\r
77   EFI_FIRMWARE_VOLUME2_PROTOCOL     *FwVol;\r
78   EFI_SECTION_TYPE                  SectionType;\r
79   UINT8                             *Pe32Buffer;\r
80   UINTN                             Pe32BufferSize;\r
81   EFI_FV_FILETYPE                   Type;\r
82   EFI_FV_FILE_ATTRIBUTES            Attrib;\r
83   EFI_FILE_INFO                     *FileInfo;\r
84   UINTN                             FileInfoSize;\r
85   EFI_GUID                          *NameGuid;\r
86 \r
87   *AuthenticationStatus = 0;\r
88   ZeroMem (ImageFileHandle, sizeof (IMAGE_FILE_HANDLE));\r
89   ImageFileHandle->Signature = IMAGE_FILE_HANDLE_SIGNATURE;\r
90 \r
91   //\r
92   // If the caller passed a copy of the file, then just use it\r
93   //\r
94   if (SourceBuffer != NULL) {\r
95     ImageFileHandle->Source     = SourceBuffer;\r
96     ImageFileHandle->SourceSize = SourceSize;\r
97     *DeviceHandle     = NULL;\r
98     CoreLocateDevicePath (&gEfiDevicePathProtocolGuid, &FilePath, DeviceHandle);\r
99     if (SourceSize > 0) {\r
100       Status = EFI_SUCCESS;\r
101     } else {\r
102       Status = EFI_LOAD_ERROR;\r
103     }\r
104     goto Done;\r
105   }\r
106 \r
107   //\r
108   // Make sure FilePath is valid\r
109   //\r
110   if (FilePath == NULL) {\r
111     return EFI_INVALID_PARAMETER;\r
112   }\r
113 \r
114   //\r
115   // Check to see if it's in a Firmware Volume\r
116   //\r
117   FwVolFilePathNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)FilePath;\r
118   Status = CoreDevicePathToInterface (\r
119             &gEfiFirmwareVolume2ProtocolGuid, \r
120             (EFI_DEVICE_PATH_PROTOCOL **)&FwVolFilePathNode, \r
121             (VOID*)&FwVol, \r
122             DeviceHandle\r
123             );\r
124   if (!EFI_ERROR (Status)) {\r
125     //\r
126     // For FwVol File system there is only a single file name that is a GUID.\r
127     //\r
128     NameGuid = EfiGetNameGuidFromFwVolDevicePathNode (FwVolFilePathNode);\r
129     if (NameGuid != NULL) {\r
130 \r
131       SectionType = EFI_SECTION_PE32;\r
132       Pe32Buffer  = NULL;\r
133       Status = FwVol->ReadSection (\r
134                         FwVol, \r
135                         NameGuid,  \r
136                         SectionType,   \r
137                         0,\r
138                         (VOID **)&Pe32Buffer,\r
139                         &Pe32BufferSize,\r
140                         AuthenticationStatus\r
141                         );\r
142       if (EFI_ERROR (Status)) {\r
143         //\r
144         // Try a raw file, since a PE32 SECTION does not exist\r
145         //\r
146         if (Pe32Buffer != NULL) {\r
147           CoreFreePool (Pe32Buffer);\r
148           *AuthenticationStatus = 0;\r
149         }\r
150         Pe32Buffer = NULL;\r
151         Status = FwVol->ReadFile (\r
152                           FwVol, \r
153                           NameGuid, \r
154                           (VOID **)&Pe32Buffer,\r
155                           &Pe32BufferSize,\r
156                           &Type,\r
157                           &Attrib,\r
158                           AuthenticationStatus\r
159                           );\r
160       }\r
161             \r
162       if (!EFI_ERROR (Status)) {\r
163         //\r
164         // One of the reads passed so we are done\r
165         //\r
166         ImageFileHandle->Source = Pe32Buffer;\r
167         ImageFileHandle->SourceSize = Pe32BufferSize;\r
168         ImageFileHandle->FreeBuffer = TRUE;\r
169         goto Done;\r
170       }\r
171     }\r
172   }\r
173 \r
174   //\r
175   // Attempt to access the file via a file system interface\r
176   //\r
177   FilePathNode = (FILEPATH_DEVICE_PATH *) FilePath;\r
178   Status = CoreDevicePathToInterface (\r
179             &gEfiSimpleFileSystemProtocolGuid, \r
180             (EFI_DEVICE_PATH_PROTOCOL **)&FilePathNode, \r
181             (VOID*)&Volume, \r
182             DeviceHandle\r
183             );\r
184   if (!EFI_ERROR (Status)) {\r
185     //\r
186     // Open the Volume to get the File System handle\r
187     //\r
188     Status = Volume->OpenVolume (Volume, &FileHandle);\r
189     if (!EFI_ERROR (Status)) {\r
190      \r
191       //\r
192       // Parse each MEDIA_FILEPATH_DP node. There may be more than one, since the\r
193       //  directory information and filename can be seperate. The goal is to inch\r
194       //  our way down each device path node and close the previous node\r
195       //\r
196       while (!IsDevicePathEnd (&FilePathNode->Header)) {\r
197         if (DevicePathType (&FilePathNode->Header) != MEDIA_DEVICE_PATH ||\r
198             DevicePathSubType (&FilePathNode->Header) != MEDIA_FILEPATH_DP) {\r
199           Status = EFI_UNSUPPORTED;\r
200         }\r
201 \r
202         if (EFI_ERROR (Status)) {\r
203           //\r
204           // Exit loop on Error\r
205           //\r
206           break;\r
207         }\r
208 \r
209         LastHandle = FileHandle;\r
210         FileHandle = NULL;\r
211         Status = LastHandle->Open (\r
212                               LastHandle,\r
213                               &FileHandle,\r
214                               FilePathNode->PathName,\r
215                               EFI_FILE_MODE_READ,\r
216                               0\r
217                               );\r
218 \r
219         //\r
220         // Close the previous node\r
221         //\r
222         LastHandle->Close (LastHandle);\r
223 \r
224         FilePathNode = (FILEPATH_DEVICE_PATH *) NextDevicePathNode (&FilePathNode->Header);\r
225       }\r
226 \r
227       if (!EFI_ERROR (Status)) {\r
228         //\r
229         // We have found the file. Now we need to read it. Before we can read the file we need to\r
230         // figure out how big the file is.\r
231         //\r
232         FileInfo = NULL;\r
233         FileInfoSize = sizeof (EFI_FILE_INFO);\r
234         while (CoreGrowBuffer (&Status, (VOID **)&FileInfo, FileInfoSize)) {\r
235           //\r
236           // Automatically allocate buffer of the correct size and make the call\r
237           //\r
238           Status = FileHandle->GetInfo (\r
239                                 FileHandle,\r
240                                 &gEfiFileInfoGuid,\r
241                                 &FileInfoSize,\r
242                                 FileInfo                               \r
243                                 );\r
244         }\r
245         if (!EFI_ERROR (Status)) {\r
246           //\r
247           // Allocate space for the file\r
248           //\r
249           ImageFileHandle->Source = CoreAllocateBootServicesPool ((UINTN)FileInfo->FileSize);\r
250           if (ImageFileHandle->Source != NULL) {\r
251             //\r
252             // Read the file into the buffer we allocated\r
253             //\r
254             ImageFileHandle->SourceSize = (UINTN)FileInfo->FileSize;\r
255             ImageFileHandle->FreeBuffer = TRUE;\r
256             Status = FileHandle->Read (FileHandle, &ImageFileHandle->SourceSize, ImageFileHandle->Source);\r
257 \r
258             //\r
259             // Close the file since we are done\r
260             //\r
261             FileHandle->Close (FileHandle);\r
262           } else {\r
263             Status = EFI_OUT_OF_RESOURCES;\r
264           }\r
265 \r
266           goto Done;\r
267         }\r
268       }\r
269     }\r
270   } \r
271 \r
272 \r
273   //\r
274   // Try LoadFile style\r
275   //\r
276 \r
277   TempFilePath = FilePath;\r
278   Status = CoreDevicePathToInterface (\r
279               &gEfiLoadFileProtocolGuid,\r
280               &TempFilePath,\r
281               (VOID*)&LoadFile,\r
282               DeviceHandle\r
283               );\r
284   if (!EFI_ERROR (Status)) {\r
285     //\r
286     // Call LoadFile with the correct buffer size\r
287     //\r
288     while (CoreGrowBuffer (&Status, (VOID **)&ImageFileHandle->Source, ImageFileHandle->SourceSize)) {\r
289       Status = LoadFile->LoadFile (\r
290                            LoadFile,\r
291                            TempFilePath,\r
292                            BootPolicy,\r
293                            &ImageFileHandle->SourceSize,\r
294                            ImageFileHandle->Source\r
295                            );\r
296       //\r
297       // If success or other error happens, stop loop\r
298       //\r
299       if (Status != EFI_BUFFER_TOO_SMALL) {\r
300         break;\r
301       }\r
302     }\r
303 \r
304     if (!EFI_ERROR (Status) || Status == EFI_ALREADY_STARTED) {\r
305       ImageFileHandle->FreeBuffer = TRUE;\r
306       goto Done;\r
307     }\r
308   }\r
309 \r
310   //\r
311   // Nothing else to try\r
312   //\r
313   DEBUG ((EFI_D_LOAD|EFI_D_WARN, "CoreOpenImageFile: Device did not support a known load protocol\n"));\r
314   Status = EFI_NOT_FOUND;\r
315 \r
316 Done:\r
317 \r
318   //\r
319   // If the file was not accessed, clean up\r
320   //\r
321   if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
322     if (ImageFileHandle->FreeBuffer) {\r
323       //\r
324       // Free the source buffer if we allocated it\r
325       //\r
326       CoreFreePool (ImageFileHandle->Source);\r
327     }\r
328   }\r
329 \r
330   return Status;\r
331 }\r
332 \r
333 \r
334 \r
335 EFI_STATUS\r
336 EFIAPI\r
337 CoreReadImageFile (\r
338   IN     VOID    *UserHandle,\r
339   IN     UINTN   Offset,\r
340   IN OUT UINTN   *ReadSize,\r
341   OUT    VOID    *Buffer\r
342   )\r
343 /*++\r
344 \r
345 Routine Description:\r
346 \r
347   Read image file (specified by UserHandle) into user specified buffer with specified offset\r
348   and length.\r
349 \r
350 Arguments:\r
351 \r
352   UserHandle      - Image file handle\r
353   \r
354   Offset          - Offset to the source file\r
355   \r
356   ReadSize        - For input, pointer of size to read;\r
357                     For output, pointer of size actually read.\r
358   \r
359   Buffer          - Buffer to write into\r
360 \r
361 Returns:\r
362 \r
363   EFI_SUCCESS     - Successfully read the specified part of file into buffer.\r
364 \r
365 --*/\r
366 {\r
367   UINTN               EndPosition;\r
368   IMAGE_FILE_HANDLE  *FHand;\r
369 \r
370   FHand = (IMAGE_FILE_HANDLE  *)UserHandle;\r
371   ASSERT (FHand->Signature == IMAGE_FILE_HANDLE_SIGNATURE);\r
372 \r
373   //\r
374   // Move data from our local copy of the file\r
375   //\r
376   EndPosition = Offset + *ReadSize;\r
377   if (EndPosition > FHand->SourceSize) {\r
378     *ReadSize = (UINT32)(FHand->SourceSize - Offset);\r
379   }  \r
380   if (Offset >= FHand->SourceSize) {\r
381       *ReadSize = 0;\r
382   }\r
383 \r
384   CopyMem (Buffer, (CHAR8 *)FHand->Source + Offset, *ReadSize);\r
385   return EFI_SUCCESS;\r
386 }\r
387 \r
388 EFI_STATUS\r
389 CoreDevicePathToInterface (\r
390   IN EFI_GUID                     *Protocol,\r
391   IN EFI_DEVICE_PATH_PROTOCOL     **FilePath,\r
392   OUT VOID                        **Interface,\r
393   OUT EFI_HANDLE                  *Handle\r
394   )\r
395 /*++\r
396 \r
397 Routine Description:\r
398 \r
399   Search a handle to a device on a specified device path that supports a specified protocol,\r
400   interface of that protocol on that handle is another output.\r
401 \r
402 Arguments:\r
403 \r
404   Protocol      - The protocol to search for\r
405   \r
406   FilePath      - The specified device path\r
407   \r
408   Interface     - Interface of the protocol on the handle\r
409   \r
410   Handle        - The handle to the device on the specified device path that supports the protocol.\r
411   \r
412 Returns:\r
413 \r
414   Status code.\r
415 \r
416 --*/\r
417 {\r
418   EFI_STATUS                      Status;\r
419 \r
420   Status = CoreLocateDevicePath (Protocol, FilePath, Handle);\r
421   if (!EFI_ERROR (Status)) {\r
422     Status = CoreHandleProtocol (*Handle, Protocol, Interface);\r
423   }\r
424   return Status;\r
425 }\r
426 \r
427 BOOLEAN\r
428 CoreGrowBuffer (\r
429   IN OUT EFI_STATUS   *Status,\r
430   IN OUT VOID         **Buffer,\r
431   IN UINTN            BufferSize\r
432   )\r
433 /*++\r
434 \r
435 Routine Description:\r
436 \r
437     Helper function called as part of the code needed\r
438     to allocate the proper sized buffer for various \r
439     EFI interfaces.\r
440 \r
441 Arguments:\r
442 \r
443     Status      - Current status\r
444 \r
445     Buffer      - Current allocated buffer, or NULL\r
446 \r
447     BufferSize  - Current buffer size needed\r
448     \r
449 Returns:\r
450     \r
451     TRUE - if the buffer was reallocated and the caller \r
452     should try the API again.\r
453 \r
454     FALSE - buffer could not be allocated and the caller\r
455     should not try the API again.\r
456 \r
457 --*/\r
458 {\r
459   BOOLEAN         TryAgain;\r
460 \r
461   TryAgain = FALSE;\r
462   //\r
463   // If this is an initial request, buffer will be null with a new buffer size\r
464   //\r
465   if (*Buffer == NULL) {\r
466     *Status = EFI_BUFFER_TOO_SMALL;\r
467   }\r
468 \r
469   if (BufferSize == 0) {\r
470     return TRUE;\r
471   }\r
472 \r
473   //\r
474   // If the status code is "buffer too small", resize the buffer\r
475   //\r
476       \r
477   if (*Status == EFI_BUFFER_TOO_SMALL) {\r
478     if (*Buffer != NULL) {\r
479       CoreFreePool (*Buffer);\r
480     }\r
481 \r
482     *Buffer = CoreAllocateBootServicesPool (BufferSize);\r
483     if (*Buffer != NULL) {\r
484       TryAgain = TRUE;\r
485     } else {    \r
486       *Status = EFI_OUT_OF_RESOURCES;\r
487     } \r
488   }\r
489 \r
490   //\r
491   // If there's an error, free the buffer\r
492   //\r
493   if ((!TryAgain) && (EFI_ERROR (*Status)) && (*Buffer)) {\r
494     CoreFreePool (*Buffer);\r
495     *Buffer = NULL;\r
496   }\r
497 \r
498   return TryAgain;\r
499 }\r
500 \r