Project: libjxl (libjxl/libjxl), language: c++
Vulnerability instance: libjxl.cve-2021-45928
Sanitizer: address

================= Bug Report (1/2) ==================
## Source: Chromium
## URL: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=36456
## Description:
Detailed Report: https://oss-fuzz.com/testcase?key=5454144264601600

Project: libvips
Fuzzing Engine: libFuzzer
Fuzz Target: jpegsave_buffer_fuzzer
Job Type: libfuzzer_asan_libvips
Platform Id: linux

Crash Type: UNKNOWN WRITE
Crash Address: 0x000000001000
Crash State:
  jxl::ModularFrameDecoder::DecodeGroup
  jxl::FrameDecoder::ProcessACGroup
  jxl::ThreadPool::RunCallState<jxl::FrameDecoder::ProcessSections
 
Sanitizer: address (ASAN)

Recommended Security Severity: High

Regressed: https://oss-fuzz.com/revisions?job=libfuzzer_asan_libvips&range=202107210601:202107220605

Reproducer Testcase: https://oss-fuzz.com/download?testcase_id=5454144264601600

Issue filed automatically.

See https://google.github.io/oss-fuzz/advanced-topics/reproducing for instructions to reproduce this bug locally.
When you fix this bug, please
  * mention the fix revision(s).
  * state whether the bug was a short-lived regression or an old bug in any stable releases.
  * add any other useful information.
This information can help downstream consumers.

If you need to contact the OSS-Fuzz team with a question, concern, or any other feedback, please file an issue at https://github.com/google/oss-fuzz/issues. Comments on individual Monorail issues are not monitored.

This bug is subject to a 90 day disclosure deadline. If 90 days elapse
without an upstream patch, then the bug report will automatically
become visible to the public.

================= Bug Report (2/2) ==================
## Source: GitHub Issue
## URL: https://github.com/libjxl/libjxl/issues/360
## Description:
Issue: libjxl/libjxl#360
Title: Out of bounds write in master libjxl reported by oss-fuzz
State: closed
Created by: jcupitt
Created at: 2021-07-26 11:42:15+00:00
Labels: fuzzerbug

Issue Body:
Hello, oss-fuzz is reporting an out of bounds write in libjxl master:

```
  | AddressSanitizer:DEADLYSIGNAL
  | =================================================================
  | ==655724==ERROR: AddressSanitizer: SEGV on unknown address 0x000000001000 (pc 0x000002d55019 bp 0x7f4bc69f7a50 sp 0x7f4bc69f78c0 T2)
  | ==655724==The signal is caused by a WRITE memory access.
  | SCARINESS: 30 (wild-addr-write)
  | #0 0x2d55019 in jxl::ModularFrameDecoder::DecodeGroup(jxl::Rect const&, jxl::BitReader*, int, int, jxl::ModularStreamId const&, bool, jxl::PassesDecoderState*, jxl::ImageBundle*) libjxl/lib/jxl/dec_modular.cc:329:20
  | #1 0x2c0d17a in jxl::FrameDecoder::ProcessACGroup(unsigned long, jxl::BitReader* restrict*, unsigned long, unsigned long, bool, bool) libjxl/lib/jxl/dec_frame.cc:578:7
  | #2 0x2c161e4 in operator() libjxl/lib/jxl/dec_frame.cc:731:16
  | #3 0x2c161e4 in jxl::ThreadPool::RunCallState<jxl::FrameDecoder::ProcessSections(jxl::FrameDecoder::SectionInfo const*, unsigned long, jxl::FrameDecoder::SectionStatus*)::$_1, jxl::FrameDecoder::ProcessSections(jxl::FrameDecoder::SectionInfo const*, unsigned long, jxl::FrameDecoder::SectionStatus*)::$_2>::CallDataFunc(void*, unsigned int, unsigned long) libjxl/lib/jxl/base/data_parallel.h:88:14
  | #4 0x31ac619 in RunRange libjxl/lib/threads/thread_parallel_runner_internal.cc:137:7
```

Reproducer:

http://www.rollthepotato.net/~john/.clusterfuzz-testcase-minimized-5454144264601600.jxl


Comments:
Comment by jonsneyers on 2021-07-26 12:16:17+00:00:
I can't seem to reproduce this one. Some fuzzerbugs were fixed recently, could you check if the bug is still there in the current most recent git version?

---

Comment by jcupitt on 2021-07-26 12:28:30+00:00:
Looks like it was last test yesterday:

```
[2021-07-25 14:16:26 UTC] oss-fuzz-linux-zone1-host-jq6g-7: Progression task finished: still crashes on latest revision r202107250600.
```

I'll look a bit more.

---

Comment by jcupitt on 2021-07-26 12:34:47+00:00:
Yes, libjxl git master was cloned around mid-day on the 25th.

---

Comment by jonsneyers on 2021-07-26 14:34:15+00:00:
Can you check if you linked the right reproducer?

This one has the following result, which looks like it fails quite early already, earlier than what could lead to that stack trace...

```
JPEG XL decoder v0.5.0 [SSE4,Scalar]
Read 776 compressed bytes.
No output file specified.
Decoding will be performed, but the result will be discarded.
../lib/jxl/dec_bit_reader.h:212: JXL_FAILURE: Non-zero padding bits
../lib/jxl/toc.cc:64: JXL_RETURN_IF_ERROR code=1: reader->JumpToByteBoundary()
../lib/jxl/dec_frame.cc:271: JXL_RETURN_IF_ERROR code=1: ReadGroupOffsets(toc_entries, br, &section_offsets_, &section_sizes_, &groups_total_size)
../lib/jxl/dec_frame.cc:151: JXL_RETURN_IF_ERROR code=1: frame_decoder.InitFrame( reader, decoded, is_preview, dparams.allow_partial_files, dparams.allow_partial_files && dparams.allow_more_progressive_steps)
../lib/jxl/dec_file.cc:155: JXL_RETURN_IF_ERROR code=1: dec_ok
Failed to decompress to pixels.
```

---

Comment by deymo on 2021-07-26 15:16:32+00:00:
Jon it looks like you are using CRASH_ON_ERROR, which we don't have enabled in release mode and don't expect users to have. 

The linked file is from a different fuzzer than our djxl_fuzzer, so it is probably easier to reproduce with djxl or decode_oneshot. I managed to do that with an asan build following the instructions in our doc but building decode_oneshot instead of djxl_fuzzer.

---

Comment by jcupitt on 2021-07-27 11:34:36+00:00:
@jonsneyers I checked and it's the right reproducer.

I did a little digging and it's a 86 x 72 pixel image, with 1 colour channel and 2 extra channels. This is a case (I think?) that djxl does not handle, so it rejects the image before attempting decode.

libvips supports any number of alphas, so it tries to decode it as GAA, and this triggers the failure.

You can reproduce with the libvips decoder like this:

```
$ valgrind vips copy clusterfuzz-testcase-minimized-5454144264601600.jxl x.v
==683866== Memcheck, a memory error detector
==683866== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==683866== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==683866== Command: vips copy clusterfuzz-testcase-minimized-5454144264601600.jxl x.v
==683866== 
==683866== Thread 17:
==683866== Invalid write of size 4
==683866==    at 0x5B84473: jxl::ModularFrameDecoder::DecodeGroup(jxl::Rect const&, jxl::BitReader*, int, int, jxl::ModularStreamId const&, bool, jxl::PassesDecoderState*, jxl::ImageBundle*) (in /home/john/vips/lib/libjxl.so.0.5.0)
==683866==    by 0x5B4DAA1: jxl::FrameDecoder::ProcessACGroup(unsigned long, jxl::BitReader* restrict*, unsigned long, unsigned long, bool, bool) (in /home/john/vips/lib/libjxl.so.0.5.0)
==683866==    by 0x5B4DC6D: jxl::ThreadPool::RunCallState<jxl::FrameDecoder::ProcessSections(jxl::FrameDecoder::SectionInfo const*, unsigned long, jxl::FrameDecoder::SectionStatus*)::{lambda(unsigned long)#2}, jxl::FrameDecoder::ProcessSections(jxl::FrameDecoder::SectionInfo const*, unsigned long, jxl::FrameDecoder::SectionStatus*)::{lambda(unsigned long, unsigned long)#3}>::CallDataFunc(void*, unsigned int, unsigned long) (in /home/john/vips/lib/libjxl.so.0.5.0)
==683866==    by 0x5AA8337: jpegxl::ThreadParallelRunner::RunRange(jpegxl::ThreadParallelRunner*, unsigned long, int) (in /home/john/vips/lib/libjxl_threads.so.0.5.0)
==683866==    by 0x5AA842F: jpegxl::ThreadParallelRunner::ThreadFunc(jpegxl::ThreadParallelRunner*, int) (in /home/john/vips/lib/libjxl_threads.so.0.5.0)
...
```

Of course perhaps this is such a strange case that it's not worth fixing. Should libvips reject JXL with `num_extra_channels` > 1? It might be useful to support it for scientific imaging, where many alphas are quite common.


---

Comment by deymo on 2021-07-27 12:54:01+00:00:
@jcupitt #365 should address this one. We want to support multiple extra channels so this should be fixed in libjxl anyway.

---

Comment by jcupitt on 2021-07-27 14:44:05+00:00:
Yup, passes cleanly now. Nice job!

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
  export AFL_NOOPT=1
  cmake "${build_args[@]}" "${SRC}/libjxl"
export CXXFLAGS="${CXXFLAGS} -DJXL_IS_DEBUG_BUILD=1"
mkdir -p ${WORK}/libjxl-fuzzer
cmake \
  "${build_args[@]}" \
  -DJPEGXL_FUZZER_LINK_FLAGS="${LIB_FUZZING_ENGINE}" \
  "${SRC}/libjxl"
ninja clean
ninja "${fuzzers[@]}"

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y cmake ninja-build pkg-config
RUN git clone https://github.com/libjxl/libjxl libjxl
RUN git -C libjxl checkout b499ae3a2701634b9d528ab512921184a62497eb
WORKDIR $SRC/libjxl
COPY build.sh $SRC/

Additional files: