Werner Lemberg pushed to branch master at FreeType / FreeType
Commits:
-
13983b05
by Ben Wagner at 2023-01-17T08:48:33+01:00
[base] Fix leak of internal stream marked external.
`open_face_from_buffer` allocates a new `FT_Stream` to pass to
`ft_open_face_internal`. Because this is an `FT_OPEN_STREAM`,
`ft_open_face_internal` will mark this as an 'external stream', which the
caller must free. However, `open_face_from_buffer` cannot directly free it
because the stream must last as long as the face. There is currently an
attempt at this by clearing the 'external stream' bit after
`open_face_from_buffer` returns successfully. However, this is too late as
the original stream may have already been closed and the stream on the face
may not be the same stream as originally passed.
It is tempting to use `FT_OPEN_MEMORY` and let `ft_open_face_internal`
create the stream internally. However, with this method there is no means
to pass through a 'close' function to the created stream to free the
underlying data, which must be owned by the stream.
A possibility is to check on success if the stream of the face is the same
as the original stream. If it is then unset the external flag. If not,
then free the original stream. Unfortunately, while no current
implementation does so, it is possible that the face still has the original
stream somewhere other than as the `FT_FaceRec::stream`. The stream needs
to remain available for the life of the face or until it is closed,
whichever comes earlier.
The approach taken here is to let the stream own itself. When the stream is
closed it will free itself.
* src/base/ftobjs.c (memory_stream_close): Free `stream`.
(open_face_from_buffer): Simplify error handling, since
`ft_open_face_internal` always closes `args.stream` on any error.
Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=54930
-
0d4f887c
by Ben Wagner at 2023-01-17T08:54:11+01:00
[base] Always close user-provided stream.
The `FT_Open_Face` documentation states
> If `FT_OPEN_STREAM` is set in `args->flags`, the stream in `args->stream`
> is automatically closed before this function returns any error (including
> `FT_Err_Invalid_Argument`).
However, if the user provides a stream in `args.stream` with
`FT_OPEN_STREAM` set and a `close` function, but then for some reason passes
NULL for `aface` and a non-negative `face_index`, the error
`Invalid_Argument` is returned but the `close` callback will not be called
on the user-provided stream. This may cause resource leaks if the caller is
depending on the `close` callback to free resources.
The difficulty is that a user may fill out a `FT_StreamRec` and pass its
address as `args.stream`, but the stream isn't really 'live' until
`FT_Stream_New` is called on it (and `memory` is set). In particular, it
cannot really be cleaned up properly in `ft_open_face_internal` until the
stream pointer has been copied into the `stream` local variable.
* src/base/ftobj.c (ft_open_face_internal): Ensure that user-provided
`args.stream.close` is called even with early errors.
-
29f83d1d
by Ben Wagner at 2023-01-17T08:59:25+01:00
[base] 'close' callback may not use `stream->memory`.
The documentation for `FT_StreamRec::memory` states that it 'shouldn't be
touched by stream implementations'. This is true even for internal
implementations of the 'close' callback, since it is not guaranteed that
`memory` will even be set when the 'close' callback occurs.
* src/base/ftobjs.c (new_memory_stream): stash current `memory` in
`stream->descriptor`.
(memory_stream_close): Use it.
1 changed file:
Changes:
src/base/ftobjs.c
... |
... |
@@ -1672,13 +1672,13 @@ |
1672
|
1672
|
static void
|
1673
|
1673
|
memory_stream_close( FT_Stream stream )
|
1674
|
1674
|
{
|
1675
|
|
- FT_Memory memory = stream->memory;
|
|
1675
|
+ FT_Memory memory = (FT_Memory)stream->descriptor.pointer;
|
1676
|
1676
|
|
1677
|
1677
|
|
1678
|
1678
|
FT_FREE( stream->base );
|
1679
|
|
-
|
1680
|
1679
|
stream->size = 0;
|
1681
|
1680
|
stream->close = NULL;
|
|
1681
|
+ FT_FREE( stream );
|
1682
|
1682
|
}
|
1683
|
1683
|
|
1684
|
1684
|
|
... |
... |
@@ -1709,7 +1709,8 @@ |
1709
|
1709
|
|
1710
|
1710
|
FT_Stream_OpenMemory( stream, base, size );
|
1711
|
1711
|
|
1712
|
|
- stream->close = close;
|
|
1712
|
+ stream->descriptor.pointer = memory;
|
|
1713
|
+ stream->close = close;
|
1713
|
1714
|
|
1714
|
1715
|
*astream = stream;
|
1715
|
1716
|
|
... |
... |
@@ -1734,6 +1735,7 @@ |
1734
|
1735
|
FT_Memory memory = library->memory;
|
1735
|
1736
|
|
1736
|
1737
|
|
|
1738
|
+ /* `memory_stream_close` also frees the stream object. */
|
1737
|
1739
|
error = new_memory_stream( library,
|
1738
|
1740
|
base,
|
1739
|
1741
|
size,
|
... |
... |
@@ -1763,21 +1765,7 @@ |
1763
|
1765
|
face_index &= 0x7FFF0000L; /* retain GX data */
|
1764
|
1766
|
#endif
|
1765
|
1767
|
|
1766
|
|
- error = ft_open_face_internal( library, &args, face_index, aface, 0 );
|
1767
|
|
-
|
1768
|
|
- if ( !error )
|
1769
|
|
- (*aface)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
|
1770
|
|
- else
|
1771
|
|
-#ifdef FT_MACINTOSH
|
1772
|
|
- FT_Stream_Free( stream, 0 );
|
1773
|
|
-#else
|
1774
|
|
- {
|
1775
|
|
- FT_Stream_Close( stream );
|
1776
|
|
- FT_FREE( stream );
|
1777
|
|
- }
|
1778
|
|
-#endif
|
1779
|
|
-
|
1780
|
|
- return error;
|
|
1768
|
+ return ft_open_face_internal( library, &args, face_index, aface, 0 );
|
1781
|
1769
|
}
|
1782
|
1770
|
|
1783
|
1771
|
|
... |
... |
@@ -2557,7 +2545,7 @@ |
2557
|
2545
|
|
2558
|
2546
|
/* test for valid `library' delayed to `FT_Stream_New' */
|
2559
|
2547
|
|
2560
|
|
- if ( ( !aface && face_index >= 0 ) || !args )
|
|
2548
|
+ if ( !args )
|
2561
|
2549
|
return FT_THROW( Invalid_Argument );
|
2562
|
2550
|
|
2563
|
2551
|
external_stream = FT_BOOL( ( args->flags & FT_OPEN_STREAM ) &&
|
... |
... |
@@ -2568,6 +2556,14 @@ |
2568
|
2556
|
if ( error )
|
2569
|
2557
|
goto Fail3;
|
2570
|
2558
|
|
|
2559
|
+ /* Do this error check after `FT_Stream_New` to ensure that the */
|
|
2560
|
+ /* 'close' callback is called. */
|
|
2561
|
+ if ( !aface && face_index >= 0 )
|
|
2562
|
+ {
|
|
2563
|
+ error = FT_THROW( Invalid_Argument );
|
|
2564
|
+ goto Fail3;
|
|
2565
|
+ }
|
|
2566
|
+
|
2571
|
2567
|
memory = library->memory;
|
2572
|
2568
|
|
2573
|
2569
|
/* If the font driver is specified in the `args' structure, use */
|
|