Skip to content

Instantly share code, notes, and snippets.

@ARISTODE
Last active November 23, 2024 18:44
Show Gist options
  • Select an option

  • Save ARISTODE/28e0f52b3bb7016c3fb937f9627b88af to your computer and use it in GitHub Desktop.

Select an option

Save ARISTODE/28e0f52b3bb7016c3fb937f9627b88af to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <setjmp.h>
// Structure to hold decoder state
struct decoder_state {
struct jpeg_decompress_struct* info;
struct jpeg_error_mgr* err;
unsigned char* image_data;
size_t image_data_size;
unsigned char* back_buffer;
size_t back_buffer_len;
jmp_buf setjmp_buffer; // For error handling
};
// Forward declarations
static void error_exit(j_common_ptr cinfo);
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes);
static boolean fill_input_buffer(j_decompress_ptr cinfo);
static void init_source(j_decompress_ptr cinfo);
static void term_source(j_decompress_ptr cinfo);
// Source manager setup function
static void setup_source_manager(struct jpeg_source_mgr* src) {
src->init_source = init_source;
src->fill_input_buffer = fill_input_buffer;
src->skip_input_data = skip_input_data;
src->resync_to_restart = jpeg_resync_to_restart;
src->term_source = term_source;
src->bytes_in_buffer = 0;
src->next_input_byte = NULL;
}
// Initialize source - called by jpeg_read_header()
static void init_source(j_decompress_ptr cinfo) {
// Vulnerability: No validation before using client_data
struct decoder_state* state = (struct decoder_state*)cinfo->client_data;
if (state) {
state->back_buffer_len = 0;
}
}
// Skip data - called by libjpeg when it wants to skip input data
static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
struct jpeg_source_mgr* src = cinfo->src;
// Vulnerability: No validation of num_bytes from libjpeg
if (num_bytes > 0) {
src->next_input_byte += num_bytes;
src->bytes_in_buffer -= num_bytes;
}
}
// Fill input buffer - called by libjpeg when it needs more data
static boolean fill_input_buffer(j_decompress_ptr cinfo) {
// Vulnerability: No validation of cinfo pointer
struct jpeg_source_mgr* src = cinfo->src;
struct decoder_state* state = (struct decoder_state*)cinfo->client_data;
// Vulnerability: No bounds checking on buffer access
if (state && state->back_buffer_len > 0) {
memcpy(state->back_buffer,
src->next_input_byte,
src->bytes_in_buffer);
}
// Simulate end of input
static const JOCTET EOI_BUFFER[2] = {0xFF, JPEG_EOI};
src->next_input_byte = EOI_BUFFER;
src->bytes_in_buffer = 2;
return TRUE;
}
// Terminate source - called by libjpeg when finishing decompression
static void term_source(j_decompress_ptr cinfo) {
// Nothing to do
}
// Error handler for libjpeg
static void error_exit(j_common_ptr cinfo) {
// Vulnerability: Unsafe error handling using longjmp
struct decoder_state* state = (struct decoder_state*)cinfo->client_data;
if (state) {
longjmp(state->setjmp_buffer, 1);
}
}
// Initialize the decoder
struct decoder_state* init_decoder() {
// Allocate decoder state
struct decoder_state* state = malloc(sizeof(struct decoder_state));
if (!state) return NULL;
// Initialize to zero
memset(state, 0, sizeof(struct decoder_state));
// Allocate and initialize JPEG decompression object and error manager
state->info = malloc(sizeof(struct jpeg_decompress_struct));
state->err = malloc(sizeof(struct jpeg_error_mgr));
if (!state->info || !state->err) {
free(state->info);
free(state->err);
free(state);
return NULL;
}
// Set up error handler
state->info->err = jpeg_std_error(state->err);
state->err->error_exit = error_exit;
// Initialize JPEG decompression object
jpeg_create_decompress(state->info);
// Vulnerability: Store decoder state pointer in libjpeg struct
state->info->client_data = (void*)state;
// Set up source manager
struct jpeg_source_mgr* src = malloc(sizeof(struct jpeg_source_mgr));
if (!src) {
jpeg_destroy_decompress(state->info);
free(state->err);
free(state->info);
free(state);
return NULL;
}
setup_source_manager(src);
state->info->src = src;
// Allocate back buffer for input data
state->back_buffer = malloc(4096); // Fixed size buffer
state->back_buffer_len = 4096;
return state;
}
// Clean up the decoder
void cleanup_decoder(struct decoder_state* state) {
if (!state) return;
if (state->info) {
if (state->info->src) {
free(state->info->src);
}
jpeg_destroy_decompress(state->info);
free(state->info);
}
free(state->err);
free(state->image_data);
free(state->back_buffer);
free(state);
}
// Decode JPEG data
int decode_jpeg(struct decoder_state* state,
const unsigned char* input_data,
size_t input_length) {
if (!state || !input_data) return 0;
// Set up error handling
if (setjmp(state->setjmp_buffer)) {
// Error handling path
return 0;
}
// Set input data
state->info->src->next_input_byte = input_data;
state->info->src->bytes_in_buffer = input_length;
// Read JPEG header
if (jpeg_read_header(state->info, TRUE) != JPEG_HEADER_OK) {
return 0;
}
// Start decompression
jpeg_start_decompress(state->info);
// Allocate output buffer
// Vulnerability: No integer overflow check
size_t row_stride = state->info->output_width *
state->info->output_components;
state->image_data = malloc(row_stride * state->info->output_height);
state->image_data_size = row_stride * state->info->output_height;
if (!state->image_data) {
return 0;
}
// Read scanlines
unsigned char* scanline = state->image_data;
while (state->info->output_scanline < state->info->output_height) {
// Vulnerability: Double fetch and no bounds checking
JSAMPROW row_pointer = scanline;
jpeg_read_scanlines(state->info, &row_pointer, 1);
scanline += row_stride;
}
// Finish decompression
jpeg_finish_decompress(state->info);
return 1;
}
int main() {
struct decoder_state* decoder = init_decoder();
if (!decoder) {
printf("Failed to initialize decoder\n");
return 1;
}
// Vulnerability #1: No size validation on file read
unsigned char buffer[1024*1024];
FILE* f = fopen("test.jpg", "rb");
if (!f) {
cleanup_decoder(decoder);
return 1;
}
size_t bytes_read = fread(buffer, 1, sizeof(buffer), f);
fclose(f);
// Set up error handling
if (setjmp(decoder->setjmp_buffer)) {
printf("Error during decoding\n");
cleanup_decoder(decoder);
return 1;
}
// Set input data
decoder->info->src->next_input_byte = buffer;
decoder->info->src->bytes_in_buffer = bytes_read;
// Vulnerability #2: Using return values without validation
if (jpeg_read_header(decoder->info, TRUE) != JPEG_HEADER_OK) {
cleanup_decoder(decoder);
return 1;
}
jpeg_start_decompress(decoder->info);
// Vulnerability #3: Unchecked arithmetic from libjpeg values
size_t row_stride = decoder->info->output_width *
decoder->info->output_components;
// Vulnerability #4: Using untrusted values for allocation
unsigned char* output_buffer = malloc(row_stride *
decoder->info->output_height);
if (!output_buffer) {
cleanup_decoder(decoder);
return 1;
}
// Vulnerability #5: Double-fetch when reading scanlines
unsigned char* scanline = output_buffer;
while (decoder->info->output_scanline < decoder->info->output_height) {
JSAMPROW row_pointer = scanline;
jpeg_read_scanlines(decoder->info, &row_pointer, 1);
// Vulnerable: output_width could change between iterations
scanline += row_stride;
}
// Vulnerability #6: Direct use of function pointers
if (decoder->info->err->emit_message) {
decoder->info->err->emit_message(
(j_common_ptr)decoder->info,
JMSG_INFO);
}
// Vulnerability #7: Unsafe direct memory copying
if (decoder->back_buffer) {
memcpy(decoder->back_buffer,
decoder->info->src->next_input_byte,
decoder->info->src->bytes_in_buffer);
}
jpeg_finish_decompress(decoder->info);
// Cleanup
free(output_buffer);
cleanup_decoder(decoder);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment