To quote its repo[0]: Wuffs is a memory-safe programming language (and a standard library written in that language) for Wrangling Untrusted File Formats Safely. Wrangling includes parsing, decoding and encoding. It compiles its library, written in its own language, to a C/C++ source file that can then be used independently without needing support for the language. That library is now imported to src/vendorcode/wuffs/. This change modifies our linters to ignore that directory because it's supposed to contain the wuffs compiler's result verbatim. Nigel Tao provided an initial wrapper around wuffs' jpeg decoder that implements our JPEG API. I further changed it a bit regarding data placement, dropped stuff from our API that wasn't ever used, or isn't used anymore, and generally made it fit coreboot a bit better. Features are Nigel's, bugs are mine. This commit also adapts our jpeg fuzz test to work with the modified API. After limiting it to deal only with approximately screen sized inputs, it fuzzed for 25 hours CPU time without a single hang or crash. This is a notable improvement over running the test with our old decoder which crashes within a minute. Finally, I tried the new parser with a pretty-much-random JPEG file I got from the internet, and it just showed it (once the resolution matched), which is also a notable improvement over the old decoder which is very particular about the subset of JPEG it supports. In terms of code size, a QEmu build's ramstage increases from 128060 bytes decompressed (64121 bytes after LZMA) to 172304 bytes decompressed (82734 bytes after LZMA). [0] https://github.com/google/wuffs Change-Id: If8fa7da69da1ad412f27c2c5e882393c7739bc82 Signed-off-by: Patrick Georgi <patrick@coreboot.org> Based-on-work-by: Nigel Tao <nigeltao@golang.org> Reviewed-on: https://review.coreboot.org/c/coreboot/+/78271 Reviewed-by: Paul Menzel <paulepanter@mailbox.org> Reviewed-by: Martin L Roth <gaumless@gmail.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
125 lines
3.4 KiB
C
125 lines
3.4 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
|
|
/*
|
|
* Provide a simple API around the Wuffs JPEG decoder
|
|
* Uses the heap (and lots of it) for the image-size specific
|
|
* work buffer, so ramstage-only.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "jpeg.h"
|
|
|
|
#define WUFFS_CONFIG__AVOID_CPU_ARCH
|
|
#define WUFFS_CONFIG__MODULES
|
|
#define WUFFS_CONFIG__MODULE__BASE
|
|
#define WUFFS_CONFIG__MODULE__JPEG
|
|
#define WUFFS_CONFIG__STATIC_FUNCTIONS
|
|
#define WUFFS_IMPLEMENTATION
|
|
#include "../vendorcode/wuffs/wuffs-v0.4.c"
|
|
|
|
/* ~16K is big enough to move this off the stack */
|
|
static wuffs_jpeg__decoder dec;
|
|
|
|
int jpeg_fetch_size(unsigned char *filedata, size_t filesize, unsigned int *width,
|
|
unsigned int *height)
|
|
{
|
|
if (!width || !height) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__status status = wuffs_jpeg__decoder__initialize(
|
|
&dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__image_config imgcfg;
|
|
wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true);
|
|
status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src);
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
*width = wuffs_base__pixel_config__width(&imgcfg.pixcfg);
|
|
*height = wuffs_base__pixel_config__height(&imgcfg.pixcfg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int jpeg_decode(unsigned char *filedata, size_t filesize, unsigned char *pic,
|
|
unsigned int width, unsigned int height, unsigned int bytes_per_line,
|
|
unsigned int depth)
|
|
{
|
|
if (!filedata || !pic) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
/* Relatively arbitrary limit that shouldn't hurt anybody.
|
|
* 300M (10k*10k*3bytes/pixel) is already larger than our heap, so
|
|
* it's on the safe side.
|
|
* This avoids overflows when width or height are used for
|
|
* calculations in this function.
|
|
*/
|
|
if ((width > 10000) || (height > 10000)) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
uint32_t pixfmt;
|
|
switch (depth) {
|
|
case 16:
|
|
pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR_565;
|
|
break;
|
|
case 24:
|
|
pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGR;
|
|
break;
|
|
case 32:
|
|
pixfmt = WUFFS_BASE__PIXEL_FORMAT__BGRA_NONPREMUL;
|
|
break;
|
|
default:
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__status status = wuffs_jpeg__decoder__initialize(
|
|
&dec, sizeof(dec), WUFFS_VERSION, WUFFS_INITIALIZE__DEFAULT_OPTIONS);
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__image_config imgcfg;
|
|
wuffs_base__io_buffer src = wuffs_base__ptr_u8__reader(filedata, filesize, true);
|
|
status = wuffs_jpeg__decoder__decode_image_config(&dec, &imgcfg, &src);
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__pixel_config pixcfg;
|
|
wuffs_base__pixel_config__set(&pixcfg, pixfmt, 0, width, height);
|
|
|
|
wuffs_base__pixel_buffer pixbuf;
|
|
status = wuffs_base__pixel_buffer__set_interleaved(
|
|
&pixbuf, &pixcfg,
|
|
wuffs_base__make_table_u8(pic, width * (depth / 8), height, bytes_per_line),
|
|
wuffs_base__empty_slice_u8());
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
uint64_t workbuf_len_min_incl = wuffs_jpeg__decoder__workbuf_len(&dec).min_incl;
|
|
uint8_t *workbuf_array = malloc(workbuf_len_min_incl);
|
|
if ((workbuf_array == NULL) && workbuf_len_min_incl) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
wuffs_base__slice_u8 workbuf =
|
|
wuffs_base__make_slice_u8(workbuf_array, workbuf_len_min_incl);
|
|
status = wuffs_jpeg__decoder__decode_frame(&dec, &pixbuf, &src,
|
|
WUFFS_BASE__PIXEL_BLEND__SRC, workbuf, NULL);
|
|
|
|
free(workbuf_array);
|
|
|
|
if (status.repr) {
|
|
return JPEG_DECODE_FAILED;
|
|
}
|
|
|
|
return 0;
|
|
}
|