google / draco

Draco is a library for compressing and decompressing 3D geometric meshes and point clouds. It is intended to improve the storage and transmission of 3D graphics.
https://google.github.io/draco/
Apache License 2.0
6.47k stars 962 forks source link

Crashes in draco_encoder when processing malformed OBJ and DRC files #795

Open retpoline opened 2 years ago

retpoline commented 2 years ago

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)
tomfinegan commented 2 years ago

Your files cannot be downloaded without paying ufile extortion. Could you provide a new link?

retpoline commented 2 years ago

The ufile link has probably expired after 3 months or so, I've uploaded here as that should work too.

draco-crashes.zip

Thanks for taking a look!