Project: libraw (libraw/libraw), language: c++
Vulnerability instance: libraw.cve-2017-14608
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/LibRaw/LibRaw/issues/101
## Description:
Issue: LibRaw/LibRaw#101
Title: Stack out of bounds read  in internal/dcraw_common.cpp(kodak_65000_load_raw)
State: closed
Created by: Twi1ight
Created at: 2017-09-13 03:32:23+00:00

Issue Body:
Command to reproduce: `simple_dcraw crash-kodak_65000_load_raw_out-of-bounds-read`
[crash-kodak_65000_load_raw_out-of-bounds-read.zip](https://github.com/LibRaw/LibRaw/files/1298058/crash-kodak_65000_load_raw_out-of-bounds-read.zip)

```
=================================================================
==229209==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffffff73ee2 at pc 0x7ffff778df6e bp 0x7ffffff72730 sp 0x7ffffff72728
READ of size 2 at 0x7ffffff73ee2 thread T0
    #0 0x7ffff778df6d in LibRaw::kodak_65000_load_raw() /root/Desktop/fuzz/src/LibRaw/internal/dcraw_common.cpp:3731:34
    #1 0x7ffff7a96515 in LibRaw::unpack() /root/Desktop/fuzz/src/LibRaw/src/libraw_cxx.cpp:2807:7
    #2 0x52d0a3 in main /root/Desktop/fuzz/src/LibRaw/samples/simple_dcraw.cpp:126:31
    #3 0x7ffff6273f44 in __libc_start_main /build/eglibc-SvCtMH/eglibc-2.19/csu/libc-start.c:287
    #4 0x41b6db in _start (/root/Desktop/fuzz/src/LibRaw/bin/.libs/simple_dcraw+0x41b6db)

Address 0x7ffffff73ee2 is located in stack of thread T0 at offset 1058 in frame
    #0 0x52b7df in main /root/Desktop/fuzz/src/LibRaw/samples/simple_dcraw.cpp:56

  This frame has 3 object(s):
    [32, 1056) 'outfn' (line 60) <== Memory access at offset 1058 overflows this variable
    [1184, 2208) 'thumbfn' (line 60)
    [2336, 566928) 'RawProcessor' (line 62)
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /root/Desktop/fuzz/src/LibRaw/internal/dcraw_common.cpp:3731:34 in LibRaw::kodak_65000_load_raw()
Shadow bytes around the buggy address:
  0x10007ffe6780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe6790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe67a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe67b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe67c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10007ffe67d0: 00 00 00 00 00 00 00 00 00 00 00 00[f2]f2 f2 f2
  0x10007ffe67e0: f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 f2 00 00 00 00
  0x10007ffe67f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe6800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe6810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10007ffe6820: 00 00 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==229209==ABORTING
```
gdb debug output:
```
(gdb) bt
#0  0x00000000004fdc04 in __asan::ReportGenericError(unsigned long, unsigned long, unsigned long, unsigned long, bool, unsigned long, unsigned int, bool) ()
#1  0x00000000004fe9f3 in __asan_report_load2 ()
#2  0x00007ffff778df6e in LibRaw::kodak_65000_load_raw (this=0x7ffffff743e0) at internal/dcraw_common.cpp:3731
#3  0x00007ffff7a96516 in LibRaw::unpack (this=0x7ffffff743e0) at src/libraw_cxx.cpp:2807
#4  0x000000000052d0a4 in main (ac=2, av=0x7fffffffe3a8) at samples/simple_dcraw.cpp:126
(gdb) f 2
#2  0x00007ffff778df6e in LibRaw::kodak_65000_load_raw (this=0x7ffffff743e0) at internal/dcraw_common.cpp:3731
3731	        if ((RAW(row, col + i) = curve[ret ? buf[i] : (pred[i & 1] += buf[i])]) >> 12)
(gdb) p ret ? buf[i] : (pred[i & 1] += buf[i])
$5 = -544416
(gdb) p pred
$6 = {-544416, 32767}
(gdb) p buf[i]
$7 = 17376
(gdb) 
```
we could see that the index of `curve` is a large negative number `-544416`, leads out of bounds read.


Comments:
Comment by LibRaw on 2017-09-13 04:07:46+00:00:
Thanks.
Could you please  guys 
1) accumulate multiple errors before reporting to CVE team
2) Coordinate with another team (https://github.com/LibRaw/LibRaw/issues/100 )
?
It is not hard to publish new patches every day, but too much trouble to publish full release 3rd time a week.

LibRaw deals with data come from external sources and the data format(s) are complex and not well defined. So, it is very likely that we have lot of such bugs.

So, it is much better to create single CVE (multiple vulns in ...) but not separate version/separate CVE for each patch.

---

Comment by LibRaw on 2017-09-13 06:33:32+00:00:
Fixed in https://github.com/LibRaw/LibRaw/commit/d13e8f6d1e987b7491182040a188c16a395f1d21

---

Comment by pgajdos on 2017-09-15 10:03:37+00:00:
With 0.18.4 and above example I get:

$ valgrind simple_dcraw crash-kodak_65000_load_raw_out-of-bounds-read
==24943== Memcheck, a memory error detector
==24943== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==24943== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==24943== Command: simple_dcraw crash-kodak_65000_load_raw_out-of-bounds-read
==24943== 
crash-kodak_65000_load_raw_out-of-bounds-read: data corrupted at 782348
==24943== Invalid read of size 2
==24943==    at 0x4E609E0: LibRaw::kodak_65000_load_raw() (dcraw_common.cpp:3244)
==24943==    by 0x4EA9989: LibRaw::unpack() (libraw_cxx.cpp:2523)
==24943==    by 0x400F74: main (simple_dcraw.cpp:131)
==24943==  Address 0x1ffef9ebca is on thread 1's stack
==24943==  726 bytes below stack pointer
==24943== 
==24943== 
==24943== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==24943==  Access not within mapped region at address 0x1FFEF9AFA8
==24943==    at 0x4E609E0: LibRaw::kodak_65000_load_raw() (dcraw_common.cpp:3244)
==24943==    by 0x4EA9989: LibRaw::unpack() (libraw_cxx.cpp:2523)
==24943==    by 0x400F74: main (simple_dcraw.cpp:131)
==24943==  If you believe this happened as a result of a stack
==24943==  overflow in your program's main thread (unlikely but
==24943==  possible), you can try to increase the size of the
==24943==  main thread stack using the --main-stacksize= flag.
==24943==  The main thread stack size used in this run was 8388608.
==24943== 
==24943== HEAP SUMMARY:
==24943==     in use at exit: 27,568,990 bytes in 8 blocks
==24943==   total heap usage: 25 allocs, 17 frees, 27,643,443 bytes allocated
==24943== 
==24943== LEAK SUMMARY:
==24943==    definitely lost: 0 bytes in 0 blocks
==24943==    indirectly lost: 0 bytes in 0 blocks
==24943==      possibly lost: 0 bytes in 0 blocks
==24943==    still reachable: 27,568,990 bytes in 8 blocks
==24943==         suppressed: 0 bytes in 0 blocks
==24943== Rerun with --leak-check=full to see details of leaked memory
==24943== 
==24943== For counts of detected and suppressed errors, rerun with: -v
==24943== ERROR SUMMARY: 65 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)
$

---

Comment by LibRaw on 2017-09-15 10:07:31+00:00:
Have you applied patch https://github.com/LibRaw/LibRaw/commit/d13e8f6d1e987b7491182040a188c16a395f1d21 ?

---

Comment by pgajdos on 2017-09-15 10:14:28+00:00:
Eh, of course not. I read the difference of the change log wrongly. I am sorry for noise.

---

Comment by LibRaw on 2017-09-15 10:17:39+00:00:
BTW, it needs more patch(es), it is possible to run out of buf[256], will fix today

---

Comment by pgajdos on 2017-10-18 12:49:20+00:00:
With 0.18.5, I do not get segfault anymore. However, there are many valgrind errors:

```
$ valgrind --quiet simple_dcraw crash-kodak_65000_load_raw_out-of-bounds-read
crash-kodak_65000_load_raw_out-of-bounds-read: data corrupted at 725440
==29012== Conditional jump or move depends on uninitialised value(s)
==29012==    at 0x4E61A18: LibRaw::kodak_65000_load_raw() (dcraw_common.cpp:3245)
==29012==    by 0x4EAA999: LibRaw::unpack() (libraw_cxx.cpp:2523)
==29012==    by 0x400F74: main (simple_dcraw.cpp:131)
==29012== 
==29012== Use of uninitialised value of size 8
==29012==    at 0x4E619C7: LibRaw::kodak_65000_load_raw() (dcraw_common.cpp:3247)
==29012==    by 0x4EAA999: LibRaw::unpack() (libraw_cxx.cpp:2523)
==29012==    by 0x400F74: main (simple_dcraw.cpp:131)
==29012== 
==29012== 
==29012== More than 10000000 total errors detected.  I'm not reporting any more.
==29012== Final error counts will be inaccurate.  Go fix your program!
==29012== Rerun with --error-limit=no to disable this cutoff.  Note
==29012== that errors may occur in your program without prior warning from
==29012== Valgrind, because errors are no longer being displayed.
==29012==
$
```

---

Comment by LibRaw on 2017-10-18 12:58:28+00:00:
these jumps are limited by curve input range

Attachments:
https://github.com/LibRaw/LibRaw/files/1298058/crash-kodak_65000_load_raw_out-of-bounds-read.zip

Commit References:
d13e8f6d1e987b7491182040a188c16a395f1d21

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./configure --disable-examples
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool pkg-config
RUN git clone https://github.com/libraw/libraw libraw
RUN git -C libraw checkout 8303e74b0567806dd5f16fc39aab70fe928de1a2
WORKDIR $SRC/libraw
COPY build.sh $SRC/

Additional files:
filename: libraw_fuzzer.cc
content: /*  Copyright 2020 Google Inc.

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.
*/

#include <stddef.h>
#include <stdint.h>

#include <string>

#include <libraw.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  // Input less than 10mb
  if (size > 10000000) {
    return 0;
  }

  LibRaw lib_raw;

  int result = lib_raw.open_buffer(
      const_cast<char*>(reinterpret_cast<const char*>(data)), size);
  if (result != LIBRAW_SUCCESS) {
    return 0;
  }

  result = lib_raw.unpack();
  if (result != LIBRAW_SUCCESS) {
    return 0;
  }

  result = lib_raw.dcraw_process();
  if (result != LIBRAW_SUCCESS) {
    return 0;
  }

  return 0;
}