tjko / jpegoptim

jpegoptim - utility to optimize/compress JPEG files
http://www.iki.fi/tjko/projects.html
GNU General Public License v3.0
1.56k stars 116 forks source link

SEGV in jpegoptim.c:631, jpeg_read_header() #168

Open schsiung opened 7 months ago

schsiung commented 7 months ago

Expected behavior and actual behavior.

SEGV_jpegoptim-1.4.7.tar.gz

Expect running without SEGV .

Steps to reproduce the problem.

  1. bin/jpegoptim out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 -o 1.jpg
    
    [AFL++ 4547ba12d0d6] /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/obj # bin/jpegoptim out/def
    ault/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 -o 1.jpg
    out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 AddressSanitizer:DEADLYSIGNAL
    =================================================================
    ==4092299==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x7fc08097ab95 bp 0x7fff8b6b04c0 sp 0x7fff8b6aff20 T0)
    ==4092299==The signal is caused by a READ memory access.
    ==4092299==Hint: this fault was caused by a dereference of a high value address (see register values below).  Disassemble the provided pc to learn which register was used.
    #0 0x7fc08097ab95  (/lib/x86_64-linux-gnu/libjpeg.so.8+0x23b95) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b)
    #1 0x7fc0809824ef  (/lib/x86_64-linux-gnu/libjpeg.so.8+0x2b4ef) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b)
    #2 0x7fc08098123e  (/lib/x86_64-linux-gnu/libjpeg.so.8+0x2a23e) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b)
    #3 0x7fc080974cce in jpeg_consume_input (/lib/x86_64-linux-gnu/libjpeg.so.8+0x1dcce) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b)
    #4 0x7fc080974f21 in jpeg_read_header (/lib/x86_64-linux-gnu/libjpeg.so.8+0x1df21) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b)
    #5 0x56080c4c5466 in main /data/openeuler/jpegoptim/jpegoptim-1.4.7/jpegoptim.c:631:3
    #6 0x7fc080738d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: a43bfc8428df6623cd498c9c0caeb91aec9be4f9)
    #7 0x7fc080738e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: a43bfc8428df6623cd498c9c0caeb91aec9be4f9)
    #8 0x56080c3ff454 in _start (/data/openeuler/jpegoptim/jpegoptim-1.4.7/build/obj/bin/jpegoptim+0x5e454) (BuildId: a3aaafbe2592aad4)

AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libjpeg.so.8+0x23b95) (BuildId: c54abff9294357e28532a76a049a4cb2542fc15b) ==4092299==ABORTING


2.  GDB info  `gdb bin/jpegoptim` 

