From 9ac93d1252c4ddd0043dbe7b08781320b26ad4c9 Mon Sep 17 00:00:00 2001 From: Arkadiusz Bokowy Date: Sat, 31 Aug 2024 20:31:02 +0200 Subject: [PATCH] Fix TLV dB parser in case of used container In case when dB information does not appear as the only TLV type in the stream (it might be wrapped in a container, but the container can not have any other type), the TLV parser fails to get the dB TLV pointer. This commit fixes it by distinguishing between TLV parse error and dB information not being found in a container (-ENOENT), so the parser can iterate over all elements in the container. Also, it fixes out-of-bounds read in case of malicious TLV record. Closes: https://github.com/alsa-project/alsa-lib/pull/409 Signed-off-by: Arkadiusz Bokowy Signed-off-by: Jaroslav Kysela --- src/control/tlv.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/control/tlv.c b/src/control/tlv.c index 3a2b731d..fed46acd 100644 --- a/src/control/tlv.c +++ b/src/control/tlv.c @@ -40,6 +40,8 @@ #define int_index(size) (((size) + sizeof(int) - 1) / sizeof(int)) /* max size of a TLV entry for dB information (including compound one) */ #define MAX_TLV_RANGE_SIZE 256 +/* min length of a TLV stream to contain type and size */ +#define MIN_TLV_STREAM_LEN ((SNDRV_CTL_TLVO_LEN + 1) * sizeof(int)) #endif /** @@ -47,12 +49,12 @@ * \param tlv the TLV source * \param tlv_size the byte size of TLV source * \param db_tlvp the pointer stored the dB TLV information - * \return the byte size of dB TLV information if found in the given - * TLV source, or a negative error code. + * \return The byte size of dB TLV information if found in the given TLV + * source, -ENOENT if not found, or a negative error code in case of an error. * * This function parses the given TLV source and stores the TLV start * point if the TLV information regarding dB conversion is found. - * The stored TLV pointer can be passed to the convesion functions + * The stored TLV pointer can be passed to the conversion functions * #snd_tlv_convert_to_dB(), #snd_tlv_convert_from_dB() and * #snd_tlv_get_dB_range(). */ @@ -64,6 +66,13 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, unsigned int size; int err; + /* Validate that it is possible to read the type and size + * without reading past the end of the buffer. */ + if (tlv_size < MIN_TLV_STREAM_LEN) { + SNDERR("TLV stream too short"); + return -EINVAL; + } + *db_tlvp = NULL; type = tlv[SNDRV_CTL_TLVO_TYPE]; size = tlv[SNDRV_CTL_TLVO_LEN]; @@ -79,7 +88,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, while (size > 0) { unsigned int len; err = snd_tlv_parse_dB_info(tlv, size, db_tlvp); - if (err < 0) + if (err < 0 && err != -ENOENT) return err; /* error */ if (err > 0) return err; /* found */ @@ -114,7 +123,7 @@ int snd_tlv_parse_dB_info(unsigned int *tlv, default: break; } - return -EINVAL; /* not found */ + return -ENOENT; } /**