Crashes in draco_encoder when processing malformed OBJ and DRC files
Hi team,
Some crashes were found while fuzz testing of the draco_encoder binary which can be triggered via malformed OBJ and DRC files. Although these malformed files only crash the program, they could potentially be crafted further into security issues where these kinds of files would be able compromise the process's memory through memory corruption, so hardening the code to prevent these kinds of bugs would be great to mitigate such issues.
See details below for repro and debug information.
You can download the crashing OBJ and DRC files (~few mb file size) from Ufile to to debug and understand where the code is crashing.
crash-1.obj
(gdb) r -i crash-1.obj -o test.drc
Starting program: draco_encoder -i crash-1.obj -o test.drc
Encoder options:
Compression level = 7
Positions: Quantization = 11 bits
Normals: Quantization = 8 bits
Generic: Quantization = 8 bits
Program received signal SIGSEGV, Segmentation fault.
0x000055555565084c in draco::SequentialAttributeEncoder::SetPredictionSchemeParentAttributes(draco::PredictionSchemeInterface*) ()
(gdb) bt
#0 0x000055555565084c in draco::SequentialAttributeEncoder::SetPredictionSchemeParentAttributes(draco::PredictionSchemeInterface*) ()
#1 0x000055555560c9b5 in draco::SequentialIntegerAttributeEncoder::EncodeValues(std::vector<draco::IndexType<unsigned int, draco::PointIndex_tag_type_>, std::allocator<draco::IndexType<unsigned int, draco::PointIndex_tag_type_> > > const&, draco::EncoderBuffer*) ()
#2 0x000055555560b95b in draco::SequentialAttributeEncodersController::EncodeAttributes(draco::EncoderBuffer*) ()
#3 0x00005555555bcafe in draco::PointCloudEncoder::EncodeAllAttributes() ()
#4 0x00005555555bd8f7 in draco::PointCloudEncoder::Encode(draco::EncoderOptionsBase<int> const&, draco::EncoderBuffer*) ()
#5 0x000055555558ca41 in draco::ExpertEncoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#6 0x000055555558cd36 in draco::ExpertEncoder::EncodeToBuffer(draco::EncoderBuffer*) ()
#7 0x000055555558811a in draco::Encoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#8 0x0000555555565ad2 in main ()
(gdb) i r
rax 0x200000000 8589934592
rbx 0x55555569b040 93824993570880
rcx 0x0 0
rdx 0x7fffffffe0d0 140737488347344
rsi 0x55555569b040 93824993570880
rdi 0x55555569b040 93824993570880
rbp 0x0 0x0
rsp 0x7fffffffdbc0 0x7fffffffdbc0
r8 0x47 71
r9 0xffffffff 4294967295
r10 0x5555556a3a6c 93824993606252
r11 0x7ffff7c49be0 140737350245344
r12 0x5555556972c0 93824993555136
r13 0x0 0
r14 0x5555556a2618 93824993601048
r15 0x55555569cab0 93824993577648
rip 0x55555565084c 0x55555565084c <draco::SequentialAttributeEncoder::SetPredictionSchemeParentAttributes(draco::PredictionSchemeInterface*)+108>
eflags 0x10206 [ PF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/i $rip
=> 0x55555565084c <_ZN5draco26SequentialAttributeEncoder35SetPredictionSchemeParentAttributesEPNS_25PredictionSchemeInterfaceE+108>: call QWORD PTR [rax+0x28]
(gdb) exploitable
Description: Access violation during branch instruction
Short description: BranchAv (4/22)
Hash: 14eb572e65bc3f8236cd1eebf61b9bd5.f882154e7e098166ec3c4eed69faf284
Exploitability Classification: EXPLOITABLE
Explanation: The target crashed on a branch instruction, which may indicate that the control flow is tainted.
Other tags: DestAv (8/22), AccessViolation (21/22)
crash-2.obj
(gdb) r -i crash-2.obj -o test.drc
Starting program: draco_encoder -i crash-2.obj -o test.drc
Encoder options:
Compression level = 7
Positions: Quantization = 11 bits
Normals: Quantization = 8 bits
draco_encoder: malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Program received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
(gdb) bt
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff7a83859 in __GI_abort () at abort.c:79
#2 0x00007ffff7af645a in __malloc_assert (
assertion=assertion@entry=0x7ffff7c1a8a8 "(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)", file=file@entry=0x7ffff7c163c3 "malloc.c", line=line@entry=2379, function=function@entry=0x7ffff7c1b030 <__PRETTY_FUNCTION__.13066> "sysmalloc") at malloc.c:298
#3 0x00007ffff7af8abf in sysmalloc (nb=nb@entry=139344, av=av@entry=0x7ffff7c49b80 <main_arena>) at malloc.c:2379
#4 0x00007ffff7af9913 in _int_malloc (av=av@entry=0x7ffff7c49b80 <main_arena>, bytes=bytes@entry=139336) at malloc.c:4141
#5 0x00007ffff7afb2d4 in __GI___libc_malloc (bytes=139336) at malloc.c:3058
#6 0x00007ffff7e64b39 in operator new(unsigned long) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#7 0x0000555555594267 in std::vector<draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_>, std::allocator<draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_> > >::_M_fill_insert(__gnu_cxx::__normal_iterator<draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_>*, std::vector<draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_>, std::allocator<draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_> > > >, unsigned long, draco::IndexType<unsigned int, draco::AttributeValueIndex_tag_type_> const&) ()
#8 0x000055555560c6de in draco::SequentialIntegerAttributeEncoder::TransformAttributeToPortableFormat(std::vector<draco::IndexType<unsigned int, draco::PointIndex_tag_type_>, std::allocator<draco::IndexType<unsigned int, draco::PointIndex_tag_type_> > > const&) ()
#9 0x000055555560b1a1 in draco::SequentialAttributeEncodersController::TransformAttributesToPortableFormat() ()
#10 0x000055555560b902 in draco::SequentialAttributeEncodersController::EncodeAttributes(draco::EncoderBuffer*) ()
#11 0x00005555555bcafe in draco::PointCloudEncoder::EncodeAllAttributes() ()
#12 0x00005555555bd8f7 in draco::PointCloudEncoder::Encode(draco::EncoderOptionsBase<int> const&, draco::EncoderBuffer*) ()
#13 0x000055555558ca41 in draco::ExpertEncoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#14 0x000055555558cd36 in draco::ExpertEncoder::EncodeToBuffer(draco::EncoderBuffer*) ()
#15 0x000055555558811a in draco::Encoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#16 0x0000555555565ad2 in main ()
(gdb) i r
rax 0x0 0
rbx 0x7ffff7a5d0c0 140737348227264
rcx 0x7ffff7aa418b 140737348518283
rdx 0x0 0
rsi 0x7fffffffd830 140737488345136
rdi 0x2 2
rbp 0x22050 0x22050
rsp 0x7fffffffd830 0x7fffffffd830
r8 0x0 0
r9 0x7fffffffd830 140737488345136
r10 0x8 8
r11 0x246 582
r12 0x7908 30984
r13 0x1000 4096
r14 0x555555e36f90 93825001549712
r15 0x2203 8707
rip 0x7ffff7aa418b 0x7ffff7aa418b <__GI_raise+203>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/i $rip
=> 0x7ffff7aa418b <__GI_raise+203>: mov rax,QWORD PTR [rsp+0x108]
(gdb) exploitable
Description: Heap error
Short description: HeapError (10/22)
Hash: 90460f0076369aa90b8324c78a0360d9.08405b5508b3311cb893e8a64e50d937
Exploitability Classification: EXPLOITABLE
Explanation: The target's backtrace indicates that libc has detected a heap error or that the target was executing a heap function when it stopped. This could be due to heap corruption, passing a bad pointer to a heap function such as free(), etc. Since heap errors might include buffer overflows, use-after-free situations, etc. they are generally considered exploitable.
Other tags: AbortSignal (20/22)
crash-3.drc
(gdb) r -i crash-3.drc -o test.obj
Starting program: draco_encoder -i crash-3.drc -o test.obj
Encoder options:
Compression level = 7
Positions: Quantization = 11 bits
Texture coordinates: Quantization = 10 bits
Normals: Quantization = 8 bits
Program received signal SIGSEGV, Segmentation fault.
0x0000555555624759 in draco::ComputeShannonEntropy(unsigned int const*, int, int, int*) ()
(gdb) bt
#0 0x0000555555624759 in draco::ComputeShannonEntropy(unsigned int const*, int, int, int*) ()
#1 0x000055555562d6a7 in draco::EncodeSymbols(unsigned int const*, int, int, draco::Options const*, draco::EncoderBuffer*) ()
#2 0x000055555560ce2e in draco::SequentialIntegerAttributeEncoder::EncodeValues(std::vector<draco::IndexType<unsigned int, draco::PointIndex_tag_type_>, std::allocator<draco::IndexType<unsigned int, draco::PointIndex_tag_type_> > > const&, draco::EncoderBuffer*) ()
#3 0x000055555560b95b in draco::SequentialAttributeEncodersController::EncodeAttributes(draco::EncoderBuffer*) ()
#4 0x00005555555bcafe in draco::PointCloudEncoder::EncodeAllAttributes() ()
#5 0x00005555555bd8f7 in draco::PointCloudEncoder::Encode(draco::EncoderOptionsBase<int> const&, draco::EncoderBuffer*) ()
#6 0x000055555558ca41 in draco::ExpertEncoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#7 0x000055555558cd36 in draco::ExpertEncoder::EncodeToBuffer(draco::EncoderBuffer*) ()
#8 0x000055555558811a in draco::Encoder::EncodeMeshToBuffer(draco::Mesh const&, draco::EncoderBuffer*) ()
#9 0x0000555555565ad2 in main ()
(gdb) i r
rax 0x1 1
rbx 0x328e 12942
rcx 0x7fffffffdb58 140737488345944
rdx 0xd32 3378
rsi 0x328e 12942
rdi 0x5555556d8060 93824993820768
rbp 0x0 0x0
rsp 0x7fffffffda80 0x7fffffffda80
r8 0x0 0
r9 0x7ffff7c4a350 140737350247248
r10 0x555555681010 93824993464336
r11 0x7ffff7c49be0 140737350245344
r12 0x7fffffffdb58 140737488345944
r13 0x5555556d8060 93824993820768
r14 0xc 12
r15 0xffffffffffffffff -1
rip 0x555555624759 0x555555624759 <draco::ComputeShannonEntropy(unsigned int const*, int, int, int*)+121>
eflags 0x10202 [ IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/i $rip
=> 0x555555624759 <_ZN5draco21ComputeShannonEntropyEPKjiiPi+121>: add DWORD PTR [rbp+rdx*4+0x0],0x1
(gdb) exploitable
Description: Access violation near NULL on destination operand
Short description: DestAvNearNull (15/22)
Hash: 3eb9a5ebba48baef8d0d4d4b8f3d1d7c.cfad5f0fa3da74b30fdb03813e641dae
Exploitability Classification: PROBABLY_EXPLOITABLE
Explanation: The target crashed on an access violation at an address matching the destination operand of the instruction. This likely indicates a write access violation, which means the attacker may control write address and/or value. However, it there is a chance it could be a NULL dereference.
Other tags: AccessViolation (21/22)
Your files cannot be downloaded without paying ufile extortion. Could you provide a new link?
The ufile link has probably expired after 3 months or so, I've uploaded here as that should work too.
Thanks for taking a look!