Reading symbols from bin/jpegoptim... (gdb) break jpegoptim.c:631 Breakpoint 1 at 0x12445a: file jpegoptim.c, line 631. (gdb) break jpeg_read_header Breakpoint 2 at 0x134cf0 (gdb) break jpeg_consume_input Function "jpeg_consume_input" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 3 (jpeg_consume_input) pending. (gdb) run out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 -o 1.jpg Starting program: /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/obj/bin/jpegoptim out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 -o 1.jpg [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 Breakpoint 1, main (argc=4, argv=0x7fffffffe178) at jpegoptim.c:631 631 jpeg_read_header(&dinfo, TRUE); (gdb) p dinfo $1 = {err = 0x7fffffff9900, mem = 0x60f000000040, progress = 0x0, client_data = 0x0, is_decompressor = 1, global_state = 200, src = 0x61d0000002a0, image_width = 0, image_height = 0, num_components = 0, jpeg_color_space = JCS_UNKNOWN, out_color_space = JCS_UNKNOWN, scale_num = 0, scale_denom = 0, output_gamma = 0, buffered_image = 0, raw_data_out = 0, dct_method = JDCT_ISLOW, do_fancy_upsampling = 0, do_block_smoothing = 0, quantize_colors = 0, dither_mode = JDITHER_NONE, two_pass_quantize = 0, desired_number_of_colors = 0, enable_1pass_quant = 0, enable_external_quant = 0, enable_2pass_quant = 0, output_width = 0, output_height = 0, out_color_components = 0, output_components = 0, rec_outbuf_height = 0, actual_number_of_colors = 0, colormap = 0x0, output_scanline = 0, input_scan_number = 0, input_iMCU_row = 0, output_scan_number = 0, output_iMCU_row = 0, coef_bits = 0x0, quant_tbl_ptrs = {0x0, 0x0, 0x0, 0x0}, dc_huff_tbl_ptrs = {0x0, 0x0, 0x0, 0x0}, ac_huff_tbl_ptrs = {0x0, 0x0, 0x0, 0x0}, data_precision = 0, comp_info = 0x0, is_baseline = 0, progressive_mode = 0, arith_code = 0, arith_dc_L = '\000' <repeats 15 times>, arith_dc_U = '\000' <repeats 15 times>, arith_ac_K = '\000' <repeats 15 times>, restart_interval = 0, saw_JFIF_marker = 0, JFIF_major_version = 0 '\000', JFIF_minor_version = 0 '\000', density_unit = 0 '\000', X_density = 0, Y_density = 0, saw_Adobe_marker = 0, Adobe_transform = 0 '\000', CCIR601_sampling = 0, marker_list = 0x0, max_h_samp_factor = 0, max_v_samp_factor = 0, min_DCT_h_scaled_size = 0, min_DCT_v_scaled_size = 0, total_iMCU_rows = 0, sample_range_limit = 0x0, comps_in_scan = 0, cur_comp_info = {0x0, 0x0, 0x0, 0x0}, MCUs_per_row = 0, MCU_rows_in_scan = 0, blocks_in_MCU = 0, MCU_membership = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Ss = 0, Se = 0, Ah = 0, Al = 0, block_size = 0, natural_order = 0x0, lim_Se = 0, unread_marker = 0, master = 0x61d000000200, main = 0x0, coef = 0x0, post = 0x0, inputctl = 0x61d0000001c0, marker = 0x61d0000000a0, entropy = 0x0, idct = 0x0, upsample = 0x0, cconvert = 0x0, cquantize = 0x0} (gdb) n

Breakpoint 2, 0x00007ffff7e6aef0 in jpeg_read_header () from /lib/x86_64-linux-gnu/libjpeg.so.8 (gdb) n Single stepping until exit from function jpeg_read_header, which has no line number information.

Breakpoint 3, 0x00007ffff7e6ac60 in jpeg_consume_input () from /lib/x86_64-linux-gnu/libjpeg.so.8 (gdb) n Single stepping until exit from function jpeg_consume_input, which has no line number information.

Program received signal SIGSEGV, Segmentation fault. 0x00007ffff7e70b95 in ?? () from /lib/x86_64-linux-gnu/libjpeg.so.8 (gdb) bt

0 0x00007ffff7e70b95 in ?? () from /lib/x86_64-linux-gnu/libjpeg.so.8

1 0x00007ffff7e784f0 in ?? () from /lib/x86_64-linux-gnu/libjpeg.so.8

2 0x00007ffff7e7723f in ?? () from /lib/x86_64-linux-gnu/libjpeg.so.8

3 0x00007ffff7e6accf in jpeg_consume_input () from /lib/x86_64-linux-gnu/libjpeg.so.8

4 0x00007ffff7e6af22 in jpeg_read_header () from /lib/x86_64-linux-gnu/libjpeg.so.8

5 0x0000555555678467 in main (argc=4, argv=0x7fffffffe178) at jpegoptim.c:631

(gdb) q


3. I have tried to link a static `libjpeg.a`, it gives  more information:

[AFL++ 4547ba12d0d6] /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/obj # ../../jpegoptim out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 AddressSanitizer:DEADLYSIGNAL

==813934==ERROR: AddressSanitizer: SEGV on unknown address (pc 0x55a710e410ae bp 0x7ffd21d5a550 sp 0x7ffd21d5a4f0 T0) ==813934==The signal is caused by a READ memory access. ==813934==Hint: this fault was caused by a dereference of a high value address (see register values below). Disassemble the provided pc to learn which register was used.

0 0x55a710e410ae in skip_variable /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:876:5

#1 0x55a710e2ae93 in read_markers /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:1107:12
#2 0x55a710e4f721 in consume_markers /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdinput.c:334:9
#3 0x55a710e21052 in jpeg_consume_input /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdapimin.c:320:15
#4 0x55a710e20a0f in jpeg_read_header /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdapimin.c:268:13
#5 0x55a710deca4a in main /data/openeuler/jpegoptim/jpegoptim-1.4.7/jpegoptim.c:631:3
#6 0x7f81f9cf0d8f  (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: a43bfc8428df6623cd498c9c0caeb91aec9be4f9)
#7 0x7f81f9cf0e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: a43bfc8428df6623cd498c9c0caeb91aec9be4f9)
#8 0x55a710d21434 in _start (/data/openeuler/jpegoptim/jpegoptim-1.4.7/jpegoptim+0xa4434) (BuildId: 4d794c3820837ef3)

AddressSanitizer can not provide additional info. SUMMARY: AddressSanitizer: SEGV /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:876:5 in skip_variable ==813934==ABORTING

Reading symbols from ../../jpegoptim... (gdb) run out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 Starting program: /data/openeuler/jpegoptim/jpegoptim-1.4.7/jpegoptim out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". out/default/crashes/id:000000,sig:11,src:000055,time:111839,execs:11762,op:havoc,rep:7 Program received signal SIGSEGV, Segmentation fault. 0x00005555557180ae in skip_variable (cinfo=0x7fffffff91a0) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:876 876 (*cinfo->src->skip_input_data) (cinfo, (long)length); (gdb) bt

0 0x00005555557180ae in skip_variable (cinfo=0x7fffffff91a0) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:876

1 0x0000555555701e94 in read_markers (cinfo=0x7fffffff91a0) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdmarker.c:1107

2 0x0000555555726722 in consume_markers (cinfo=0x7fffffff91a0) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdinput.c:334

3 0x00005555556f8053 in jpeg_consume_input (cinfo=0x7fffffff91a0) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdapimin.c:320

4 0x00005555556f7a10 in jpeg_read_header (cinfo=0x7fffffff91a0, require_image=1) at /data/openeuler/jpegoptim/jpegoptim-1.4.7/build/libjpeg-turbo/jdapimin.c:268

5 0x00005555556c3a4b in main (argc=2, argv=0x7fffffffe1a8) at jpegoptim.c:631

(gdb) p cinfo $1 = {err = 0x7fffffff9940, mem = 0x60f000000040, progress = 0x0, client_data = 0x0, is_decompressor = 1, global_state = 201, src = 0x61c000000260, image_width = 0, image_height = 0, num_components = 0, jpeg_color_space = JCS_UNKNOWN, out_color_space = JCS_UNKNOWN, scale_num = 0, scale_denom = 0, output_gamma = 0, buffered_image = 0, raw_data_out = 0, dct_method = JDCT_ISLOW, do_fancy_upsampling = 0, do_block_smoothing = 0, quantize_colors = 0, dither_mode = JDITHER_NONE, two_pass_quantize = 0, desired_number_of_colors = 0, enable_1pass_quant = 0, enable_external_quant = 0, enable_2pass_quant = 0, output_width = 0, output_height = 0, out_color_components = 0, output_components = 0, rec_outbuf_height = 0, actual_number_of_colors = 0, colormap = 0x0, output_scanline = 0, input_scan_number = 0, input_iMCU_row = 0, output_scan_number = 0, output_iMCU_row = 0, coef_bits = 0x0, quant_tbl_ptrs = {0x61c0000002e8, 0x0, 0x0, 0x0}, dc_huff_tbl_ptrs = {0x0, 0x0, 0x0, 0x0}, ac_huff_tbl_ptrs = {0x0, 0x0, 0x0, 0x0}, data_precision = 8, comp_info = 0x0, is_baseline = 0, progressive_mode = 0, arith_code = 0, arith_dc_L = '\000' <repeats 15 times>, arith_dc_U = '\001' <repeats 16 times>, arith_ac_K = '\005' <repeats 16 times>, restart_interval = 0, saw_JFIF_marker = 0, JFIF_major_version = 1 '\001', JFIF_minor_version = 1 '\001', density_unit = 0 '\000', X_density = 1, Y_density = 1, saw_Adobe_marker = 0, Adobe_transform = 0 '\000', CCIR601_sampling = 0, marker_list = 0x607000000038, max_h_samp_factor = 0, max_v_samp_factor = 0, min_DCT_h_scaled_size = 0, min_DCT_v_scaled_size = 0, total_iMCU_rows = 0, sample_range_limit = 0x0, comps_in_scan = 0, cur_comp_info = {0x0, 0x0, 0x0, 0x0}, MCUs_per_row = 0, MCU_rows_in_scan = 0, blocks_in_MCU = 0, MCU_membership = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, Ss = 0, Se = 0, Ah = 0, Al = 0, block_size = 0, natural_order = 0x0, lim_Se = 0, unread_marker = 220, master = 0x61c0000001d0, main = 0x0, coef = 0x0, post = 0x0, inputctl = 0x61c0000001a0, marker = 0x61c000000098, entropy = 0x0, idct = 0x0, upsample = 0x0, cconvert = 0x0, cquantize = 0x0} (gdb) p cinfo->src $2 = {next_input_byte = 0x621000000178 "\001VZZxix\353\202\202\353", '\377' <repeats 18 times>, "\177\377\377\377\377\377\377\377\377\377\364", '\377' <repeats 26 times>, "\300", bytes_in_buffer = 280, init_source = 0x555555c59a80 , fill_input_buffer = 0x555555c59fd0 , skip_input_data = 0xbebebebebebebebe, resync_to_restart = 0x5555556fbfa0 , term_source = 0x555555c5cb20 } (gdb) p *cinfo->src->skip_input_data Cannot access memory at address 0xbebebebebebebebe


## Operating system

[AFL++ 4547ba12d0d6] /data/openeuler/jpegoptim/jpegoptim-1.4.7/build # uname -a Linux 4547ba12d0d6 5.15.0-91-generic #101-Ubuntu SMP Tue Nov 14 13:30:08 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux [AFL++ 4547ba12d0d6] /data/openeuler/jpegoptim/jpegoptim-1.4.7/build #



##  version

jpegoptim-1.4.7

From: [xiongshengchao@jyhlab.org.cn](mailto:xiongshengchao@jyhlab.org.cn)
tjko commented 7 months ago

This would seem to be issue in libjpeg and not in jpegoptim?

schsiung commented 7 months ago

@tjko thanks, I have reported the issue to libjpeg-turbo, wait for their reply

schsiung commented 7 months ago

referring the comment, https://github.com/libjpeg-turbo/libjpeg-turbo/security/advisories/GHSA-mm58-28hq-3c32#advisory-comment-93367 It will check with the latest jpegoptim version of v1.5.5

schsiung commented 7 months ago

@tjko there are no more SEGV crashes using the same POC with jpegoptim v1.5.5, I suppose the issue was already fixed by https://github.com/tjko/jpegoptim/commit/3401f250ace692a5b23eac1617dc429eb3566f9f It seems to be the same issue with https://github.com/tjko/jpegoptim/issues/107
CVE-2022-32325

tjko commented 7 months ago

@schsiung, yes I think you're correct. I had forgotten about the 'custom' memory manager that jpegoptim uses. It can make it look like there is problem in the libjpeg, when the problem is actually in jpegoptim (jpegsrc.c)...