Source: libsndfile
Version: 1.2.2-4
Severity: important
Tags: security upstream patch
X-Debbugs-Cc: [email protected]

Hi,

The W64 and RF64 readers accept repeated 'fmt ' chunks. A later chunk can
raise sf.channels while psf->channel_map still has the size allocated for
the first chunk (wavlike_read_fmt_chunk only reallocates it in the
WAVE_FORMAT_EXTENSIBLE branch). SFC_GET_CHANNEL_MAP_INFO then checks the
caller's datasize against the new channel count and copies that many
entries out of the old, smaller buffer. wav.c already ignores a second
'fmt ' chunk; rf64.c and w64.c do not.

This is upstream issue 968, which is still open and reports the crash
through sndfile-convert (copy_metadata) on an RF64 file. I found no CVE for
it, and nothing about it in the tracker or the open libsndfile bugs.

Upstream fix: commit 59f33be, "rf64, w64: ignore a second 'fmt ' chunk"
(from pull request 1149). It is on master; there has been no release since
1.2.2.

Tested on 1.2.2-4
-----------------
I built the 1.2.2-4 source with all Debian patches applied, using clang
with ASan and UBSan, and opened the W64 test input that upstream's commit
adds to tests/command_test.c (first 'fmt ' has 1 channel and is extensible,
second claims 64 channels). A small program then called
SFC_GET_CHANNEL_MAP_INFO with a buffer sized for sf.channels:

AddressSanitizer: heap-buffer-overflow, READ of size 256
in sf_command
0 bytes after the 4-byte region allocated in
wavlike_read_fmt_chunk (wavlike.c:351)

The bytes read end up in the caller's buffer as channel map entries, so
this is an information leak as well as a possible crash. The size of the
over-read follows the channel count in the file.

With the source hunks of 59f33be applied, the same input opens as 1
channel with no sanitizer report. The project's test suite passes 143 of
143 both with and without the patch.

I only saw the ASan report through the W64 path. Upstream's RF64 test
input does not create a channel map, so it produced no report, but it did
open as 64 channels without the patch and 1 channel with it, and the
missing check in rf64.c is the same.

Other suites
------------
The attached patch (the rf64.c and w64.c hunks of 59f33be with DEP-3
headers) passes patch --dry-run against 1.2.2-4 and against the patched
sources of 1.2.2-2+deb13u1 (trixie) and 1.2.0-1+deb12u1 (bookworm). I did
not build or test those two.

Regards,
Sumit Verma
Description: rf64, w64: ignore a second 'fmt ' chunk
 A second 'fmt ' chunk can raise sf.channels after the channel map (allocated
 for the first chunk's channel count) has been set up, so
 SFC_GET_CHANNEL_MAP_INFO copies past the end of that heap buffer. wav.c
 already ignores repeated 'fmt ' chunks. Source hunks of the upstream commit,
 without its test changes.
Origin: upstream, https://github.com/libsndfile/libsndfile/commit/59f33be
Bug: https://github.com/libsndfile/libsndfile/issues/968
Last-Update: 2026-11-08
---
diff --git a/src/rf64.c b/src/rf64.c
index 3b890e6..9bedddd 100644
--- a/src/rf64.c
+++ b/src/rf64.c
@@ -257,6 +257,12 @@ rf64_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock)
 				break ;
 
 			case fmt_MARKER:
+					if (parsestage & HAVE_fmt)
+					{	psf_log_printf (psf, "*** Second 'fmt ' chunk?\n") ;
+						psf_binheader_readf (psf, "j", chunk_size) ;
+						break ;
+						} ;
+
 					psf_log_printf (psf, "%M : %u\n", marker, chunk_size) ;
 					if ((error = wavlike_read_fmt_chunk (psf, chunk_size)) != 0)
 						return error ;
diff --git a/src/w64.c b/src/w64.c
index fa5ade3..8ff3914 100644
--- a/src/w64.c
+++ b/src/w64.c
@@ -277,6 +277,11 @@ w64_read_header	(SF_PRIVATE *psf, int *blockalign, int *framesperblock)
 					if ((parsestage & (HAVE_riff | HAVE_wave)) != (HAVE_riff | HAVE_wave))
 						return SFE_WAV_NO_FMT ;
 
+					if (parsestage & HAVE_fmt)
+					{	psf_log_printf (psf, "*** Second 'fmt ' chunk?\n") ;
+						break ;
+						} ;
+
 					psf_log_printf (psf, " fmt : %D\n", chunk_size) ;
 
 					/* size of 16 byte marker and 8 byte chunk_size value. */

Reply via email to