Project: libsndfile (libsndfile/libsndfile), language: c++
Vulnerability instance: libsndfile.cve-2024-50612
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/libsndfile/libsndfile/issues/1035
## Description:
Issue: libsndfile/libsndfile#1035
Title: Segmentation fault error in ogg_vorbis.c:417 vorbis_analysis_wrote()
State: closed
Created by: 4n0nym4u5
Created at: 2024-08-29 05:44:12+00:00

Issue Body:
## Description
The latest version of libsndfile was discovered to contain  a memory corruption vulnerability in ogg_vorbis.c:417 vorbis_analysis_wrote() when parsing a specially crafted input file. This vulnerability leads to Denial of Service (DoS). 

## ASAN Log
```c
Summary: CRASH detected in vorbis_analysis_wrote due to a fault at or near 0x0000000000000030 leading to SIGSEGV (si_signo=11) / SEGV_MAPERR (si_code=1)
Command line: ./sndfile-convert @@ /tmp/Master.ogg
Testcase: out/cpu03/crashes/id:000047,sig:11,src:001542,time:8004993,execs:855790,op:quick,pos:84
Crash bucket: 8b83505d9a486363d6fe62258b78fea5

Crashing thread backtrace:
#0  0x00007ffff7df044b in vorbis_analysis_wrote (/lib/x86_64-linux-gnu/libvorbis.so.0)
#1  0x00005555557ce546 in vorbis_close (/home/user/fuzzing_targets/libsndfile/CMakeBuild/sndfile-convert)
                       402: int vorbis_close(psf = (SF_PRIVATE *)0x625000002900) {
                       |||:
                       |||: /* Local reference: SF_PRIVATE * psf = 0x625000002900; */
                       |||: /* Local reference: VORBIS_PRIVATE * vdata = 0x615000000080; */
                       415: 			vorbis_write_header (psf, 0) ;
                       416: 
                       417: 		vorbis_analysis_wrote (&vdata->vdsp, 0) ;
                       |||:
                       ---: }
                       at /home/user/fuzzing_targets/libsndfile/src/ogg_vorbis.c:417

#2  0x00005555556863f4 in psf_close (/home/user/fuzzing_targets/libsndfile/CMakeBuild/sndfile-convert)
                       2964: int psf_close(psf = (SF_PRIVATE *)0x625000002900) {
                       ||||:
                       ||||: /* Local reference: SF_PRIVATE * psf = 0x625000002900; */
                       ||||: /* Local reference: int error = 0; */
                       2967: 
                       2968: 	if (psf->codec_close)
                       2969: 	{	error = psf->codec_close (psf) ;
                       ||||:
                       ----: }
                       at /home/user/fuzzing_targets/libsndfile/src/sndfile.c:2969

#3  0x0000555555683219 in main (/home/user/fuzzing_targets/libsndfile/CMakeBuild/sndfile-convert)
                       134: int main(argc = (int)<optimized out>, argv = (char **)<optimized out>) {
                       |||:
                       |||: /* Local reference: SNDFILE * infile = 0x625000000100; */
                       |||: /* Local reference: SNDFILE * outfile = 0x625000002900; */
                       368: 
                       369: 	sf_close (infile) ;
                       370: 	sf_close (outfile) ;
                       |||:
                       ---: }
                       at /home/user/fuzzing_targets/libsndfile/programs/sndfile-convert.c:370

Crash context:
/* Register reference: r14 - 0x0000625000000100 (108095736905984) */
/* Register reference: r13 - 0x0000000000000000 (0) */
Execution stopped here ==> 0x00007ffff7df044b: mov    r14,QWORD PTR [r13+0x30]

Register info:
    rax - 0x0000000000000000 (0)
    rbx - 0x00006150000000e0 (106996225278176)
    rcx - 0x0000555556259180 (93825005883776)
    rdx - 0x0000000000000002 (2)
    rsi - 0x0000000000000000 (0)
    rdi - 0x00006150000000e0 (106996225278176)
    rbp - 0x00007fffffff5af0 (0x7fffffff5af0)
    rsp - 0x00007fffffff5ab0 (0x7fffffff5ab0)
     r8 - 0x0000000000001698 (5784)
     r9 - 0x00007ffff5c00b60 (140737316391776)
    r10 - 0x00007ffff7de5548 (140737351931208)
    r11 - 0x00007ffff7df0420 (140737351975968)
    r12 - 0x0000627000000100 (108233175859456)
    r13 - 0x0000000000000000 (0)
    r14 - 0x0000625000000100 (108095736905984)
    r15 - 0x0000625000002900 (108095736916224)
    rip - 0x00007ffff7df044b (0x7ffff7df044b <vorbis_analysis_wrote+43>)
 eflags - 0x00010246 ([ PF ZF IF RF ])
     cs - 0x00000033 (51)
     ss - 0x0000002b (43)
     ds - 0x00000000 (0)
     es - 0x00000000 (0)
     fs - 0x00000000 (0)
     gs - 0x00000000 (0)
fs_base - 0x00007ffff79fd800 (140737347835904)
gs_base - 0x0000000000000000 (0)
```
## sndfile-info
```yaml
File : /REDACTED/poc
Length : 284
RF64 : 0x45564157 (should be 0xFFFFFFFF)
  W
fmt  : 50
  Format        : 0x1 => WAVE_FORMAT_PCM
  Channels      : 1
  Sample Rate   : 11025
  Block Align   : 256
  Bit Width     : 4
  Bytes/sec     : 5645 (should be 2822400)
fmt  : 50
  Format        : 0x1 => WAVE_FORMAT_PCM
  Channels      : 1
  Sample Rate   : 11938577
  Block Align   : 256
  Bit Width     : 28
  Bytes/sec     : 5645 (should be -1238691584)
*** fact : 4 (unknown marker)
data : 0x1700
**** Weird, RF64 file without a 'ds64' chunk and no valid 'data' size.
End
*** Calculated frame count 34 does not match value from 'ds64' chunk of 0.

----------------------------------------
Sample Rate : 11938577
Frames      : 34
Channels    : 1
Format      : 0x00220004
Sections    : 1
Seekable    : TRUE
Duration    : 00:00:00.000
Signal Max  : 9.89862e+08 (-6.73 dB)
```

## Proof-of-Concept Files
[poc](https://github.com/4n0nym4u5/CVE/raw/main/libsndfile/segmentation_fault/poc)


## Reproduction
```sh
git clone https://github.com/libsndfile/libsndfile
mkdir CMakeBuild
cd CMakeBuild
CC=clang CXX=clang++  cmake .. 
make
./sndfile-convert poc a.ogg
```

## Results
```sh
➜  CMakeBuild git:(master) ✗ ./sndfile-convert poc /tmp/a.ogg                                                                                    
AddressSanitizer:DEADLYSIGNAL
=================================================================
==1208976==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000030 (pc 0x7ffff7ded44b bp 0x7fffffff5d10 sp 0x7fffffff5cd0 T0)
==1208976==The signal is caused by a READ memory access.
==1208976==Hint: address points to the zero page.
    #0 0x7ffff7ded44b in vorbis_analysis_wrote (/lib/x86_64-linux-gnu/libvorbis.so.0+0xc44b) (BuildId: 58a44b2feaebf749cc199712b9da8b0a72529304)
    #1 0x5555557ce545 in vorbis_close /home/user/fuzzing_targets/libsndfile/src/ogg_vorbis.c:417:3
    #2 0x5555556863f3 in psf_close /home/user/fuzzing_targets/libsndfile/src/sndfile.c:2969:12
    #3 0x555555683218 in main /home/user/fuzzing_targets/libsndfile/programs/sndfile-convert.c:370:2
    #4 0x7ffff7a2a1c9 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #5 0x7ffff7a2a28a in __libc_start_main csu/../csu/libc-start.c:360:3
    #6 0x5555555c1e84 in _start (/home/user/fuzzing_targets/libsndfile/CMakeBuild/sndfile-convert+0x6de84) (BuildId: cbada87c9425ff09ce9f7616c3a774b7f2e3be61)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/lib/x86_64-linux-gnu/libvorbis.so.0+0xc44b) (BuildId: 58a44b2feaebf749cc199712b9da8b0a72529304) in vorbis_analysis_wrote
==1208976==ABORTING

```

## Environment
```
Linux Mint 22 Wilma 
Ubuntu clang version 18.1.3 (1ubuntu1)
gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0
```


Comments:
Comment by carnil on 2024-10-28 21:03:06+00:00:
This issue appears to be associated with CVE-2024-50612

---

Comment by sboukortt on 2024-11-06 13:01:01+00:00:
This appears to prevent the crash; not sure whether there is maybe a more “proper” fix.

```diff
diff --git a/src/ogg_vorbis.c b/src/ogg_vorbis.c
index f9428ed1..c1061ca2 100644
--- a/src/ogg_vorbis.c
+++ b/src/ogg_vorbis.c
@@ -403,7 +403,7 @@ vorbis_close (SF_PRIVATE *psf)
 {      OGG_PRIVATE* odata = psf->container_data ;
        VORBIS_PRIVATE *vdata = psf->codec_data ;
 
-       if (odata == NULL || vdata == NULL)
+       if (odata == NULL || vdata == NULL || vdata->vdsp.vi == NULL)
                return 0 ;
 
        /*      Clean up this logical bitstream ; before exit we shuld see if we're
```

---

Comment by erikd on 2024-11-12 04:40:58+00:00:
@sboukortt 's patch looks good to me. I recommend that it be added. @sboukortt Maybe create a PR?

---

Comment by arthurt on 2024-11-15 23:39:13+00:00:
@sboukortt `vorbis_dsp_state` is supposed to be private: [docs](https://xiph.org/vorbis/doc/libvorbis/vorbis_dsp_state.html)

The actual issue is that the error return value from `vorbis_write_header()` called from `vorbis_close()` is not checked.

```c

if (psf->file.mode == SFM_WRITE)
{
	if (psf->write_current <= 0)
		vorbis_write_header (psf, 0) ;    // retval unchecked

	vorbis_analysis_wrote (&vdata->vdsp, 0) ; // vdata->vdsp is uninitialized
```

There isn't anything special about the POC. The issue is that an OggVorbis file is opened for writing with a sample format vorbis can't support, one write is attempted, then the file is closed. The vorbis sndfile code writes the header lazily, either at the first write of sample data or at close for a sample-empty file, and the vorbis sndfile code doesn't bother to init the codec state until it writes the headers.

Writing the header fails on the first write, and the header remains unwritten. On close it is noticed that the header is not written and it is tried again, but this time the error is ignored. The codec state remains uninitialized and `vorbis_analysis_wrote` causes a seg-fault.

```
gdb sndfile-covert
Reading symbols from sndfile-convert...
(gdb) break vorbis_write_header
Breakpoint 1 at 0x7ffff7f48c8f: file src/ogg_vorbis.c, line 311.
(gdb) break vorbis_close
Breakpoint 2 at 0x7ffff7f49081: file src/ogg_vorbis.c, line 403.

(gdb) r ../issue-1035/poc a.ogg
Breakpoint 1, vorbis_write_header (psf=0x55555556bd40, UNUSED_calc_length=0) at src/ogg_vorbis.c:311
311     {
(gdb) bt
#0  vorbis_write_header (psf=0x55555556bd40, UNUSED_calc_length=0) at src/ogg_vorbis.c:311
#1  0x00007ffff7f1635f in sf_writef_double (sndfile=0x55555556bd40, ptr=0x55555555c2a0 <data>, frames=34)
    at src/sndfile.c:2661
#2  0x0000555555557621 in sfe_copy_data_fp (outfile=0x55555556bd40, infile=0x5555555692a0, channels=1, normalize=0)
    at programs/common.c:78
#3  0x000055555555716a in main (argc=3, argv=0x7fffffffd6b8) at programs/sndfile-convert.c:361
(gdb) finish
Run till exit from #0  vorbis_write_header (psf=0x55555556bd40, UNUSED_calc_length=0) at src/ogg_vorbis.c:311
0x00007ffff7f1635f in sf_writef_double (sndfile=0x55555556bd40, ptr=0x55555555c2a0 <data>, frames=34) at src/sndfile.c:2661
2661            {       if ((psf->error = psf->write_header (psf, SF_FALSE)))
Value returned is $1 = 1
(gdb) c
Continuing.

Breakpoint 2, vorbis_close (psf=0x55555556bd40) at src/ogg_vorbis.c:403
403     {       OGG_PRIVATE* odata = psf->container_data ;
(gdb) c
Continuing.

Breakpoint 1, vorbis_write_header (psf=0x55555556bd40, UNUSED_calc_length=0) at src/ogg_vorbis.c:311
311     {
(gdb) finish
Run till exit from #0  vorbis_write_header (psf=0x55555556bd40, UNUSED_calc_length=0) at src/ogg_vorbis.c:311
vorbis_close (psf=0x55555556bd40) at src/ogg_vorbis.c:417
417                     vorbis_analysis_wrote (&vdata->vdsp, 0) ;
Value returned is $2 = 1
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7e5902b in vorbis_analysis_wrote () from /usr/lib64/libvorbis.so.0
```
```

Commit References:
cbada87c9425ff09ce9f7616c3a774b7f2e3be61
58a44b2feaebf749cc199712b9da8b0a72529304

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/libsndfile/libsndfile libsndfile
RUN git -C libsndfile checkout 72f6af15e8f85157bd622ed45b979025828b7001
WORKDIR $SRC/libsndfile
COPY build.sh $SRC/

Additional files: