Project: ox (ohler55/ox), language: ruby
Vulnerability instance: ox-ruby.cve-2017-16229
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/ohler55/ox/issues/195
## Description:
Issue: ohler55/ox#195
Title: Stack buffer overflow in SAX parser
State: closed
Created by: clod81
Created at: 2017-10-29 22:44:33+00:00

Issue Body:
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
```


Comments:
Comment by ohler55 on 2017-10-30 00:39:46+00:00:
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?

---

Comment by clod81 on 2017-10-30 01:45:45+00:00:
The file is correct. It starts with `<?`, then a bunch of `^M` (carriage-return character) followed by `A`s (6059 `A`s in total to cause the crash).

---

Comment by clod81 on 2017-10-30 02:09:46+00:00:
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`

---

Comment by ohler55 on 2017-10-30 02:17:44+00:00:
That seem a bit high. Thanks.

---

Comment by clod81 on 2017-10-30 02:27:59+00:00:
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

---

Comment by ohler55 on 2017-10-30 02:34:25+00:00:
Might be right, I haven't gotten to look at it yet.

---

Comment by clod81 on 2017-10-30 03:55:57+00:00:
this was the original file that triggered the crash: https://drive.google.com/open?id=0B5Au7ViXBlHbbWhYNUNqa1hka0k






---

Comment by ohler55 on 2017-10-30 22:14:34+00:00:
Just pushed a fix to master. I'll release after you check.

---

Comment by clod81 on 2017-10-30 22:30:38+00:00:
@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..........................```

---

Comment by ohler55 on 2017-10-31 03:48:28+00:00:
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? 

---

Comment by clod81 on 2017-10-31 07:39:15+00:00:
@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. 

---

Comment by clod81 on 2017-10-31 07:45:48+00:00:
@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

---

Comment by ohler55 on 2017-10-31 13:57:51+00:00:
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.

---

Comment by ohler55 on 2017-10-31 17:33:31+00:00:
Docker is not part of my regular routine. Once I have pulled the image, how do I run it?

---

Comment by clod81 on 2017-10-31 19:21:38+00:00:
@ohler55 to get shell access: `docker run -it --privileged REPOSITORY_NAME`

---

Comment by ohler55 on 2017-10-31 22:30:50+00:00:
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.

---

Comment by ohler55 on 2017-11-01 19:43:45+00:00:
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.

---

Comment by clod81 on 2017-11-01 19:51:04+00:00:
@ohler55 cool. great effort :+1:

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
cd $SRC/ox-ruby
gem build
RUZZY_DEBUG=1 gem install --development --verbose *.gem

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN git clone https://github.com/ohler55/ox ox
RUN git -C ox checkout d8695646fa3e134bd76ba3bd33462f15ad31b230
WORKDIR $SRC/ox
COPY build.sh $SRC/

Additional files:
filename: fuzz_parse.rb
content: # frozen_string_literal: true
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
require 'ruzzy'
require 'ox'

test_one_input = lambda do |data|
  begin
    Ox.parse(data)
  rescue Ox::ParseError, Ox::SyntaxError, EncodingError
    # pass
  end
  return 0
end

Ruzzy.fuzz(test_one_input)

filename: fuzz_sax_parse.rb
content: # frozen_string_literal: true
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
################################################################################
require 'ruzzy'
require 'ox'

class MyHandler < Ox::Sax
  # Called for the opening of an element
  def start_element(name)
  end

  # Called for the text content of an element
  def text(value)
  end

  # Called for the closing of an element
  def end_element(name)
  end
end

test_one_input = lambda do |data|
  begin
    handler = MyHandler.new
    Ox.sax_parse(handler, StringIO.new(data))
  rescue Ox::ParseError, EncodingError
    # pass
  end
  return 0
end

Ruzzy.fuzz(test_one_input)