From 4ec860910a4ee91ed4fdf1c0a49f2dad96d595c9 Mon Sep 17 00:00:00 2001
From: Alex Stewart <alex.stewart@ni.com>
Date: Mon, 16 Oct 2023 12:37:47 -0400
Subject: [PATCH 12/17] common: fix int overflow in psf_binheader_readf()

The psf_binheader_readf() function attempts to count and return the
number of bytes traversed in the header. During this accumulation, it is
possible to overflow the int-sized byte_count variable.

Avoid this overflow by checking that the accumulated bytes do not exceed
INT_MAX and throwing an error if they do. This implies that files with
multi-gigabyte headers threaten to produce this error, but I imagine
those files don't really exist - and this error is better than the
undefined behavior which would have resulted previously.

CVE: CVE-2022-33065
Fixes: https://github.com/libsndfile/libsndfile/issues/833

Signed-off-by: Alex Stewart <alex.stewart@ni.com>

Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/libsndfile/tree/debian/patches/CVE-2022-33065/CVE-2022-33065-8.patch?h=ubuntu/jammy-security
Upstream commit https://github.com/libsndfile/libsndfile/commit/4ec860910a4ee91ed4fdf1c0a49f2dad96d595c9]
CVE: CVE-2022-33065
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
---
 src/common.c | 36 ++++++++++++++++++++++++------------
 1 file changed, 24 insertions(+), 12 deletions(-)

--- libsndfile-1.0.31.orig/src/common.c
+++ libsndfile-1.0.31/src/common.c
@@ -18,6 +18,7 @@
 
 #include <config.h>
 
+#include <limits.h>
 #include <stdarg.h>
 #include <string.h>
 #if HAVE_UNISTD_H
@@ -962,6 +963,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 	double			*doubleptr ;
 	char			c ;
 	int				byte_count = 0, count = 0 ;
+	int				read_bytes = 0 ;
 
 	if (! format)
 		return psf_ftell (psf) ;
@@ -970,6 +972,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 
 	while ((c = *format++))
 	{
+		read_bytes = 0 ;
 		if (psf->header.indx + 16 >= psf->header.len && psf_bump_header_allocation (psf, 16))
 			return count ;
 
@@ -986,7 +989,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 					intptr = va_arg (argptr, unsigned int*) ;
 					*intptr = 0 ;
 					ucptr = (unsigned char*) intptr ;
-					byte_count += header_read (psf, ucptr, sizeof (int)) ;
+					read_bytes = header_read (psf, ucptr, sizeof (int)) ;
 					*intptr = GET_MARKER (ucptr) ;
 					break ;
 
@@ -994,7 +997,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 					intptr = va_arg (argptr, unsigned int*) ;
 					*intptr = 0 ;
 					ucptr = (unsigned char*) intptr ;
-					byte_count += header_read (psf, sixteen_bytes, sizeof (sixteen_bytes)) ;
+					read_bytes = header_read (psf, sixteen_bytes, sizeof (sixteen_bytes)) ;
 					{	int k ;
 						intdata = 0 ;
 						for (k = 0 ; k < 16 ; k++)
@@ -1006,14 +1009,14 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case '1' :
 					charptr = va_arg (argptr, char*) ;
 					*charptr = 0 ;
-					byte_count += header_read (psf, charptr, sizeof (char)) ;
+					read_bytes = header_read (psf, charptr, sizeof (char)) ;
 					break ;
 
 			case '2' : /* 2 byte value with the current endian-ness */
 					shortptr = va_arg (argptr, unsigned short*) ;
 					*shortptr = 0 ;
 					ucptr = (unsigned char*) shortptr ;
-					byte_count += header_read (psf, ucptr, sizeof (short)) ;
+					read_bytes = header_read (psf, ucptr, sizeof (short)) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						*shortptr = GET_BE_SHORT (ucptr) ;
 					else
@@ -1023,7 +1026,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case '3' : /* 3 byte value with the current endian-ness */
 					intptr = va_arg (argptr, unsigned int*) ;
 					*intptr = 0 ;
-					byte_count += header_read (psf, sixteen_bytes, 3) ;
+					read_bytes = header_read (psf, sixteen_bytes, 3) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						*intptr = GET_BE_3BYTE (sixteen_bytes) ;
 					else
@@ -1034,7 +1037,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 					intptr = va_arg (argptr, unsigned int*) ;
 					*intptr = 0 ;
 					ucptr = (unsigned char*) intptr ;
-					byte_count += header_read (psf, ucptr, sizeof (int)) ;
+					read_bytes = header_read (psf, ucptr, sizeof (int)) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						*intptr = psf_get_be32 (ucptr, 0) ;
 					else
@@ -1044,7 +1047,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case '8' : /* 8 byte value with the current endian-ness */
 					countptr = va_arg (argptr, sf_count_t *) ;
 					*countptr = 0 ;
-					byte_count += header_read (psf, sixteen_bytes, 8) ;
+					read_bytes = header_read (psf, sixteen_bytes, 8) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						countdata = psf_get_be64 (sixteen_bytes, 0) ;
 					else
@@ -1055,7 +1058,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case 'f' : /* Float conversion */
 					floatptr = va_arg (argptr, float *) ;
 					*floatptr = 0.0 ;
-					byte_count += header_read (psf, floatptr, sizeof (float)) ;
+					read_bytes = header_read (psf, floatptr, sizeof (float)) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						*floatptr = float32_be_read ((unsigned char*) floatptr) ;
 					else
@@ -1065,7 +1068,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case 'd' : /* double conversion */
 					doubleptr = va_arg (argptr, double *) ;
 					*doubleptr = 0.0 ;
-					byte_count += header_read (psf, doubleptr, sizeof (double)) ;
+					read_bytes = header_read (psf, doubleptr, sizeof (double)) ;
 					if (psf->rwf_endian == SF_ENDIAN_BIG)
 						*doubleptr = double64_be_read ((unsigned char*) doubleptr) ;
 					else
@@ -1089,7 +1092,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 					charptr = va_arg (argptr, char*) ;
 					count = va_arg (argptr, size_t) ;
 					memset (charptr, 0, count) ;
-					byte_count += header_read (psf, charptr, count) ;
+					read_bytes = header_read (psf, charptr, count) ;
 					break ;
 
 			case 'G' :
@@ -1100,7 +1103,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 					if (psf->header.indx + count >= psf->header.len && psf_bump_header_allocation (psf, count))
 						return 0 ;
 
-					byte_count += header_gets (psf, charptr, count) ;
+					read_bytes = header_gets (psf, charptr, count) ;
 					break ;
 
 			case 'z' :
@@ -1124,7 +1127,7 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 			case 'j' :	/* Seek to position from current position. */
 					count = va_arg (argptr, size_t) ;
 					header_seek (psf, count, SEEK_CUR) ;
-					byte_count += count ;
+					read_bytes = count ;
 					break ;
 
 			default :
@@ -1132,8 +1135,17 @@ psf_binheader_readf (SF_PRIVATE *psf, ch
 				psf->error = SFE_INTERNAL ;
 				break ;
 			} ;
+
+		if (read_bytes > 0 && byte_count > (INT_MAX - read_bytes))
+		{	psf_log_printf (psf, "Header size exceeds INT_MAX. Aborting.", c) ;
+			psf->error = SFE_INTERNAL ;
+			break ;
+		} else
+		{	byte_count += read_bytes ;
 		} ;
 
+		} ;	/*end while*/
+
 	va_end (argptr) ;
 
 	return byte_count ;
