CVE-2023-5377: Out-of-bounds Read (CWE-125)
value: Out-of-bounds Read in GitHub repository gpac/gpac prior to v2.2.2-DEV.
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.1
Published: 2023-10-04T10:15Z
Repository: https://github.com/gpac/gpac
Language: C
Files changed: include/gpac/isomedia.h, src/isomedia/box_code_base.c, src/isomedia/isom_write.c
Commit date: 2023-10-04 11:49:06 +0200
Commit message: fixed #2606

Diff: diff --git a/include/gpac/isomedia.h b/include/gpac/isomedia.h
index 09613122b35..44c7a42e85e 100644
--- a/include/gpac/isomedia.h
+++ b/include/gpac/isomedia.h
@@ -1463,13 +1463,27 @@ GF_Err gf_isom_get_audio_info(GF_ISOFile *isom_file, u32 trackNumber, u32 sample
 /*! Audio channel layout description, ISOBMFF style*/
 typedef struct
 {
 	/*! stream structure flags, 1: has channel layout, 2: has objects*/
 	u8 stream_structure;
+	/*!  order of formats in the stream : 0 unknown, 1: Channels, possibly followed by Objects, 2 Objects, possibly followed by Channels*/
+	u8 format_ordering;
+	/*! combined channel count of the channel layout and the object count*/
+	u8 base_channel_count;
 
 	/*! defined CICP channel layout*/
 	u8 definedLayout;
+	/*! indicates where the ordering of the audio channels for the definedLayout are specified
+	0: as listed for the ChannelConfigurations in ISO/IEC 23091-3
+	1: Default order of audio codec specification
+	2: Channel ordering #2 of audio codec specification
+	3: Channel ordering #3 of audio codec specification
+	4: Channel ordering #4 of audio codec specification
+	*/
+	u8 channel_order_definition;
+	/*! indicates if omittedChannelsMap is present*/
+	u8 omitted_channels_present;
 
 	/*! number of channels*/
 	u32 channels_count;
 	struct {
 		/*! speaker position*/
diff --git a/src/isomedia/box_code_base.c b/src/isomedia/box_code_base.c
index b89298cefdc..b269b555b7d 100644
--- a/src/isomedia/box_code_base.c
+++ b/src/isomedia/box_code_base.c
@@ -12674,35 +12674,67 @@ void chnl_box_del(GF_Box *s)
 GF_Err chnl_box_read(GF_Box *s,GF_BitStream *bs)
 {
 	GF_ChannelLayoutBox *ptr = (GF_ChannelLayoutBox *) s;
 
 	ISOM_DECREASE_SIZE(s, 1)
-	ptr->layout.stream_structure = gf_bs_read_u8(bs);
+	if (ptr->version==0) {
+		ptr->layout.stream_structure = gf_bs_read_u8(bs);
+	} else {
+		ptr->layout.stream_structure = gf_bs_read_int(bs, 4);
+		ptr->layout.format_ordering = gf_bs_read_int(bs, 4);
+		ISOM_DECREASE_SIZE(s, 1)
+		ptr->layout.base_channel_count = gf_bs_read_u8(bs);
+	}
 	if (ptr->layout.stream_structure & 1) {
 		ISOM_DECREASE_SIZE(s, 1)
 		ptr->layout.definedLayout = gf_bs_read_u8(bs);
 		if (ptr->layout.definedLayout==0) {
 			u32 remain = (u32) ptr->size;
 			if (ptr->layout.stream_structure & 2) remain--;
 			ptr->layout.channels_count = 0;
+			u32 nb_channels = 0;
+			if (ptr->version) {
+				ISOM_DECREASE_SIZE(s, 1)
+				nb_channels = gf_bs_read_u8(bs);
+			}
 			while (remain) {
+				if (ptr->layout.channels_count==64) return GF_ISOM_INVALID_FILE;
+
 				ISOM_DECREASE_SIZE(s, 1)
 				ptr->layout.layouts[ptr->layout.channels_count].position = gf_bs_read_u8(bs);
 				remain--;
 				if (ptr->layout.layouts[ptr->layout.channels_count].position == 126) {
 					ISOM_DECREASE_SIZE(s, 3)
 					ptr->layout.layouts[ptr->layout.channels_count].azimuth = gf_bs_read_int(bs, 16);
 					ptr->layout.layouts[ptr->layout.channels_count].elevation = gf_bs_read_int(bs, 8);
 					remain-=3;
 				}
+				ptr->layout.channels_count++;
+				if (ptr->version) {
+					nb_channels--;
+					if (!nb_channels) break;
+				}
 			}
 		} else {
-			ISOM_DECREASE_SIZE(s, 8)
-			ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
+			if (ptr->version==0) {
+				ISOM_DECREASE_SIZE(s, 8)
+				ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
+				ptr->layout.omitted_channels_present = 1;
+				ptr->layout.channel_order_definition = 0;
+			} else {
+				ISOM_DECREASE_SIZE(s, 1)
+				gf_bs_read_int(bs, 4);
+				ptr->layout.channel_order_definition = gf_bs_read_int(bs, 3);
+				ptr->layout.omitted_channels_present = gf_bs_read_int(bs, 1);
+				if (ptr->layout.omitted_channels_present) {
+					ISOM_DECREASE_SIZE(s, 8)
+					ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
+				}
+			}
 		}
 	}
-	if (ptr->layout.stream_structure & 2) {
+	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
 		ISOM_DECREASE_SIZE(s, 1)
 		ptr->layout.object_count = gf_bs_read_u8(bs);
 	}
 	return GF_OK;
 }
@@ -12722,49 +12754,75 @@ GF_Err chnl_box_write(GF_Box *s, GF_BitStream *bs)
 
 	e = gf_isom_full_box_write(s, bs);
 	if (e) return e;
 
 	gf_bs_write_u8(bs, ptr->layout.stream_structure);
+	if (ptr->version==0) {
+		gf_bs_write_u8(bs, ptr->layout.stream_structure);
+	} else {
+		gf_bs_write_int(bs, ptr->layout.stream_structure, 4);
+		gf_bs_write_int(bs, ptr->layout.format_ordering, 4);
+		gf_bs_write_u8(bs, ptr->layout.base_channel_count);
+	}
 	if (ptr->layout.stream_structure & 1) {
 		gf_bs_write_u8(bs, ptr->layout.definedLayout);
 		if (ptr->layout.definedLayout==0) {
 			u32 i;
+			if (ptr->version==1) {
+				gf_bs_write_u8(bs, ptr->layout.channels_count);
+			}
 			for (i=0; i<ptr->layout.channels_count; i++) {
 				gf_bs_write_u8(bs, ptr->layout.layouts[i].position);
 				if (ptr->layout.layouts[i].position==126) {
 					gf_bs_write_int(bs, ptr->layout.layouts[i].azimuth, 16);
 					gf_bs_write_int(bs, ptr->layout.layouts[i].elevation, 8);
 				}
 			}
 		} else {
-			gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
+			if (ptr->version==1) {
+				gf_bs_write_int(bs, 0, 4);
+				gf_bs_write_int(bs, ptr->layout.channel_order_definition, 3);
+				gf_bs_write_int(bs, ptr->layout.omitted_channels_present, 1);
+				if (ptr->layout.omitted_channels_present)
+					gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
+			} else {
+				gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
+			}
 		}
 	}
-	if (ptr->layout.stream_structure & 2) {
+	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
 		gf_bs_write_u8(bs, ptr->layout.object_count);
 	}
 	return GF_OK;
 }
 
 GF_Err chnl_box_size(GF_Box *s)
 {
 	GF_ChannelLayoutBox *ptr = (GF_ChannelLayoutBox *) s;
 	s->size += 1;
+	if (ptr->version==1) s->size++;
 	if (ptr->layout.stream_structure & 1) {
 		s->size += 1;
 		if (ptr->layout.definedLayout==0) {
 			u32 i;
+			if (ptr->version==1) s->size++;
 			for (i=0; i<ptr->layout.channels_count; i++) {
 				s->size+=1;
 				if (ptr->layout.layouts[i].position==126)
 					s->size+=3;
 			}
 		} else {
-			s->size += 8;
+			if (ptr->version==1) {
+				s->size += 1;
+				if (ptr->layout.omitted_channels_present)
+					s->size += 8;
+			} else {
+				s->size += 8;
+			}
 		}
 	}
-	if (ptr->layout.stream_structure & 2) {
+	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
 		s->size += 1;
 	}
 	return GF_OK;
 }
 
diff --git a/src/isomedia/isom_write.c b/src/isomedia/isom_write.c
index 62cce8f5bc4..4757640fb2f 100644
--- a/src/isomedia/isom_write.c
+++ b/src/isomedia/isom_write.c
@@ -2510,10 +2510,14 @@ GF_Err gf_isom_set_audio_layout(GF_ISOFile *movie, u32 trackNumber, u32 sampleDe
 	GF_SampleDescriptionBox *stsd;
 	GF_ChannelLayoutBox *chnl;
 	e = CanAccessMovie(movie, GF_ISOM_OPEN_WRITE);
 	if (e) return e;
 
+	if (!layout) return GF_BAD_PARAM;
+	if ((layout->stream_structure & 1) && (layout->definedLayout==0) && (layout->channels_count>=64))
+		return GF_BAD_PARAM;
+
 	trak = gf_isom_get_track_from_file(movie, trackNumber);
 	if (!trak) return GF_BAD_PARAM;
 
 	stsd = trak->Media->information->sampleTable->SampleDescription;
 	if (!stsd) {

Vulnerable code: 	ptr->layout.stream_structure = gf_bs_read_u8(bs);
			ISOM_DECREASE_SIZE(s, 8)
			ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
	if (ptr->layout.stream_structure & 2) {
			gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
	if (ptr->layout.stream_structure & 2) {
			s->size += 8;
	if (ptr->layout.stream_structure & 2) {
Fixed code: 	/*!  order of formats in the stream : 0 unknown, 1: Channels, possibly followed by Objects, 2 Objects, possibly followed by Channels*/
	u8 format_ordering;
	/*! combined channel count of the channel layout and the object count*/
	u8 base_channel_count;
	/*! indicates where the ordering of the audio channels for the definedLayout are specified
	0: as listed for the ChannelConfigurations in ISO/IEC 23091-3
	1: Default order of audio codec specification
	2: Channel ordering #2 of audio codec specification
	3: Channel ordering #3 of audio codec specification
	4: Channel ordering #4 of audio codec specification
	*/
	u8 channel_order_definition;
	/*! indicates if omittedChannelsMap is present*/
	u8 omitted_channels_present;
	if (ptr->version==0) {
		ptr->layout.stream_structure = gf_bs_read_u8(bs);
	} else {
		ptr->layout.stream_structure = gf_bs_read_int(bs, 4);
		ptr->layout.format_ordering = gf_bs_read_int(bs, 4);
		ISOM_DECREASE_SIZE(s, 1)
		ptr->layout.base_channel_count = gf_bs_read_u8(bs);
	}
			u32 nb_channels = 0;
			if (ptr->version) {
				ISOM_DECREASE_SIZE(s, 1)
				nb_channels = gf_bs_read_u8(bs);
			}
				if (ptr->layout.channels_count==64) return GF_ISOM_INVALID_FILE;

				ptr->layout.channels_count++;
				if (ptr->version) {
					nb_channels--;
					if (!nb_channels) break;
				}
			if (ptr->version==0) {
				ISOM_DECREASE_SIZE(s, 8)
				ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
				ptr->layout.omitted_channels_present = 1;
				ptr->layout.channel_order_definition = 0;
			} else {
				ISOM_DECREASE_SIZE(s, 1)
				gf_bs_read_int(bs, 4);
				ptr->layout.channel_order_definition = gf_bs_read_int(bs, 3);
				ptr->layout.omitted_channels_present = gf_bs_read_int(bs, 1);
				if (ptr->layout.omitted_channels_present) {
					ISOM_DECREASE_SIZE(s, 8)
					ptr->layout.omittedChannelsMap = gf_bs_read_u64(bs);
				}
			}
	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
	if (ptr->version==0) {
		gf_bs_write_u8(bs, ptr->layout.stream_structure);
	} else {
		gf_bs_write_int(bs, ptr->layout.stream_structure, 4);
		gf_bs_write_int(bs, ptr->layout.format_ordering, 4);
		gf_bs_write_u8(bs, ptr->layout.base_channel_count);
	}
			if (ptr->version==1) {
				gf_bs_write_u8(bs, ptr->layout.channels_count);
			}
			if (ptr->version==1) {
				gf_bs_write_int(bs, 0, 4);
				gf_bs_write_int(bs, ptr->layout.channel_order_definition, 3);
				gf_bs_write_int(bs, ptr->layout.omitted_channels_present, 1);
				if (ptr->layout.omitted_channels_present)
					gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
			} else {
				gf_bs_write_u64(bs, ptr->layout.omittedChannelsMap);
			}
	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
	if (ptr->version==1) s->size++;
			if (ptr->version==1) s->size++;
			if (ptr->version==1) {
				s->size += 1;
				if (ptr->layout.omitted_channels_present)
					s->size += 8;
			} else {
				s->size += 8;
			}
	if ((ptr->version==0) && (ptr->layout.stream_structure & 2)) {
	if (!layout) return GF_BAD_PARAM;
	if ((layout->stream_structure & 1) && (layout->definedLayout==0) && (layout->channels_count>=64))
		return GF_BAD_PARAM;