Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack buffer overflow in SAX parser #195

Closed
clod81 opened this issue Oct 29, 2017 · 18 comments
Closed

Stack buffer overflow in SAX parser #195

clod81 opened this issue Oct 29, 2017 · 18 comments

Comments

@clod81
Copy link

clod81 commented Oct 29, 2017

Tested on ruby-2.4.2

POC crash (test.rb):

require 'stringio'
require 'ox'

class Sample < ::Ox::Sax
  def start_element(name); puts "start: #{name}";        end
  def end_element(name);   puts "end: #{name}";          end
  def attr(name, value);   puts "  #{name} => #{value}"; end
  def text(value);         puts "text #{value}";         end
end

File.open(ARGV[0]) do |f|
  io = StringIO.new(f.read)
  handler = Sample.new()
  Ox.sax_parse(handler, io)
end

Input file that causes the crash: https://drive.google.com/open?id=0B5Au7ViXBlHbVG8yUDBtdHJzNlU

Launch:

ruby test.rb ox_sax_crash

Output:

fuzz.rb:14: [BUG] Segmentation fault at 0x41414141
ruby 2.4.2p198 (2017-09-14 revision 59899) [x86_64-linux]

-- Control frame information -----------------------------------------------
c:0005 p:---- s:0023 e:000022 CFUNC  :sax_parse
c:0004 p:0051 s:0017 e:000016 BLOCK  fuzz.rb:14 [FINISH]
c:0003 p:---- s:0011 e:000010 CFUNC  :open
c:0002 p:0063 s:0006 e:000005 EVAL   fuzz.rb:11 [FINISH]
c:0001 p:0000 s:0003 E:0020b0 (none) [FINISH]

-- Ruby level backtrace information ----------------------------------------
fuzz.rb:11:in `<main>'
fuzz.rb:11:in `open'
fuzz.rb:14:in `block in <main>'
fuzz.rb:14:in `sax_parse'

-- Machine register context ------------------------------------------------
  GS: 0x00000063  FS: 0x00000000  ES: 0x0000002b  DS: 0x0000002b EDI: 0x41414141
 ESI: 0xffd2a1f0 EBP: 0x00000405 ESP: 0xffd297a0 EBX: 0xf6bd8000 EDX: 0xffd2a1f0
 ECX: 0x9459c100 EAX: 0x00000000 TRA: 0x0000000e ERR: 0x00000004 EIP: 0xf6bc2228
  CS: 0x00000023 EFL: 0x00010246 UES: 0xffd297a0  SS: 0x0000002b

-- C level backtrace information -------------------------------------------
/usr/local/lib/libruby.so.2.4(rb_vm_bugreport+0x45b) [0xf766208b] vm_dump.c:684
/lib/i386-linux-gnu/libc.so.6 [0xf6e28ac0]
/lib/i386-linux-gnu/libc.so.6 [0xf6e289a7]

-- Other runtime information -----------------------------------------------

* Loaded script: fuzz.rb

* Loaded features:

    0 enumerator.so
    1 thread.rb
    2 rational.so
    3 complex.so
    4 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/encdb.so
    5 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/trans/transdb.so
    6 /usr/local/lib/ruby/2.4.0/unicode_normalize.rb
    7 /usr/local/lib/ruby/2.4.0/x86_64-linux/rbconfig.rb
    8 /usr/local/lib/ruby/2.4.0/rubygems/compatibility.rb
    9 /usr/local/lib/ruby/2.4.0/rubygems/defaults.rb
   10 /usr/local/lib/ruby/2.4.0/rubygems/deprecate.rb
   11 /usr/local/lib/ruby/2.4.0/rubygems/errors.rb
   12 /usr/local/lib/ruby/2.4.0/rubygems/version.rb
   13 /usr/local/lib/ruby/2.4.0/rubygems/requirement.rb
   14 /usr/local/lib/ruby/2.4.0/rubygems/platform.rb
   15 /usr/local/lib/ruby/2.4.0/rubygems/basic_specification.rb
   16 /usr/local/lib/ruby/2.4.0/rubygems/stub_specification.rb
   17 /usr/local/lib/ruby/2.4.0/rubygems/util/list.rb
   18 /usr/local/lib/ruby/2.4.0/x86_64-linux/stringio.so
   19 /usr/local/lib/ruby/2.4.0/rubygems/specification.rb
   20 /usr/local/lib/ruby/2.4.0/rubygems/exceptions.rb
   21 /usr/local/lib/ruby/2.4.0/rubygems/dependency.rb
   22 /usr/local/lib/ruby/2.4.0/rubygems/core_ext/kernel_gem.rb
   23 /usr/local/lib/ruby/2.4.0/monitor.rb
   24 /usr/local/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb
   25 /usr/local/lib/ruby/2.4.0/rubygems.rb
   26 /usr/local/lib/ruby/2.4.0/rubygems/path_support.rb
   27 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/version.rb
   28 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/core_ext/name_error.rb
   29 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/levenshtein.rb
   30 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/jaro_winkler.rb
   31 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checker.rb
   32 /usr/local/lib/ruby/2.4.0/delegate.rb
   33 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
   34 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
   35 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checkers/name_error_checkers.rb
   36 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checkers/method_name_checker.rb
   37 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/spell_checkers/null_checker.rb
   38 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean/formatter.rb
   39 /usr/local/lib/ruby/gems/2.4.0/gems/did_you_mean-1.1.0/lib/did_you_mean.rb
   40 /usr/local/bundle/gems/ox-2.8.1/lib/ox/version.rb
   41 /usr/local/bundle/gems/ox-2.8.1/lib/ox/error.rb
   42 /usr/local/bundle/gems/ox-2.8.1/lib/ox/hasattrs.rb
   43 /usr/local/bundle/gems/ox-2.8.1/lib/ox/node.rb
   44 /usr/local/bundle/gems/ox-2.8.1/lib/ox/comment.rb
   45 /usr/local/bundle/gems/ox-2.8.1/lib/ox/raw.rb
   46 /usr/local/bundle/gems/ox-2.8.1/lib/ox/instruct.rb
   47 /usr/local/bundle/gems/ox-2.8.1/lib/ox/cdata.rb
   48 /usr/local/bundle/gems/ox-2.8.1/lib/ox/doctype.rb
   49 /usr/local/bundle/gems/ox-2.8.1/lib/ox/element.rb
   50 /usr/local/bundle/gems/ox-2.8.1/lib/ox/document.rb
   51 /usr/local/bundle/gems/ox-2.8.1/lib/ox/bag.rb
   52 /usr/local/bundle/gems/ox-2.8.1/lib/ox/sax.rb
   53 /usr/local/lib/ruby/2.4.0/x86_64-linux/date_core.so
   54 /usr/local/lib/ruby/2.4.0/date.rb
   55 /usr/local/lib/ruby/2.4.0/time.rb
   56 /usr/local/lib/ruby/2.4.0/x86_64-linux/bigdecimal.so
   57 /usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so
   58 /usr/local/bundle/gems/ox-2.8.1/lib/ox.rb

* Process memory map:

08048000-08049000 r-xp 00000000 00:34 74                                 /usr/local/bin/ruby
08049000-0804a000 r--p 00000000 00:34 74                                 /usr/local/bin/ruby
0804a000-0804b000 rw-p 00001000 00:34 74                                 /usr/local/bin/ruby
09ef9000-0a15c000 rw-p 00000000 00:00 0                                  [heap]
f5ab7000-f5c66000 r--s 00000000 00:34 42                                 /lib/i386-linux-gnu/libc-2.19.so
f5c66000-f6a24000 r--s 00000000 00:34 77                                 /usr/local/lib/libruby.so.2.4.2
f6a24000-f6a40000 r-xp 00000000 00:34 561                                /lib/i386-linux-gnu/libgcc_s.so.1
f6a40000-f6a41000 rw-p 0001b000 00:34 561                                /lib/i386-linux-gnu/libgcc_s.so.1
f6a43000-f6a46000 r--s 00000000 00:34 74                                 /usr/local/bin/ruby
f6a46000-f6a8d000 r-xp 00000000 00:34 1021                               /usr/local/lib/ruby/2.4.0/x86_64-linux/bigdecimal.so
f6a8d000-f6a8e000 r--p 00046000 00:34 1021                               /usr/local/lib/ruby/2.4.0/x86_64-linux/bigdecimal.so
f6a8e000-f6a8f000 rw-p 00047000 00:34 1021                               /usr/local/lib/ruby/2.4.0/x86_64-linux/bigdecimal.so
f6a8f000-f6b62000 r-xp 00000000 00:34 228                                /usr/local/lib/ruby/2.4.0/x86_64-linux/date_core.so
f6b62000-f6b63000 r--p 000d2000 00:34 228                                /usr/local/lib/ruby/2.4.0/x86_64-linux/date_core.so
f6b63000-f6b64000 rw-p 000d3000 00:34 228                                /usr/local/lib/ruby/2.4.0/x86_64-linux/date_core.so
f6b64000-f6bd7000 r-xp 00000000 00:34 999                                /usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so
f6bd7000-f6bd8000 r--p 00072000 00:34 999                                /usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so
f6bd8000-f6bd9000 rw-p 00073000 00:34 999                                /usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so
f6bd9000-f6bee000 r-xp 00000000 00:34 113                                /usr/local/lib/ruby/2.4.0/x86_64-linux/stringio.so
f6bee000-f6bef000 r--p 00014000 00:34 113                                /usr/local/lib/ruby/2.4.0/x86_64-linux/stringio.so
f6bef000-f6bf0000 rw-p 00015000 00:34 113                                /usr/local/lib/ruby/2.4.0/x86_64-linux/stringio.so
f6bf0000-f6bf2000 r-xp 00000000 00:34 96                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/trans/transdb.so
f6bf2000-f6bf3000 r--p 00002000 00:34 96                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/trans/transdb.so
f6bf3000-f6bf4000 rw-p 00003000 00:34 96                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/trans/transdb.so
f6bf4000-f6bf6000 r-xp 00000000 00:34 94                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/encdb.so
f6bf6000-f6bf7000 r--p 00001000 00:34 94                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/encdb.so
f6bf7000-f6bf8000 rw-p 00002000 00:34 94                                 /usr/local/lib/ruby/2.4.0/x86_64-linux/enc/encdb.so
f6bf8000-f6c7b000 rw-p 00000000 00:00 0 
f6c7b000-f6e26000 r-xp 00000000 00:34 42                                 /lib/i386-linux-gnu/libc-2.19.so
f6e26000-f6e28000 r--p 001aa000 00:34 42                                 /lib/i386-linux-gnu/libc-2.19.so
f6e28000-f6e29000 rw-p 001ac000 00:34 42                                 /lib/i386-linux-gnu/libc-2.19.so
f6e29000-f6e2c000 rw-p 00000000 00:00 0 
f6e2c000-f6e70000 r-xp 00000000 00:34 83                                 /lib/i386-linux-gnu/libm-2.19.so
f6e70000-f6e71000 r--p 00043000 00:34 83                                 /lib/i386-linux-gnu/libm-2.19.so
f6e71000-f6e72000 rw-p 00044000 00:34 83                                 /lib/i386-linux-gnu/libm-2.19.so
f6e72000-f6e7a000 r-xp 00000000 00:34 81                                 /lib/i386-linux-gnu/libcrypt-2.19.so
f6e7a000-f6e7b000 r--p 00008000 00:34 81                                 /lib/i386-linux-gnu/libcrypt-2.19.so
f6e7b000-f6e7c000 rw-p 00009000 00:34 81                                 /lib/i386-linux-gnu/libcrypt-2.19.so
f6e7c000-f6ea3000 rw-p 00000000 00:00 0 
f6ea3000-f6ea6000 r-xp 00000000 00:34 40                                 /lib/i386-linux-gnu/libdl-2.19.so
f6ea6000-f6ea7000 r--p 00002000 00:34 40                                 /lib/i386-linux-gnu/libdl-2.19.so
f6ea7000-f6ea8000 rw-p 00003000 00:34 40                                 /lib/i386-linux-gnu/libdl-2.19.so
f6ea8000-f6ea9000 rw-p 00000000 00:00 0 
f6ea9000-f6ec1000 r-xp 00000000 00:34 79                                 /lib/i386-linux-gnu/libpthread-2.19.so
f6ec1000-f6ec2000 r--p 00018000 00:34 79                                 /lib/i386-linux-gnu/libpthread-2.19.so
f6ec2000-f6ec3000 rw-p 00019000 00:34 79                                 /lib/i386-linux-gnu/libpthread-2.19.so
f6ec3000-f6ec6000 rw-p 00000000 00:00 0 
f6ec6000-f6ec7000 ---p 00000000 00:00 0 
f6ec7000-f6eca000 rw-p 00000000 00:00 0 
f6eca000-f7708000 r-xp 00000000 00:34 77                                 /usr/local/lib/libruby.so.2.4.2
f7708000-f770b000 r--p 0083e000 00:34 77                                 /usr/local/lib/libruby.so.2.4.2
f770b000-f770d000 rw-p 00841000 00:34 77                                 /usr/local/lib/libruby.so.2.4.2
f770d000-f7716000 rw-p 00000000 00:00 0 
f7716000-f7718000 r--p 00000000 00:00 0                                  [vvar]
f7718000-f771a000 r-xp 00000000 00:00 0                                  [vdso]
f771a000-f773a000 r-xp 00000000 00:34 35                                 /lib/i386-linux-gnu/ld-2.19.so
f773a000-f773b000 r--p 0001f000 00:34 35                                 /lib/i386-linux-gnu/ld-2.19.so
f773b000-f773c000 rw-p 00020000 00:34 35                                 /lib/i386-linux-gnu/ld-2.19.so
ff52e000-ffd2d000 rw-p 00000000 00:00 0                                  [stack]

Backtrace:

#0  0x00007fa291725428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007fa29172702a in __GI_abort () at abort.c:89
#2  0x00007fa29258a003 in die () at error.c:478
#3  0x00007fa29258a4a5 in rb_bug_context (ctx=<optimized out>, fmt=<optimized out>) at error.c:508
#4  0x00007fa29282d649 in sigsegv (sig=<optimized out>, info=<optimized out>, ctx=0x1d368c0) at signal.c:907
#5  <signal handler called>
#6  __strncpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:94
#7  0x00007fa290ecfd0d in strncpy (__dest=0x7ffecc2ac24a <error: Cannot access memory at address 0x7ffecc2ac24a>, __src=0x20685fa 'A' <repeats 200 times>..., __len=3233)
    at /usr/include/x86_64-linux-gnu/bits/string3.h:126
#8  ox_stpncpy (dest=0x7ffecc2ac24a <error: Cannot access memory at address 0x7ffecc2ac24a>, src=0x20685fa 'A' <repeats 200 times>..., n=18446603341419804305) at sax_buf.c:204
#9  read_from_str (buf=0x7ffecc2a7808) at sax_buf.c:220
#10 0x00007fa290ed03b8 in ox_sax_buf_read (buf=0x7ffecc2a7808) at sax_buf.c:115
#11 0x00007fa290ec3c05 in buf_get (buf=<optimized out>) at ./sax_buf.h:49
#12 read_instruction (dr=<optimized out>) at sax.c:549
#13 parse (dr=<optimized out>) at sax.c:286
#14 0x00007fa290ebc00b in protect_parse (drp=140732323775050) at sax.c:66
#15 0x00007fa2925a45df in rb_protect (proc=0x7fa290ebbfc0 <protect_parse>, data=140732323756040, state=0x7ffecc2a7804) at eval.c:906
#16 0x00007fa290ebbe62 in ox_sax_parse (handler=32294280, io=32294360, options=<optimized out>) at sax.c:165
#17 0x00007fa290eafd17 in sax_parse (argc=<optimized out>, argv=0x7fa292dd70a0, self=<optimized out>) at ox.c:1066
#18 0x00007fa2929b2ac8 in call_cfunc_m1 (func=0x7fa290eaf6f0 <sax_parse>, recv=0, argc=-869612982, argv=0x20685f0) at ./vm_insnhelper.c:1589
#19 0x00007fa2929a46db in vm_call_cfunc_with_frame (th=<optimized out>, reg_cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:1768
#20 vm_call_cfunc (th=<optimized out>, reg_cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:1863
#21 0x00007fa29299f791 in vm_call_method_each_type (th=<optimized out>, cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:2162
#22 0x00007fa29299f560 in vm_call_method (th=0x1cc86d0, cfp=<optimized out>, calling=<optimized out>, ci=0x2031f90, cc=<optimized out>) at ./vm_insnhelper.c:2293
#23 0x00007fa29299f1eb in vm_call_general (th=0x7ffecc2ac24a, reg_cfp=0x20685f0, calling=0x0, ci=0xa, cc=0xca1) at ./vm_insnhelper.c:2325
#24 0x00007fa29295e91a in vm_exec_core (th=<optimized out>, initial=<optimized out>) at insns.def:1066
#25 0x00007fa2929947c6 in vm_exec (th=0x1cc86d0) at vm.c:1774
#26 0x00007fa2929bf618 in invoke_block (th=<optimized out>, iseq=<optimized out>, captured=<optimized out>, cref=<optimized out>, type=<optimized out>, opt_pc=<optimized out>, self=<optimized out>)
    at vm.c:973
#27 invoke_iseq_block_from_c (th=<optimized out>, captured=<optimized out>, self=<optimized out>, argc=<optimized out>, argv=<optimized out>, passed_block_handler=<optimized out>, cref=<optimized out>, 
    splattable=<optimized out>, is_lambda=<optimized out>) at vm.c:1018
#28 0x00007fa2929bdd80 in invoke_block_from_c_splattable (th=<optimized out>, block_handler=<optimized out>, argc=<optimized out>, argv=<optimized out>, passed_block_handler=<optimized out>, 
    cref=<optimized out>, splattable=<optimized out>, is_lambda=<optimized out>) at vm.c:1036
#29 0x00007fa292981efc in vm_yield (th=<optimized out>, argc=1, argv=<optimized out>) at vm.c:1078
#30 rb_yield_0 (argc=1, argv=<optimized out>) at ./vm_eval.c:1010
#31 rb_yield_1 (val=<optimized out>) at ./vm_eval.c:1016
#32 rb_yield (val=<optimized out>) at ./vm_eval.c:1026
#33 0x00007fa2925a48ab in rb_ensure (b_proc=0x7fa292981c30 <rb_yield>, data1=32294440, e_proc=0x7fa2926485d0 <io_close>, data2=<optimized out>) at eval.c:937
#34 0x00007fa2926245b1 in rb_io_s_open (argc=<optimized out>, argv=<optimized out>, klass=<optimized out>) at io.c:6486
#35 0x00007fa2929b2ac8 in call_cfunc_m1 (func=0x7fa292624510 <rb_io_s_open>, recv=0, argc=-869612982, argv=0x20685f0) at ./vm_insnhelper.c:1589
#36 0x00007fa2929a46db in vm_call_cfunc_with_frame (th=<optimized out>, reg_cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:1768
#37 vm_call_cfunc (th=<optimized out>, reg_cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:1863
#38 0x00007fa29299f791 in vm_call_method_each_type (th=<optimized out>, cfp=<optimized out>, calling=<optimized out>, ci=<optimized out>, cc=<optimized out>) at ./vm_insnhelper.c:2162
#39 0x00007fa29299f560 in vm_call_method (th=0x1cc86d0, cfp=<optimized out>, calling=<optimized out>, ci=0x2033200, cc=<optimized out>) at ./vm_insnhelper.c:2293
#40 0x00007fa29299f1eb in vm_call_general (th=0x7ffecc2ac24a, reg_cfp=0x20685f0, calling=0x0, ci=0xa, cc=0xca1) at ./vm_insnhelper.c:2325
#41 0x00007fa29295d21f in vm_exec_core (th=<optimized out>, initial=<optimized out>) at insns.def:967
#42 0x00007fa2929947c6 in vm_exec (th=0x1cc86d0) at vm.c:1774
#43 0x00007fa292997278 in rb_iseq_eval_main (iseq=<optimized out>) at vm.c:2020
#44 0x00007fa2925a13f9 in ruby_exec_internal (n=<optimized out>) at eval.c:244
#45 ruby_exec_node (n=<optimized out>) at eval.c:308
#46 ruby_run_node (n=<optimized out>) at eval.c:300
#47 0x00000000004011d7 in main (argc=<optimized out>, argv=0x20685f0) at main.c:2

Output with AddressSanitizer (ASan):

==19005== ERROR: AddressSanitizer: stack-buffer-overflow on address 0xffe037e0 at pc 0xec9b9239 bp 0xffe02e68 sp 0xffe02e5c
READ of size 1 at 0xffe037e0 thread T0
    #0 0xec9b9238 (/usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so+0xdd238)
    #1 0xec9d4a02 (/usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so+0xf8a02)
    #2 0xec9ddccb (/usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so+0x101ccb)
    #3 0xf56b8e11 (/usr/local/lib/libruby.so.2.4.2+0x39ee11)
    #4 0xec9b1177 (/usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so+0xd5177)
    #5 0xec98ba3d (/usr/local/bundle/gems/ox-2.8.1/ext/ox/ox.so+0xafa3d)
    #6 0xf5e6da5d (/usr/local/lib/libruby.so.2.4.2+0xb53a5d)
    #7 0xf5ea2427 (/usr/local/lib/libruby.so.2.4.2+0xb88427)
    #8 0xf5f27565 (/usr/local/lib/libruby.so.2.4.2+0xc0d565)
    #9 0xf5f29e9d (/usr/local/lib/libruby.so.2.4.2+0xc0fe9d)
    #10 0xf5ee6ec0 (/usr/local/lib/libruby.so.2.4.2+0xbccec0)
    #11 0xf5f13e80 (/usr/local/lib/libruby.so.2.4.2+0xbf9e80)
    #12 0xf5f5103f (/usr/local/lib/libruby.so.2.4.2+0xc3703f)
    #13 0xf56b9df3 (/usr/local/lib/libruby.so.2.4.2+0x39fdf3)
    #14 0xf578120a (/usr/local/lib/libruby.so.2.4.2+0x46720a)
    #15 0xf5e6da5d (/usr/local/lib/libruby.so.2.4.2+0xb53a5d)
    #16 0xf5ea2427 (/usr/local/lib/libruby.so.2.4.2+0xb88427)
    #17 0xf5f27565 (/usr/local/lib/libruby.so.2.4.2+0xc0d565)
    #18 0xf5f29e9d (/usr/local/lib/libruby.so.2.4.2+0xc0fe9d)
    #19 0xf5ee61fe (/usr/local/lib/libruby.so.2.4.2+0xbcc1fe)
    #20 0xf5f13e80 (/usr/local/lib/libruby.so.2.4.2+0xbf9e80)
    #21 0xf56a8402 (/usr/local/lib/libruby.so.2.4.2+0x38e402)
    #22 0xf56b33c3 (/usr/local/lib/libruby.so.2.4.2+0x3993c3)
    #23 0xf56c0c5b (/usr/local/lib/libruby.so.2.4.2+0x3a6c5b)
    #24 0x8048af0 (/usr/local/bin/ruby+0x8048af0)
    #25 0xf5165af2 (/lib/i386-linux-gnu/libc-2.19.so+0x19af2)
    #26 0x8048b9f (/usr/local/bin/ruby+0x8048b9f)
Address 0xffe037e0 is located at offset 1856 in frame <parse> of T0's stack:
  This frame has 10 object(s):
    [32, 36) 'args'
    [96, 100) 'args'
    [160, 164) 'args'
    [224, 228) 'args'
    [288, 292) 'args'
    [352, 356) 'args'
    [416, 420) 'args'
    [480, 484) 'args'
    [544, 800) 'msg'
    [832, 1856) 'content'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
  0x3ffc06a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffc06b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffc06c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffc06d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffc06e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x3ffc06f0: 00 00 00 00 00 00 00 00 00 00 00 00[f3]f3 f3 f3
  0x3ffc0700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x3ffc0710: 00 00 00 00 00 00 f1 f1 f1 f1 04 f4 f4 f4 f2 f2
  0x3ffc0720: f2 f2 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2
  0x3ffc0730: f2 f2 04 f4 f4 f4 f2 f2 f2 f2 04 f4 f4 f4 f2 f2
  0x3ffc0740: f2 f2 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
@clod81 clod81 changed the title Stack buffer overlfow Stack buffer overflow Oct 29, 2017
@clod81 clod81 changed the title Stack buffer overflow Stack buffer overflow in SAX parser Oct 29, 2017
@ohler55
Copy link
Owner

ohler55 commented Oct 30, 2017

Not sure if the file from gist is correct. Basically it is <? followed by lots of \r then a couple of a then a series of A characters. Is that correct?

@clod81
Copy link
Author

clod81 commented Oct 30, 2017

The file is correct. It starts with <?, then a bunch of ^M (carriage-return character) followed by As (6059 As in total to cause the crash).

@clod81
Copy link
Author

clod81 commented Oct 30, 2017

from a quick debug, I can see that in the instance where the issue is triggered:

ox_stpncpy (n=18446697161219597017, src=0x555555b2aaca 'A' <repeats 200 times>..., dest=0x8000000000c2 <error: Cannot access memory at address 0x8000000000c2>) at sax_buf.c:199
199         size_t      cnt = strlen(src) + 1;
(gdb) p n
$1 = 18446697161219597017
(gdb) p strncpy(dest, src, n)
Thread 1 "ruby" received signal SIGSEGV, Segmentation fault.

the max from read_from_str in this case is set to 18446697161219597017

@ohler55
Copy link
Owner

ohler55 commented Oct 30, 2017

That seem a bit high. Thanks.

@clod81
Copy link
Author

clod81 commented Oct 30, 2017

I'm not too strong in C, but I can see that in the ox_stpncpy function the sizeof(dest) is never checked when invoking strncpy function

@ohler55
Copy link
Owner

ohler55 commented Oct 30, 2017

Might be right, I haven't gotten to look at it yet.

@clod81
Copy link
Author

clod81 commented Oct 30, 2017

this was the original file that triggered the crash: https://drive.google.com/open?id=0B5Au7ViXBlHbbWhYNUNqa1hka0k

@ohler55
Copy link
Owner

ohler55 commented Oct 30, 2017

Just pushed a fix to master. I'll release after you check.

@clod81
Copy link
Author

clod81 commented Oct 30, 2017

@ohler55 had a quick go with 2.8.2, seems it's still happening.

gem install ox-2.8.2.gem 
Building native extensions.  This could take a while...

Successfully installed ox-2.8.2
1 gem installed
root@05362b01a944:/data/ox# 
root@05362b01a944:/data/ox# cd ..
root@05362b01a944:/data# ruby fuzz.rb ox_sax_crash 
=================================================================
==366== ERROR: AddressSanitizer: stack-buffer-overflow on address 0xffbc9ca0 at pc 0xec9b8233 bp 0xffbc9328 sp 0xffbc931c
READ of size 1 at 0xffbc9ca0 thread T0..........................```

@ohler55
Copy link
Owner

ohler55 commented Oct 31, 2017

It looks like it might be a different, but related error. Not a crash but reading one past the loaded string. Any chance of telling me what the stack trace is?

@clod81
Copy link
Author

clod81 commented Oct 31, 2017

@ohler55 in a container with the extension compiled with ASan, it still giving me the same error. Without it seems to be fine now. I'd considered it closed, since I can't replicate in a normal execution scenario. I'll give it another go tomorrow. I'll let you know if something else pops up.

@clod81 clod81 closed this as completed Oct 31, 2017
@clod81
Copy link
Author

clod81 commented Oct 31, 2017

@ohler55 if you want to test it out with ASan, here is the container I was using: https://hub.docker.com/r/clod81/afl-gcc-asan-ruby2.4.2/

On that, the error still shows up

@ohler55
Copy link
Owner

ohler55 commented Oct 31, 2017

Thanks. I'll take a look in a few hours. I believe it is a different error but none the less it should be fixed and it is related. I suspect a check for <= end instead of < end. Have to find it though.

@ohler55
Copy link
Owner

ohler55 commented Oct 31, 2017

Docker is not part of my regular routine. Once I have pulled the image, how do I run it?

@clod81
Copy link
Author

clod81 commented Oct 31, 2017

@ohler55 to get shell access: docker run -it --privileged REPOSITORY_NAME

@ohler55
Copy link
Owner

ohler55 commented Oct 31, 2017

I was going to try locally but gave up and tried valgrind instead. It shows clean with no invalid reads. Turning on my own tracing it looks ok. I'm at a loss to figure out where that is occurring.

@ohler55
Copy link
Owner

ohler55 commented Nov 1, 2017

Spent some more time and got the image running with Docker. Turns out it was a different buffer than the first. Completely different issue. Basically, on a long invalid processing instruction in the XML a temporary buffer was not terminated. Fixed and pushed. Thanks for the use of the image.

@clod81
Copy link
Author

clod81 commented Nov 1, 2017

@ohler55 cool. great effort 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants