Project: jansson (akheron/jansson), language: c++
Vulnerability instance: jansson.cve-2020-36325
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/akheron/jansson/issues/548
## Description:
Issue: akheron/jansson#548
Title: OOB Read memory corruption bug
State: closed
Created by: JordyZomer
Created at: 2020-09-01 16:46:51+00:00

Issue Body:
Hi,

I encountered an OOB read memory corruption bug when fuzzing Jansson.

Below you can find the crash log:
```
# ./prog -detect_leaks=0
INFO: Seed: 665849601
INFO: Loaded 1 modules   (10 inline 8-bit counters): 10 [0x5a3f90, 0x5a3f9a),
INFO: Loaded 1 PC tables (10 PCs): 10 [0x568020,0x5680c0),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 27Mb
#19711  NEW    cov: 6 ft: 7 corp: 2/4b lim: 198 exec/s: 0 rss: 47Mb L: 3/3 MS: 4 InsertByte-InsertByte-ShuffleBytes-ShuffleBytes-
#19777  REDUCE cov: 6 ft: 7 corp: 2/3b lim: 198 exec/s: 0 rss: 47Mb L: 2/2 MS: 1 EraseBytes-
#524288 pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 262144 rss: 610Mb
#1048576        pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 262144 rss: 658Mb
#2097152        pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 262144 rss: 659Mb
#4194304        pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 262144 rss: 659Mb
#8388608        pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 262144 rss: 659Mb
#16777216       pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 254200 rss: 659Mb
#33554432       pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 252288 rss: 659Mb
#67108864       pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 246723 rss: 659Mb
#134217728      pulse  cov: 6 ft: 7 corp: 2/3b lim: 4096 exec/s: 241833 rss: 659Mb
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1898031==ERROR: AddressSanitizer: SEGV on unknown address 0x610000050000 (pc 0x7f229f4f020b bp 0x7ffcf5010a30 sp 0x7ffcf5010938 T0)
==1898031==The signal is caused by a READ memory access.
    #0 0x7f229f4f020b in string_get /root/jansson-2.13/src/load.c:893:7
    #1 0x7f229f4f06f3 in stream_get.part.0 /root/jansson-2.13/src/load.c:158:13
    #2 0x7f229f4f0837 in stream_get /root/jansson-2.13/src/load.c:154:8
    #3 0x7f229f4f0837 in lex_get_save /root/jansson-2.13/src/load.c:233:13
    #4 0x7f229f4f0a32 in lex_scan /root/jansson-2.13/src/load.c:604:17
    #5 0x7f229f4f15da in parse_json.constprop.0 /root/jansson-2.13/src/load.c:868:9
    #6 0x7f229f4f1721 in json_loads /root/jansson-2.13/src/load.c:920:14
    #7 0x54dad3 in LLVMFuzzerTestOneInput /root/fuzzing/jansson/fuzz.c:12:9
    #8 0x4586a1 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/root/fuzzing/jansson/prog+0x4586a1)
    #9 0x457de5 in fuzzer::Fuzzer::RunOne(unsigned char const*, unsigned long, bool, fuzzer::InputInfo*, bool*) (/root/fuzzing/jansson/prog+0x457de5)
    #10 0x45a087 in fuzzer::Fuzzer::MutateAndTestOne() (/root/fuzzing/jansson/prog+0x45a087)
    #11 0x45ad85 in fuzzer::Fuzzer::Loop(std::__Fuzzer::vector<fuzzer::SizedFile, fuzzer::fuzzer_allocator<fuzzer::SizedFile> >&) (/root/fuzzing/jansson/prog+0x45ad85)
    #12 0x44973e in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/root/fuzzing/jansson/prog+0x44973e)
    #13 0x472582 in main (/root/fuzzing/jansson/prog+0x472582)                                                                                                                                                  #14 0x7f229f1810b2 in __libc_start_main /build/glibc-YYA7BZ/glibc-2.31/csu/../csu/libc-start.c:308:16
    #15 0x41e4dd in _start (/root/fuzzing/jansson/prog+0x41e4dd)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /root/jansson-2.13/src/load.c:893:7 in string_get
==1898031==ABORTING
MS: 2 InsertRepeatedBytes-CopyPart-; base unit: 97d170e1550eee4afc0af065b78cda302a97674c
0x5b,0x5d,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67
,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x6
7,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x
67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0
x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,0x67,
[]gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg
artifact_prefix='./'; Test unit written to ./crash-76b9ab07b593b8511d24aea00b925d60735e9d6c
Base64: W11nZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dn
Z2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dn
```

The fuzzer I used was:
```
#include <jansson.h>
#include <stdio.h>

int LLVMFuzzerTestOneInput(const char *Data, size_t Size) {
        FILE *devNull;

        devNull = fopen("/dev/null", "w");

        json_t *root;
        json_error_t error;

        root = json_loads(Data, 0, &error);

        json_dumpf(root, devNull, 0);
        json_decref(root);

        fclose(devNull);

        return 0;
}
```

Kind Regards,

Jordy Zomer



Comments:
Comment by carnil on 2021-04-26 20:16:16+00:00:
This issue appears to have been assigned CVE-2020-36325.

---

Comment by DavidKorczynski on 2021-04-27 23:14:52+00:00:
This is not a bug. `json_loads` expects a null-terminated string - the above fuzzer is invalid as it does not ensure proper null-termination. This is how a lot of C functions operate: think `strlen` - or basically any other function in `string.h` - you will find OOBs all over libc if you don't provide null-terminated strings. 

If this was really assigned a CVE I recon it should be retracted. 

---

Comment by akheron on 2021-04-28 03:19:21+00:00:
Thanks for the analysis @DavidKorczynski 

---

Comment by carnil on 2021-04-28 04:10:11+00:00:
Thanks @DavidKorczynski for the analysis. In this case probably the
CVE should be rejected. Note I was not the one requesting int but just
sent a request to reject it via https://cveform.mitre.org/ 


---

Comment by carnil on 2021-04-28 19:12:19+00:00:
@DavidKorczynski FTR it got not REJECTED but now marked as "disputed", cf. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-36325

---

Comment by DavidKorczynski on 2021-04-28 19:15:52+00:00:
Interesting, thanks for the update! I wonder how this plays out - I would think the CVE team must have some means of handling false positives in particular with memory unsafe languages and libraries. 

---

Comment by DavidKorczynski on 2021-04-28 19:19:37+00:00:
For reference "_When one party disagrees with another party's assertion that a particular issue in software is a vulnerability, a CVE Record assigned to that issue may be designated as being "DISPUTED". In these cases, **the CVE Program is making no determination as to which party is correct**._" from here https://cve.mitre.org/about/faqs.html#disputed_signify_in_cve_record 

However, in this case it seems rejected is the correct classification: 
"_A CVE Record listed as "REJECT" is a CVE Record that is not accepted as a CVE Record. The reason a CVE Record is marked REJECT will most often be stated in the description of the CVE Record. Possible examples include it being a duplicate CVE Record, it being withdrawn by the original requester, **it being assigned incorrectly**, or some other administrative reason._" from the same URL. 

---

Comment by DavidKorczynski on 2021-04-28 19:21:10+00:00:
Anyways - issue is closed and it's no big deal. 

---

Comment by vtamara on 2022-08-01 11:27:10+00:00:
It is well known that functions like strlen that depend on null-terminated strings exclusively are not safe.  But what about improving the API of jansson? `json_loads` could receive a fourth parameter with the maximal length of the parameter data.

Commit References:
97d170e1550eee4afc0af065b78cda302a97674c
76b9ab07b593b8511d24aea00b925d60735e9d6c

Build script:
#!/bin/bash
# Default build script template for C/C++ projects
set -eu

# Define configuration options
CONFIG_OPTS="--disable-shared"
CMAKE_OPTS="-DCMAKE_BUILD_TYPE=Debug"
MAKE_OPTS="-j$(nproc)"
BUILD_DIR="build"

# 1. Run build preparation scripts if they exist
for prep_script in buildconf autogen.sh bootstrap; do
  if [ -x "$prep_script" ]; then
    echo "Running $prep_script..."
    ./$prep_script
    break
  elif [ -f "$prep_script" ]; then
    echo "Found $prep_script but it's not executable, setting permissions..."
    chmod +x $prep_script
    ./$prep_script
    break
  fi
done

# 2. Handle different build systems
if [ -f "configure" ]; then
  echo "Found configure script, running with options: $CONFIG_OPTS"
  ./configure $CONFIG_OPTS

  echo "Building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -f "CMakeLists.txt" ]; then
  echo "Found CMakeLists.txt, using CMake build system"
  mkdir -p $BUILD_DIR
  cd $BUILD_DIR
  cmake $CMAKE_OPTS ..
  make $MAKE_OPTS
elif [ -f "Makefile" ] || [ -f "makefile" ]; then
  echo "Found Makefile, building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -f "meson.build" ]; then
  echo "Found meson.build, using Meson build system"
  mkdir -p $BUILD_DIR
  meson setup $BUILD_DIR .
  cd $BUILD_DIR
  ninja
elif [ -f "GNUmakefile" ]; then
  echo "Found GNUmakefile, building with make $MAKE_OPTS"
  make $MAKE_OPTS
elif [ -d ".git" ] && [ -f "Makefile.am" ]; then
  echo "Found Makefile.am, running autoreconf"
  autoreconf -i
  ./configure $CONFIG_OPTS
  make $MAKE_OPTS
else
  echo "WARNING: No recognized build system found!"
  echo "Trying generic approach:"
  
  # Try to detect common source patterns and compile them
  if ls *.c >/dev/null 2>&1 || ls *.cpp >/dev/null 2>&1 || ls *.cc >/dev/null 2>&1; then
    echo "Found C/C++ source files, attempting direct compilation"
    if ls *.cpp >/dev/null 2>&1 || ls *.cc >/dev/null 2>&1; then
      CXX=${CXX:-g++}
      echo "Compiling C++ sources with $CXX"
      $CXX -g -O1 -fsanitize=address -fno-omit-frame-pointer -o main *.cpp *.cc 2>/dev/null || true
    elif ls *.c >/dev/null 2>&1; then
      CC=${CC:-gcc}
      echo "Compiling C sources with $CC"
      $CC -g -O1 -fsanitize=address -fno-omit-frame-pointer -o main *.c 2>/dev/null || true
    fi
  fi
fi

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

Additional files: