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

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/libsndfile/libsndfile/issues/832
## Description:
Issue: libsndfile/libsndfile#832
Title: UndefinedBehaviorSanitizer: index 100 out of bounds for type 'SF_CUE_POINT [100]'
State: open
Created by: pietroborrello
Created at: 2022-04-29 15:31:03+00:00

Issue Body:
##### **Describe the bug**
UndefinedBehaviorSanitizer: index 100 out of bounds for type 'SF_CUE_POINT [100]' in wav.c:524

##### **To Reproduce**
Built libsndfile using clang-10 according to [the oss-fuzz script](https://github.com/google/oss-fuzz/blob/master/projects/libsndfile/build.sh) with `CXXFLAGS='-O1 -fsanitize=address -fsanitize=array-bounds,bool,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,null,object-size,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr'`

commit: 4b01368fd8a1e4541d7a5d125f70e99d30851718

##### **UBSAN Output**

```
$ ./sndfile_alt_fuzzer id:000051,sig:06,src:002454+003908,time:5669051,op:splice,rep:8,trial:2
INFO: Seed: 3446105526
INFO: Loaded 1 modules   (33759 inline 8-bit counters): 33759 [0x8977c3, 0x89fba2), 
INFO: Loaded 1 PC tables (33759 PCs): 33759 [0x6f6b48,0x77a938), 
sndfile_alt_fuzzer: Running 1 inputs 1 time(s) each.
Running: id:000051,sig:06,src:002454+003908,time:5669051,op:splice,rep:8,trial:2
src/wav.c:524:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:524:8 in 
src/wav.c:525:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:525:8 in 
src/wav.c:526:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:526:8 in 
src/wav.c:527:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:527:8 in 
src/wav.c:528:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:528:8 in 
src/wav.c:529:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:529:8 in 
src/wav.c:530:8: runtime error: index 100 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior src/wav.c:530:8 in 
Executed id:000051,sig:06,src:002454+003908,time:5669051,op:splice,rep:8,trial:2 in 2 ms
```

testcase:
[idx out of bound.zip](https://github.com/libsndfile/libsndfile/files/8591945/idx.out.of.bound.zip)




Comments:
Comment by rfrohl on 2023-07-19 08:42:39+00:00:
FTR: someone also assigned a CVE to this bug report: [CVE-2022-33064](https://nvd.nist.gov/vuln/detail/CVE-2022-33064)

---

Comment by ellenjohnson on 2023-08-01 00:59:06+00:00:
Hello! 
Can someone please provide an update on a timeframe for fixing this issue?  We current use libsndfile v1.1.0 and [CVE-2022-33064](https://nvd.nist.gov/vuln/detail/CVE-2022-33064) was reported to our team as a high severity security bug.
Thanks!  

---

Comment by ellenjohnson on 2023-08-11 21:19:56+00:00:
Any updates on this?  It's a high severity security bug on our plate.  Thanks!

---

Comment by evpobr on 2023-08-12 05:33:53+00:00:
Hi @ellenjohnson ! Unfortunately not plans to fix it yet. Maybe you can help us?

---

Comment by ellenjohnson on 2023-08-14 17:37:22+00:00:
Hi @evpobr - thank you for letting us know there are no plans to fix this yet.  

---

Comment by kulikjak on 2023-09-01 06:47:42+00:00:
Hi, I don't have ASAN available and I have almost no prior knowledge of libsndfile code base; however, I looked a little into this (in version 1.1.0 - nothing relevant seems to have changed since), and I think this might be a false positive.

I added some debug prints and verified that the supplied input doesn't do anything unexpected.

The input file reports that it has 1024 cues, [`psf_cues_alloc`](https://github.com/libsndfile/libsndfile/blob/48535fc2ab7a08596474ad9104bb14e26518385e/src/common.c#L1325) allocates a structure that is 286724 bytes in size (4 bytes for cue_count and 1024*280 for all points), and then only 120 cues are actually present in the file, so it writes nowhere close to the end of the allocated memory.

My guess is that the [following definition](https://github.com/libsndfile/libsndfile/blob/48535fc2ab7a08596474ad9104bb14e26518385e/include/sndfile.h#L440) in `sndfile.h` makes it think that `SF_CUES` has 100 `cue_points`:
```C
typedef struct
{	int32_t 	indx ;
	uint32_t 	position ;
	int32_t 	fcc_chunk ;
	int32_t 	chunk_start ;
	int32_t		block_start ;
	uint32_t 	sample_offset ;
	char name [256] ;
} SF_CUE_POINT ;

#define	SF_CUES_VAR(count) \
	struct \
	{	uint32_t cue_count ; \
		SF_CUE_POINT cue_points [count] ; \
	}

typedef SF_CUES_VAR (100) SF_CUES ;
```

which is a good guess, but that is not the case when allocated with `psf_cues_alloc` as it allocates a buffer of `(sizeof (SF_CUES_0) + count * sizeof (SF_CUE_POINT))` bytes.

All that said, I might have overlooked something important, so take it with a grain of salt.


---

Comment by mhlavink on 2023-10-04 10:07:05+00:00:
I have checked this, then did some evaluations for boundary conditions and ended up with same conclusion as kulikjak. I think that it is confused about how much memory is really allocated and this is false positive.

---

Comment by ellenjohnson on 2023-10-19 19:35:42+00:00:
Thank you @kulikjak and @mhlavink for looking into this and for investigating whether this might be a false positive.
@evpobr:  Can you or someone else from libsndfile confirm whether this CVE is a real issue or a false positive?
Thanks!

---

Comment by ellenjohnson on 2024-01-12 18:18:40+00:00:
Hi @evpobr -- we're still tracking this security issue since it's still listed as a high-score CVE at https://nvd.nist.gov/vuln/detail/CVE-2022-33064.   
I see also Linux distros Debian and Ubuntu are also tracking this as unresolved.
Would you or another developer at libsndfile please confirm what @kulikjak and @mhlavink suspect -- that this might be a false positive?  Thank you!

---

Comment by pgh24 on 2024-08-29 20:05:37+00:00:
The issue here is the odd implementation of flexible array members, earlier known as a struct hack. When the last element of a structure is intended to be a variable sized array, C99 uses the format:
```
struct foo {
  int size;
  int stuff[];
}
```
Earlier C variants used:
`  int stuff[0];`
  or even
`  int stuff[1];`
as the trailing element.

In the case here, the developer chose the arbitrary value of 100 even though the structure never appears to be used with this default size.
clang and presumably other static analysis tools see that the structure could be accessed beyond 100 values and raise an error, however the structure is correctly allocated with calloc().
Note that if the trailing element is size null, 0 or 1, clang will not attempt to validate array out of bounds, but will if 2 or greater.

Recommendation. Declare the structure with a trailing SF_CUE_POINT cue_points [];
i.e.
change
`typedef SF_CUES_VAR (100) SF_CUES;`
to
`typedef SF_CUES_VAR () SF_CUES;`


Here's some test code to illustrate the issue:

```
#include <stdio.h>
#include <stdlib.h>
 
typedef struct
{ int   indx;
  char name [256];
} SF_CUE_POINT;

#define SF_CUES_VAR(count) \
  struct \
  { int cue_count; \
    SF_CUE_POINT cue_points [count]; \
  }

typedef SF_CUES_VAR (100) SF_CUES;

typedef struct sf_private_tag
{
  int morespace;
  SF_CUES *cues;
} SF_PRIVATE;

typedef SF_CUES_VAR () SF_CUES_0;

#define SF_CUES_VAR_SIZE(count) (sizeof (SF_CUES_0) + count * sizeof (SF_CUE_POINT))

SF_CUES* psf_cues_alloc(int n)
{
  SF_CUES* pcues = calloc(1, SF_CUES_VAR_SIZE(n));
  printf("allocated size %ld\n", SF_CUES_VAR_SIZE(n));
  return pcues;
}

int
main(int ac, char *av[])
{
  SF_PRIVATE sf, *psf = &sf;
  SF_CUES cues1;
  int cue_count = atoi(av[1]);
  
  printf("sizeof SF_CUES %ld\n", sizeof(cues1));
  psf->cues = psf_cues_alloc (cue_count);
  printf("sizeof cues %ld\n", sizeof *psf->cues);
  psf->cues->cue_points[cue_count-1].indx = 1;
  if (ac > 2) {
    printf("trying out of bounds\n");
    psf->cues->cue_points[cue_count].indx = 1;
  }

  free(psf->cues);
}


```
Compiling with clang and running:
```
$ clang -fsanitize=array-bounds,address snd.c -o snd
$ ./snd 50
sizeof SF_CUES 26004
allocated size 13004
sizeof cues 26004
$ ./snd 200
sizeof SF_CUES 26004
allocated size 52004
sizeof cues 26004
snd.c:44:3: runtime error: index 199 out of bounds for type 'SF_CUE_POINT [100]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior snd.c:44:3 in 
```

If I change the `typedef SF_CUES_VAR line` to `()` instead of `100`

```
$ ./snd 50
sizeof SF_CUES 4
allocated size 13004
sizeof cues 4
$ ./snd 200
sizeof SF_CUES 4
allocated size 52004
sizeof cues 4
```

The out of bounds is still picked up on both occasions:

```
$ ./snd 50 1
sizeof SF_CUES 4
allocated size 13004
sizeof cues 4
trying out of bounds
=================================================================
==1310252==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6270000033cc at pc 0x0000004c349d bp 0x7ffcad5af450 sp 0x7ffcad5af448

$ ./snd 200 1
sizeof SF_CUES 4
allocated size 52004
sizeof cues 4
trying out of bounds
=================================================================
==1461440==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62f00000cf24 at pc 0x0000004c349d bp 0x7fff53ad3970 sp 0x7fff53ad3968
```




Attachments:
https://github.com/libsndfile/libsndfile/files/8591945/idx.out.of.bound.zip

Commit References:
48535fc2ab7a08596474ad9104bb14e26518385e
4b01368fd8a1e4541d7a5d125f70e99d30851718

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 ea3ac90e98c6a98cd52cae39010446fba368a2e3
WORKDIR $SRC/libsndfile
COPY build.sh $SRC/

Additional files: