]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/BaseBmpSupportLib/BmpSupportLib.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Library / BaseBmpSupportLib / BmpSupportLib.c
1 /** @file
2
3 Provides services to convert a BMP graphics image to a GOP BLT buffer and
4 from a GOP BLT buffer to a BMP graphics image.
5
6 Caution: This module requires additional review when modified.
7 This module processes external input - BMP image.
8 This external input must be validated carefully to avoid security issue such
9 as buffer overflow, integer overflow.
10
11 TranslateBmpToGopBlt() receives untrusted input and performs basic validation.
12
13 Copyright (c) 2016-2017, Microsoft Corporation
14 Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
15
16 All rights reserved.
17 SPDX-License-Identifier: BSD-2-Clause-Patent
18
19 **/
20
21 #include <PiDxe.h>
22 #include <Library/DebugLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/SafeIntLib.h>
26 #include <IndustryStandard/Bmp.h>
27
28 #include <Library/BmpSupportLib.h>
29
30 //
31 // BMP Image header for an uncompressed 24-bit per pixel BMP image.
32 //
33 const BMP_IMAGE_HEADER mBmpImageHeaderTemplate = {
34 'B', // CharB
35 'M', // CharM
36 0, // Size will be updated at runtime
37 {0, 0}, // Reserved
38 sizeof (BMP_IMAGE_HEADER), // ImageOffset
39 sizeof (BMP_IMAGE_HEADER) - OFFSET_OF (BMP_IMAGE_HEADER, HeaderSize), // HeaderSize
40 0, // PixelWidth will be updated at runtime
41 0, // PixelHeight will be updated at runtime
42 1, // Planes
43 24, // BitPerPixel
44 0, // CompressionType
45 0, // ImageSize will be updated at runtime
46 0, // XPixelsPerMeter
47 0, // YPixelsPerMeter
48 0, // NumberOfColors
49 0 // ImportantColors
50 };
51
52 /**
53 Translate a *.BMP graphics image to a GOP blt buffer. If a NULL Blt buffer
54 is passed in a GopBlt buffer will be allocated by this routine using
55 EFI_BOOT_SERVICES.AllocatePool(). If a GopBlt buffer is passed in it will be
56 used if it is big enough.
57
58 @param[in] BmpImage Pointer to BMP file.
59 @param[in] BmpImageSize Number of bytes in BmpImage.
60 @param[in, out] GopBlt Buffer containing GOP version of BmpImage.
61 @param[in, out] GopBltSize Size of GopBlt in bytes.
62 @param[out] PixelHeight Height of GopBlt/BmpImage in pixels.
63 @param[out] PixelWidth Width of GopBlt/BmpImage in pixels.
64
65 @retval RETURN_SUCCESS GopBlt and GopBltSize are returned.
66 @retval RETURN_INVALID_PARAMETER BmpImage is NULL.
67 @retval RETURN_INVALID_PARAMETER GopBlt is NULL.
68 @retval RETURN_INVALID_PARAMETER GopBltSize is NULL.
69 @retval RETURN_INVALID_PARAMETER PixelHeight is NULL.
70 @retval RETURN_INVALID_PARAMETER PixelWidth is NULL.
71 @retval RETURN_UNSUPPORTED BmpImage is not a valid *.BMP image.
72 @retval RETURN_BUFFER_TOO_SMALL The passed in GopBlt buffer is not big
73 enough. The required size is returned in
74 GopBltSize.
75 @retval RETURN_OUT_OF_RESOURCES The GopBlt buffer could not be allocated.
76
77 **/
78 RETURN_STATUS
79 EFIAPI
80 TranslateBmpToGopBlt (
81 IN VOID *BmpImage,
82 IN UINTN BmpImageSize,
83 IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **GopBlt,
84 IN OUT UINTN *GopBltSize,
85 OUT UINTN *PixelHeight,
86 OUT UINTN *PixelWidth
87 )
88 {
89 UINT8 *Image;
90 UINT8 *ImageHeader;
91 BMP_IMAGE_HEADER *BmpHeader;
92 BMP_COLOR_MAP *BmpColorMap;
93 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltBuffer;
94 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Blt;
95 UINT32 BltBufferSize;
96 UINTN Index;
97 UINTN Height;
98 UINTN Width;
99 UINTN ImageIndex;
100 UINT32 DataSizePerLine;
101 BOOLEAN IsAllocated;
102 UINT32 ColorMapNum;
103 RETURN_STATUS Status;
104 UINT32 DataSize;
105 UINT32 Temp;
106
107 if (BmpImage == NULL || GopBlt == NULL || GopBltSize == NULL) {
108 return RETURN_INVALID_PARAMETER;
109 }
110 if (PixelHeight == NULL || PixelWidth == NULL) {
111 return RETURN_INVALID_PARAMETER;
112 }
113
114 if (BmpImageSize < sizeof (BMP_IMAGE_HEADER)) {
115 DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpImageSize too small\n"));
116 return RETURN_UNSUPPORTED;
117 }
118
119 BmpHeader = (BMP_IMAGE_HEADER *)BmpImage;
120
121 if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M') {
122 DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpHeader->Char fields incorrect\n"));
123 return RETURN_UNSUPPORTED;
124 }
125
126 //
127 // Doesn't support compress.
128 //
129 if (BmpHeader->CompressionType != 0) {
130 DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: Compression Type unsupported.\n"));
131 return RETURN_UNSUPPORTED;
132 }
133
134 if ((BmpHeader->PixelHeight == 0) || (BmpHeader->PixelWidth == 0)) {
135 DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: BmpHeader->PixelHeight or BmpHeader->PixelWidth is 0.\n"));
136 return RETURN_UNSUPPORTED;
137 }
138
139 //
140 // Only support BITMAPINFOHEADER format.
141 // BITMAPFILEHEADER + BITMAPINFOHEADER = BMP_IMAGE_HEADER
142 //
143 if (BmpHeader->HeaderSize != sizeof (BMP_IMAGE_HEADER) - OFFSET_OF (BMP_IMAGE_HEADER, HeaderSize)) {
144 DEBUG ((
145 DEBUG_ERROR,
146 "TranslateBmpToGopBlt: BmpHeader->Headership is not as expected. Headersize is 0x%x\n",
147 BmpHeader->HeaderSize
148 ));
149 return RETURN_UNSUPPORTED;
150 }
151
152 //
153 // The data size in each line must be 4 byte alignment.
154 //
155 Status = SafeUint32Mult (
156 BmpHeader->PixelWidth,
157 BmpHeader->BitPerPixel,
158 &DataSizePerLine
159 );
160 if (EFI_ERROR (Status)) {
161 DEBUG ((
162 DEBUG_ERROR,
163 "TranslateBmpToGopBlt: invalid BmpImage... PixelWidth:0x%x BitPerPixel:0x%x\n",
164 BmpHeader->PixelWidth,
165 BmpHeader->BitPerPixel
166 ));
167 return RETURN_UNSUPPORTED;
168 }
169
170 Status = SafeUint32Add (DataSizePerLine, 31, &DataSizePerLine);
171 if (EFI_ERROR (Status)) {
172 DEBUG ((
173 DEBUG_ERROR,
174 "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x\n",
175 DataSizePerLine
176 ));
177
178 return RETURN_UNSUPPORTED;
179 }
180
181 DataSizePerLine = (DataSizePerLine >> 3) &(~0x3);
182 Status = SafeUint32Mult (
183 DataSizePerLine,
184 BmpHeader->PixelHeight,
185 &BltBufferSize
186 );
187
188 if (EFI_ERROR (Status)) {
189 DEBUG ((
190 DEBUG_ERROR,
191 "TranslateBmpToGopBlt: invalid BmpImage... DataSizePerLine:0x%x PixelHeight:0x%x\n",
192 DataSizePerLine, BmpHeader->PixelHeight
193 ));
194
195 return RETURN_UNSUPPORTED;
196 }
197
198 Status = SafeUint32Mult (
199 BmpHeader->PixelHeight,
200 DataSizePerLine,
201 &DataSize
202 );
203
204 if (EFI_ERROR (Status)) {
205 DEBUG ((
206 DEBUG_ERROR,
207 "TranslateBmpToGopBlt: invalid BmpImage... PixelHeight:0x%x DataSizePerLine:0x%x\n",
208 BmpHeader->PixelHeight, DataSizePerLine
209 ));
210
211 return RETURN_UNSUPPORTED;
212 }
213
214 if ((BmpHeader->Size != BmpImageSize) ||
215 (BmpHeader->Size < BmpHeader->ImageOffset) ||
216 (BmpHeader->Size - BmpHeader->ImageOffset != DataSize)) {
217
218 DEBUG ((DEBUG_ERROR, "TranslateBmpToGopBlt: invalid BmpImage... \n"));
219 DEBUG ((DEBUG_ERROR, " BmpHeader->Size: 0x%x\n", BmpHeader->Size));
220 DEBUG ((DEBUG_ERROR, " BmpHeader->ImageOffset: 0x%x\n", BmpHeader->ImageOffset));
221 DEBUG ((DEBUG_ERROR, " BmpImageSize: 0x%lx\n", (UINTN)BmpImageSize));
222 DEBUG ((DEBUG_ERROR, " DataSize: 0x%lx\n", (UINTN)DataSize));
223
224 return RETURN_UNSUPPORTED;
225 }
226
227 //
228 // Calculate Color Map offset in the image.
229 //
230 Image = BmpImage;
231 BmpColorMap = (BMP_COLOR_MAP *)(Image + sizeof (BMP_IMAGE_HEADER));
232 if (BmpHeader->ImageOffset < sizeof (BMP_IMAGE_HEADER)) {
233 return RETURN_UNSUPPORTED;
234 }
235
236 if (BmpHeader->ImageOffset > sizeof (BMP_IMAGE_HEADER)) {
237 switch (BmpHeader->BitPerPixel) {
238 case 1:
239 ColorMapNum = 2;
240 break;
241 case 4:
242 ColorMapNum = 16;
243 break;
244 case 8:
245 ColorMapNum = 256;
246 break;
247 default:
248 ColorMapNum = 0;
249 break;
250 }
251 //
252 // BMP file may has padding data between the bmp header section and the
253 // bmp data section.
254 //
255 if (BmpHeader->ImageOffset - sizeof (BMP_IMAGE_HEADER) < sizeof (BMP_COLOR_MAP) * ColorMapNum) {
256 return RETURN_UNSUPPORTED;
257 }
258 }
259
260 //
261 // Calculate graphics image data address in the image
262 //
263 Image = ((UINT8 *)BmpImage) + BmpHeader->ImageOffset;
264 ImageHeader = Image;
265
266 //
267 // Calculate the BltBuffer needed size.
268 //
269 Status = SafeUint32Mult (
270 BmpHeader->PixelWidth,
271 BmpHeader->PixelHeight,
272 &BltBufferSize
273 );
274
275 if (EFI_ERROR (Status)) {
276 DEBUG ((
277 DEBUG_ERROR,
278 "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth:0x%x PixelHeight:0x%x\n",
279 BmpHeader->PixelWidth, BmpHeader->PixelHeight
280 ));
281
282 return RETURN_UNSUPPORTED;
283 }
284
285 Temp = BltBufferSize;
286 Status = SafeUint32Mult (
287 BltBufferSize,
288 sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL),
289 &BltBufferSize
290 );
291
292 if (EFI_ERROR (Status)) {
293 DEBUG ((
294 DEBUG_ERROR,
295 "TranslateBmpToGopBlt: invalid BltBuffer needed size... PixelWidth x PixelHeight:0x%x struct size:0x%x\n",
296 Temp, sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL)
297 ));
298
299 return RETURN_UNSUPPORTED;
300 }
301
302 IsAllocated = FALSE;
303 if (*GopBlt == NULL) {
304 //
305 // GopBlt is not allocated by caller.
306 //
307 DEBUG ((DEBUG_INFO, "Bmp Support: Allocating 0x%X bytes of memory\n", BltBufferSize));
308 *GopBltSize = (UINTN)BltBufferSize;
309 *GopBlt = AllocatePool (*GopBltSize);
310 IsAllocated = TRUE;
311 if (*GopBlt == NULL) {
312 return RETURN_OUT_OF_RESOURCES;
313 }
314 } else {
315 //
316 // GopBlt has been allocated by caller.
317 //
318 if (*GopBltSize < (UINTN)BltBufferSize) {
319 *GopBltSize = (UINTN)BltBufferSize;
320 return RETURN_BUFFER_TOO_SMALL;
321 }
322 }
323
324 *PixelWidth = BmpHeader->PixelWidth;
325 *PixelHeight = BmpHeader->PixelHeight;
326 DEBUG ((DEBUG_INFO, "BmpHeader->ImageOffset 0x%X\n", BmpHeader->ImageOffset));
327 DEBUG ((DEBUG_INFO, "BmpHeader->PixelWidth 0x%X\n", BmpHeader->PixelWidth));
328 DEBUG ((DEBUG_INFO, "BmpHeader->PixelHeight 0x%X\n", BmpHeader->PixelHeight));
329 DEBUG ((DEBUG_INFO, "BmpHeader->BitPerPixel 0x%X\n", BmpHeader->BitPerPixel));
330 DEBUG ((DEBUG_INFO, "BmpHeader->ImageSize 0x%X\n", BmpHeader->ImageSize));
331 DEBUG ((DEBUG_INFO, "BmpHeader->HeaderSize 0x%X\n", BmpHeader->HeaderSize));
332 DEBUG ((DEBUG_INFO, "BmpHeader->Size 0x%X\n", BmpHeader->Size));
333
334 //
335 // Translate image from BMP to Blt buffer format
336 //
337 BltBuffer = *GopBlt;
338 for (Height = 0; Height < BmpHeader->PixelHeight; Height++) {
339 Blt = &BltBuffer[ (BmpHeader->PixelHeight - Height - 1) * BmpHeader->PixelWidth];
340 for (Width = 0; Width < BmpHeader->PixelWidth; Width++, Image++, Blt++) {
341 switch (BmpHeader->BitPerPixel) {
342 case 1:
343 //
344 // Translate 1-bit (2 colors) BMP to 24-bit color
345 //
346 for (Index = 0; Index < 8 && Width < BmpHeader->PixelWidth; Index++) {
347 Blt->Red = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Red;
348 Blt->Green = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Green;
349 Blt->Blue = BmpColorMap[ ((*Image) >> (7 - Index)) & 0x1].Blue;
350 Blt++;
351 Width++;
352 }
353
354 Blt--;
355 Width--;
356 break;
357
358 case 4:
359 //
360 // Translate 4-bit (16 colors) BMP Palette to 24-bit color
361 //
362 Index = (*Image) >> 4;
363 Blt->Red = BmpColorMap[Index].Red;
364 Blt->Green = BmpColorMap[Index].Green;
365 Blt->Blue = BmpColorMap[Index].Blue;
366 if (Width < (BmpHeader->PixelWidth - 1)) {
367 Blt++;
368 Width++;
369 Index = (*Image) & 0x0f;
370 Blt->Red = BmpColorMap[Index].Red;
371 Blt->Green = BmpColorMap[Index].Green;
372 Blt->Blue = BmpColorMap[Index].Blue;
373 }
374 break;
375
376 case 8:
377 //
378 // Translate 8-bit (256 colors) BMP Palette to 24-bit color
379 //
380 Blt->Red = BmpColorMap[*Image].Red;
381 Blt->Green = BmpColorMap[*Image].Green;
382 Blt->Blue = BmpColorMap[*Image].Blue;
383 break;
384
385 case 24:
386 //
387 // It is 24-bit BMP.
388 //
389 Blt->Blue = *Image++;
390 Blt->Green = *Image++;
391 Blt->Red = *Image;
392 break;
393
394 case 32:
395 //
396 //Conver 32 bit to 24bit bmp - just ignore the final byte of each pixel
397 Blt->Blue = *Image++;
398 Blt->Green = *Image++;
399 Blt->Red = *Image++;
400 break;
401
402 default:
403 //
404 // Other bit format BMP is not supported.
405 //
406 if (IsAllocated) {
407 FreePool (*GopBlt);
408 *GopBlt = NULL;
409 }
410 DEBUG ((DEBUG_ERROR, "Bmp Bit format not supported. 0x%X\n", BmpHeader->BitPerPixel));
411 return RETURN_UNSUPPORTED;
412 break;
413 };
414
415 }
416
417 ImageIndex = (UINTN)Image - (UINTN)ImageHeader;
418 if ((ImageIndex % 4) != 0) {
419 //
420 // Bmp Image starts each row on a 32-bit boundary!
421 //
422 Image = Image + (4 - (ImageIndex % 4));
423 }
424 }
425
426 return RETURN_SUCCESS;
427 }
428
429 /**
430 Translate a GOP blt buffer to an uncompressed 24-bit per pixel BMP graphics
431 image. If a NULL BmpImage is passed in a BmpImage buffer will be allocated by
432 this routine using EFI_BOOT_SERVICES.AllocatePool(). If a BmpImage buffer is
433 passed in it will be used if it is big enough.
434
435 @param [in] GopBlt Pointer to GOP blt buffer.
436 @param [in] PixelHeight Height of GopBlt/BmpImage in pixels.
437 @param [in] PixelWidth Width of GopBlt/BmpImage in pixels.
438 @param [in, out] BmpImage Buffer containing BMP version of GopBlt.
439 @param [in, out] BmpImageSize Size of BmpImage in bytes.
440
441 @retval RETURN_SUCCESS BmpImage and BmpImageSize are returned.
442 @retval RETURN_INVALID_PARAMETER GopBlt is NULL.
443 @retval RETURN_INVALID_PARAMETER BmpImage is NULL.
444 @retval RETURN_INVALID_PARAMETER BmpImageSize is NULL.
445 @retval RETURN_UNSUPPORTED GopBlt cannot be converted to a *.BMP image.
446 @retval RETURN_BUFFER_TOO_SMALL The passed in BmpImage buffer is not big
447 enough. The required size is returned in
448 BmpImageSize.
449 @retval RETURN_OUT_OF_RESOURCES The BmpImage buffer could not be allocated.
450
451 **/
452 RETURN_STATUS
453 EFIAPI
454 TranslateGopBltToBmp (
455 IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GopBlt,
456 IN UINT32 PixelHeight,
457 IN UINT32 PixelWidth,
458 IN OUT VOID **BmpImage,
459 IN OUT UINT32 *BmpImageSize
460 )
461 {
462 RETURN_STATUS Status;
463 UINT32 PaddingSize;
464 UINT32 BmpSize;
465 BMP_IMAGE_HEADER *BmpImageHeader;
466 UINT8 *Image;
467 UINTN Col;
468 UINTN Row;
469 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *BltPixel;
470
471 if (GopBlt == NULL || BmpImage == NULL || BmpImageSize == NULL) {
472 return RETURN_INVALID_PARAMETER;
473 }
474
475 if ((PixelHeight == 0) || (PixelWidth == 0)) {
476 return RETURN_UNSUPPORTED;
477 }
478
479 //
480 // Allocate memory for BMP file.
481 //
482 PaddingSize = PixelWidth & 0x3;
483
484 //
485 // First check PixelWidth * 3 + PaddingSize doesn't overflow
486 //
487 Status = SafeUint32Mult (PixelWidth, 3, &BmpSize);
488 if (EFI_ERROR (Status)) {
489 DEBUG ((
490 DEBUG_ERROR,
491 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
492 PixelHeight,
493 PixelWidth
494 ));
495 return RETURN_UNSUPPORTED;
496 }
497 Status = SafeUint32Add (BmpSize, PaddingSize, &BmpSize);
498 if (EFI_ERROR (Status)) {
499 DEBUG ((
500 DEBUG_ERROR,
501 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
502 PixelHeight,
503 PixelWidth
504 ));
505 return RETURN_UNSUPPORTED;
506 }
507
508 //
509 // Second check (mLogoWidth * 3 + PaddingSize) * mLogoHeight + sizeof (BMP_IMAGE_HEADER) doesn't overflow
510 //
511 Status = SafeUint32Mult (BmpSize, PixelHeight, &BmpSize);
512 if (EFI_ERROR (Status)) {
513 DEBUG ((
514 DEBUG_ERROR,
515 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
516 PixelHeight,
517 PixelWidth
518 ));
519 return RETURN_UNSUPPORTED;
520 }
521 Status = SafeUint32Add (BmpSize, sizeof (BMP_IMAGE_HEADER), &BmpSize);
522 if (EFI_ERROR (Status)) {
523 DEBUG ((
524 DEBUG_ERROR,
525 "TranslateGopBltToBmp: GopBlt is too large. PixelHeight:0x%x PixelWidth:0x%x\n",
526 PixelHeight,
527 PixelWidth
528 ));
529 return RETURN_UNSUPPORTED;
530 }
531
532 //
533 // The image should be stored in EfiBootServicesData, allowing the system to
534 // reclaim the memory
535 //
536 if (*BmpImage == NULL) {
537 *BmpImage = AllocateZeroPool (BmpSize);
538 if (*BmpImage == NULL) {
539 return EFI_OUT_OF_RESOURCES;
540 }
541 *BmpImageSize = BmpSize;
542 } else if (*BmpImageSize < BmpSize) {
543 *BmpImageSize = BmpSize;
544 return RETURN_BUFFER_TOO_SMALL;
545 }
546
547 BmpImageHeader = (BMP_IMAGE_HEADER *)*BmpImage;
548 CopyMem (BmpImageHeader, &mBmpImageHeaderTemplate, sizeof (BMP_IMAGE_HEADER));
549 BmpImageHeader->Size = *BmpImageSize;
550 BmpImageHeader->ImageSize = *BmpImageSize - sizeof (BMP_IMAGE_HEADER);
551 BmpImageHeader->PixelWidth = PixelWidth;
552 BmpImageHeader->PixelHeight = PixelHeight;
553
554 //
555 // Convert BLT buffer to BMP file.
556 //
557 Image = (UINT8 *)BmpImageHeader + sizeof (BMP_IMAGE_HEADER);
558 for (Row = 0; Row < PixelHeight; Row++) {
559 BltPixel = &GopBlt[(PixelHeight - Row - 1) * PixelWidth];
560
561 for (Col = 0; Col < PixelWidth; Col++) {
562 *Image++ = BltPixel->Blue;
563 *Image++ = BltPixel->Green;
564 *Image++ = BltPixel->Red;
565 BltPixel++;
566 }
567
568 //
569 // Padding for 4 byte alignment.
570 //
571 Image += PaddingSize;
572 }
573
574 return RETURN_SUCCESS;
575 }