Skip to content

Commit 036bed0

Browse files
committed
Fix imagecreatefromavif() memory leak
This has been reported as libgd/libgd#831. We port the respective fix to our bundled libgd. Closes GH-8812.
1 parent 3fed226 commit 036bed0

File tree

2 files changed

+37
-18
lines changed

2 files changed

+37
-18
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ PHP NEWS
1919
. Fixed bug #78139 (timezone_open accepts invalid timezone string argument).
2020
(Derick)
2121

22+
GD:
23+
. Fixed imagecreatefromavif() memory leak. (cmb)
24+
2225
- MBString:
2326
. mb_detect_encoding recognizes all letters in Czech alphabet (alexdowad)
2427
. mb_detect_encoding recognizes all letters in Hungarian alphabet (alexdowad)

ext/gd/libgd/gd_avif.c

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ static avifBool isAvifError(avifResult result, const char *msg) {
150150
}
151151

152152

153+
typedef struct avifIOCtxReader {
154+
avifIO io; // this must be the first member for easy casting to avifIO*
155+
avifROData rodata;
156+
} avifIOCtxReader;
157+
153158
/*
154159
<readfromCtx> implements the avifIOReadFunc interface by calling the relevant functions
155160
in the gdIOCtx. Our logic is inspired by avifIOMemoryReaderRead() and avifIOFileReaderRead().
@@ -165,8 +170,8 @@ static avifBool isAvifError(avifResult result, const char *msg) {
165170
*/
166171
static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, size_t size, avifROData *out)
167172
{
168-
void *dataBuf = NULL;
169173
gdIOCtx *ctx = (gdIOCtx *) io->data;
174+
avifIOCtxReader *reader = (avifIOCtxReader *) io;
170175

171176
// readFlags is unsupported
172177
if (readFlags != 0) {
@@ -182,28 +187,34 @@ static avifResult readFromCtx(avifIO *io, uint32_t readFlags, uint64_t offset, s
182187
if (!ctx->seek(ctx, (int) offset))
183188
return AVIF_RESULT_IO_ERROR;
184189

185-
dataBuf = avifAlloc(size);
186-
if (!dataBuf) {
190+
if (size > reader->rodata.size) {
191+
reader->rodata.data = gdRealloc((void *) reader->rodata.data, size);
192+
reader->rodata.size = size;
193+
}
194+
if (!reader->rodata.data) {
187195
gd_error("avif error - couldn't allocate memory");
188196
return AVIF_RESULT_UNKNOWN_ERROR;
189197
}
190198

191199
// Read the number of bytes requested.
192200
// If getBuf() returns a negative value, that means there was an error.
193-
int charsRead = ctx->getBuf(ctx, dataBuf, (int) size);
201+
int charsRead = ctx->getBuf(ctx, (void *) reader->rodata.data, (int) size);
194202
if (charsRead < 0) {
195-
avifFree(dataBuf);
196203
return AVIF_RESULT_IO_ERROR;
197204
}
198205

199-
out->data = dataBuf;
206+
out->data = reader->rodata.data;
200207
out->size = charsRead;
201208
return AVIF_RESULT_OK;
202209
}
203210

204211
// avif.h says this is optional, but it seemed easy to implement.
205212
static void destroyAvifIO(struct avifIO *io) {
206-
gdFree(io);
213+
avifIOCtxReader *reader = (avifIOCtxReader *) io;
214+
if (reader->rodata.data != NULL) {
215+
gdFree((void *) reader->rodata.data);
216+
}
217+
gdFree(reader);
207218
}
208219

209220
/* Set up an avifIO object.
@@ -217,21 +228,23 @@ static void destroyAvifIO(struct avifIO *io) {
217228

218229
// TODO: can we get sizeHint somehow?
219230
static avifIO *createAvifIOFromCtx(gdIOCtx *ctx) {
220-
avifIO *io;
231+
struct avifIOCtxReader *reader;
221232

222-
io = gdMalloc(sizeof(*io));
223-
if (io == NULL)
233+
reader = gdMalloc(sizeof(*reader));
234+
if (reader == NULL)
224235
return NULL;
225236

226237
// TODO: setting persistent=FALSE is safe, but it's less efficient. Is it necessary?
227-
io->persistent = AVIF_FALSE;
228-
io->read = readFromCtx;
229-
io->write = NULL; // this function is currently unused; see avif.h
230-
io->destroy = destroyAvifIO;
231-
io->sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
232-
io->data = ctx;
233-
234-
return io;
238+
reader->io.persistent = AVIF_FALSE;
239+
reader->io.read = readFromCtx;
240+
reader->io.write = NULL; // this function is currently unused; see avif.h
241+
reader->io.destroy = destroyAvifIO;
242+
reader->io.sizeHint = 0; // sadly, we don't get this information from the gdIOCtx.
243+
reader->io.data = ctx;
244+
reader->rodata.data = NULL;
245+
reader->rodata.size = 0;
246+
247+
return (avifIO *) reader;
235248
}
236249

237250

@@ -576,6 +589,9 @@ void gdImageAvifCtx(gdImagePtr im, gdIOCtx *outfile, int quality, int speed)
576589

577590
if (avifOutput.data)
578591
avifRWDataFree(&avifOutput);
592+
593+
if (avifIm)
594+
avifImageDestroy(avifIm);
579595
}
580596

581597
void gdImageAvifEx(gdImagePtr im, FILE *outFile, int quality, int speed)

0 commit comments

Comments
 (0)