#include "../spng/spng.h" #include struct buf_state { const uint8_t *data; size_t bytes_left; }; static int stream_write_fn(spng_ctx *ctx, void *user, void *data, size_t length) { struct buf_state *state = (struct buf_state*)user; (void)ctx; if(length > state->bytes_left) return SPNG_IO_EOF; state->bytes_left -= length; if(state->data) { memcpy(data, state->data, length); state->data += length; } return 0; } #define copy_val(val) \ if(size < sizeof(val)) goto err; \ memcpy(&val, data, sizeof(val)); \ data += sizeof(val); \ size -= sizeof(val) #ifdef __cplusplus extern "C" #endif int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { uint8_t params[4]; if(size < sizeof(params)) return 0; memcpy(params, data, sizeof(params)); size -= sizeof(params); data += sizeof(params); //int flags = params[0]; int fmt = params[2] | (params[1] << 8); int stream = params[3] & 1; int progressive = params[3] & 2; //int file_stream = params[3] & 4; int get_buffer = params[3] & 8; int ret; size_t png_size = 0; void *png = NULL; struct buf_state state; unsigned char *img = NULL; size_t img_size; size_t pixel_bits = 0; spng_ctx *ctx = spng_ctx_new(SPNG_CTX_ENCODER); if(ctx == NULL) return 0; spng_set_image_limits(ctx, 200000, 200000); spng_set_chunk_limits(ctx, 4 * 1000 * 1000, 8 * 1000 * 1000); state.data = NULL; state.bytes_left = SIZE_MAX; spng_set_option(ctx, SPNG_IMG_COMPRESSION_LEVEL, 1); if(stream) ret = spng_set_png_stream(ctx, stream_write_fn, &state); else ret = spng_set_option(ctx, SPNG_ENCODE_TO_BUFFER, 1); if(ret) goto err; struct spng_ihdr ihdr; struct spng_plte plte; struct spng_trns trns; struct spng_chrm chrm; struct spng_chrm_int chrm_int; double gamma; struct spng_sbit sbit; uint8_t srgb_rendering_intent; struct spng_bkgd bkgd; struct spng_hist hist; struct spng_phys phys; struct spng_time time; copy_val(ihdr); copy_val(plte); copy_val(trns); copy_val(chrm); copy_val(chrm_int); copy_val(gamma); copy_val(sbit); copy_val(srgb_rendering_intent); copy_val(bkgd); copy_val(hist); copy_val(phys); copy_val(time); if(spng_set_ihdr(ctx, &ihdr)) goto err; spng_set_plte(ctx, &plte); spng_set_trns(ctx, &trns); spng_set_chrm(ctx, &chrm); spng_set_chrm_int(ctx, &chrm_int); spng_set_gama(ctx, gamma); spng_set_sbit(ctx, &sbit); spng_set_srgb(ctx, srgb_rendering_intent); spng_set_bkgd(ctx, &bkgd); spng_set_hist(ctx, &hist); spng_set_phys(ctx, &phys); spng_set_time(ctx, &time); switch(ihdr.color_type) { case SPNG_COLOR_TYPE_GRAYSCALE: case SPNG_COLOR_TYPE_INDEXED: pixel_bits = ihdr.bit_depth; break; case SPNG_COLOR_TYPE_TRUECOLOR: pixel_bits = ihdr.bit_depth * 3; break; case SPNG_COLOR_TYPE_GRAYSCALE_ALPHA: pixel_bits = ihdr.bit_depth * 2; break; case SPNG_COLOR_TYPE_TRUECOLOR_ALPHA: pixel_bits = ihdr.bit_depth * 4; break; default: goto err; } img_size = ihdr.width * pixel_bits + 7; img_size /= 8; img_size *= ihdr.height; if(img_size > size) goto err; if(img_size > 80000000) goto err; img = (unsigned char*)data; if(progressive) { if(spng_encode_image(ctx, NULL, 0, fmt, SPNG_ENCODE_PROGRESSIVE | SPNG_ENCODE_FINALIZE)) goto err; size_t ioffset, img_width = img_size / ihdr.height; struct spng_row_info ri; do { if(spng_get_row_info(ctx, &ri)) break; ioffset = ri.row_num * img_width; }while(!spng_encode_row(ctx, img + ioffset, img_size)); } else if(spng_encode_image(ctx, img, img_size, fmt, SPNG_ENCODE_FINALIZE)) goto err; if(get_buffer) { png = spng_get_png_buffer(ctx, &png_size, &ret); // These are potential vulnerabilities if(png && !png_size) return 1; if(!png && png_size) return 1; if(ret && (png || png_size)) return 1; } err: spng_ctx_free(ctx); free(png); return 0; }