Fix memory leak of altICC if avifDecoderFindGainMapItem returns early. (#3127)
Make sure none of the inputs variables are modified until the very end.
Fixes b/495087335
diff --git a/src/read.c b/src/read.c
index 430e375..a768ed9 100644
--- a/src/read.c
+++ b/src/read.c
@@ -5792,12 +5792,12 @@
// Allocate avifGainMap on the stack instead of using avifGainMapCreate() to simplify error handling.
avifGainMap gainMapTmp;
avifGainMapSetDefaults(&gainMapTmp);
- const avifResult tmapParsingRes = avifParseToneMappedImageBox(&gainMapTmp, tmapData.data, tmapData.size, data->diag);
- if (tmapParsingRes == AVIF_RESULT_NOT_IMPLEMENTED) {
+ avifResult result = avifParseToneMappedImageBox(&gainMapTmp, tmapData.data, tmapData.size, data->diag);
+ if (result == AVIF_RESULT_NOT_IMPLEMENTED) {
// Unsupported gain map version. Simply ignore the gain map.
return AVIF_RESULT_OK;
}
- AVIF_CHECKRES(tmapParsingRes);
+ AVIF_CHECKRES(result);
avifDecoderItem * gainMapItemTmp;
AVIF_CHECKRES(avifMetaFindOrCreateItem(data->meta, gainMapItemID, &gainMapItemTmp));
@@ -5805,24 +5805,30 @@
return AVIF_RESULT_NOT_IMPLEMENTED;
}
- const avifResult gainMapParsingRes = avifDecoderItemReadAndParse(decoder,
- gainMapItemTmp,
- /*isItemInInput=*/AVIF_TRUE,
- &data->tileInfos[AVIF_ITEM_GAIN_MAP].grid,
- gainMapCodecType);
- if (gainMapParsingRes == AVIF_RESULT_NOT_IMPLEMENTED) {
+ avifCodecType gainMapCodecTypeTmp;
+ result = avifDecoderItemReadAndParse(decoder,
+ gainMapItemTmp,
+ /*isItemInInput=*/AVIF_TRUE,
+ &data->tileInfos[AVIF_ITEM_GAIN_MAP].grid,
+ &gainMapCodecTypeTmp);
+ if (result == AVIF_RESULT_NOT_IMPLEMENTED) {
return AVIF_RESULT_OK;
}
- AVIF_CHECKRES(gainMapParsingRes);
+ AVIF_CHECKRES(result);
- AVIF_CHECKRES(avifReadColorProperties(decoder->io,
- &toneMappedImageItemTmp->properties,
- &gainMapTmp.altICC,
- &gainMapTmp.altColorPrimaries,
- &gainMapTmp.altTransferCharacteristics,
- &gainMapTmp.altMatrixCoefficients,
- &gainMapTmp.altYUVRange,
- /*cicpSet=*/NULL));
+ // This may allocate gainMapTmp.altICC which must be freed in case of error.
+ result = avifReadColorProperties(decoder->io,
+ &toneMappedImageItemTmp->properties,
+ &gainMapTmp.altICC,
+ &gainMapTmp.altColorPrimaries,
+ &gainMapTmp.altTransferCharacteristics,
+ &gainMapTmp.altMatrixCoefficients,
+ &gainMapTmp.altYUVRange,
+ /*cicpSet=*/NULL);
+ if (result != AVIF_RESULT_OK) {
+ avifRWDataFree(&gainMapTmp.altICC);
+ return result;
+ }
const avifProperty * clliProp = avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "clli");
if (clliProp) {
@@ -5841,10 +5847,12 @@
// Every image item shall be associated with one property of this type, prior to the association
// of all transformative properties.
avifDiagnosticsPrintf(data->diag, "Box[tmap] missing mandatory ispe property");
+ avifRWDataFree(&gainMapTmp.altICC);
return AVIF_RESULT_BMFF_PARSE_FAILED;
}
if (ispeProp->u.ispe.width != colorItem->width || ispeProp->u.ispe.height != colorItem->height) {
avifDiagnosticsPrintf(data->diag, "Box[tmap] ispe property width/height does not match base image");
+ avifRWDataFree(&gainMapTmp.altICC);
return AVIF_RESULT_BMFF_PARSE_FAILED;
}
@@ -5859,6 +5867,7 @@
// enforced at encoding. Other patterns are rejected at decoding.
avifDiagnosticsPrintf(data->diag,
"Box[tmap] 'pasp', 'clap', 'irot' and 'imir' properties must be associated with base and gain map items instead of 'tmap'");
+ avifRWDataFree(&gainMapTmp.altICC);
return AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE;
}
@@ -5868,29 +5877,40 @@
avifRange yuvRange = AVIF_RANGE_FULL;
avifBool cicpSet = AVIF_FALSE;
// Look for a colr nclx box. Other colr box types (e.g. ICC) are not supported.
- AVIF_CHECKRES(
- avifReadColorNclxProperty(&gainMapItemTmp->properties, &colorPrimaries, &transferCharacteristics, &matrixCoefficients, &yuvRange, &cicpSet));
+ result =
+ avifReadColorNclxProperty(&gainMapItemTmp->properties, &colorPrimaries, &transferCharacteristics, &matrixCoefficients, &yuvRange, &cicpSet);
+ if (result != AVIF_RESULT_OK) {
+ avifRWDataFree(&gainMapTmp.altICC);
+ return result;
+ }
// -- Everything is valid, do memory allocations and fill in output data. --
decoder->image->gainMap = avifGainMapCreate();
- AVIF_CHECKERR(decoder->image->gainMap, AVIF_RESULT_OUT_OF_MEMORY);
- *decoder->image->gainMap = gainMapTmp;
+ if (!decoder->image->gainMap) {
+ avifRWDataFree(&gainMapTmp.altICC);
+ return AVIF_RESULT_OUT_OF_MEMORY;
+ }
if (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP) {
- decoder->image->gainMap->image = avifImageCreateEmpty();
- avifImage * image = decoder->image->gainMap->image;
- AVIF_CHECKERR(image, AVIF_RESULT_OUT_OF_MEMORY);
+ avifImage * image = avifImageCreateEmpty();
+ if (!image) {
+ avifRWDataFree(&gainMapTmp.altICC);
+ return AVIF_RESULT_OUT_OF_MEMORY;
+ }
if (cicpSet) {
image->colorPrimaries = colorPrimaries;
image->transferCharacteristics = transferCharacteristics;
image->matrixCoefficients = matrixCoefficients;
image->yuvRange = yuvRange;
}
+ gainMapTmp.image = image;
}
- // Only set the output pointer after everything has been validated.
+ // Only set the output pointers after everything has been validated.
+ *decoder->image->gainMap = gainMapTmp;
*gainMapItem = gainMapItemTmp;
+ *gainMapCodecType = gainMapCodecTypeTmp;
return AVIF_RESULT_OK;
}