diff options
author | Chris Liddell <chris.liddell@artifex.com> | 2021-06-04 16:45:56 +0100 |
---|---|---|
committer | Chris Liddell <chris.liddell@artifex.com> | 2021-06-10 11:01:51 +0100 |
commit | bd56e2ddd62e5706dfafdfe817d71e6d27a7fe49 (patch) | |
tree | 3d547b54e1429152d0f0b96c140009a9def3db56 /base/stream.c | |
parent | a67318453d85336034f04229eeecb4a4b8944f9f (diff) | |
download | ghostpdl-bd56e2ddd62e5706dfafdfe817d71e6d27a7fe49.tar.gz |
Bug 703911: Erroneous freeing of stream string buffer
Previously, s_close_filters() would close and free all the streams
in a chain, including the lowest level source stream. If that stream
was a string object based stream, s_close_filters() would also free
that string buffer.
On the other hand, it is far from obvious to calling code that
the lowest level stream was memory buffer based, and equally difficult
for the calling code to retrieve that buffer for itself.
So, make two cases:
sread_string(): This sets up the stream for cases where the caller
retains ownership of the buffer - as in the case where the buffer
comes from a Postscript string object (where ownership is retained
by Postscript VM). Thus s_close_filters() will not free the buffer.
and:
sread_transient_string(): This sets up the stream, transferring
ownership of the buffer to stream. In which case s_close_filters()
will free the buffer.
Comes from:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34459&q=ghostscript&can=2
Diffstat (limited to 'base/stream.c')
-rw-r--r-- | base/stream.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/base/stream.c b/base/stream.c index 2073a12a6..22435e11b 100644 --- a/base/stream.c +++ b/base/stream.c @@ -112,6 +112,7 @@ s_init(stream *s, gs_memory_t * mem) s->file_name.size = 0; s->close_strm = false; /* default */ s->close_at_eod = true; /* default */ + s->cbuf_string_memory = NULL; } stream * s_alloc(gs_memory_t * mem, client_name_t cname) @@ -179,6 +180,7 @@ s_std_init(register stream * s, byte * ptr, uint len, const stream_procs * pp, s->file = 0; s->file_name.data = 0; /* in case stream is on stack */ s->file_name.size = 0; + s->cbuf_string_memory = NULL; if (s->memory) { if_debug4m('s', s->memory, "[s]init "PRI_INTPTR", buf="PRI_INTPTR", len=%u, modes=%d\n", (intptr_t) s, (intptr_t) ptr, len, modes); @@ -1016,6 +1018,10 @@ static int stream_cursor_write *, bool); /* Initialize a stream for reading a string. */ +/* String ownership retained by the caller, for example + Postscript string objects owned by the Postscript + interpreter + */ void sread_string(register stream *s, const byte *ptr, uint len) { @@ -1027,9 +1033,31 @@ sread_string(register stream *s, const byte *ptr, uint len) s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek); s->cbuf_string.data = (byte *)ptr; s->cbuf_string.size = len; + s->cbuf_string_memory = NULL; + s->end_status = EOFC; + s->cursor.r.limit = s->cursor.w.limit; +} + +/* The string ownership is transferred from caller to stream. + string_mem pointer must be allocator used to allocate the + "string" buffer. + */ +void +sread_transient_string(register stream *s, gs_memory_t *string_mem, const byte *ptr, uint len) +{ + static const stream_procs p = { + s_string_available, s_string_read_seek, s_std_read_reset, + s_std_read_flush, s_std_null, s_string_read_process + }; + + s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek); + s->cbuf_string.data = (byte *)ptr; + s->cbuf_string.size = len; + s->cbuf_string_memory = string_mem; s->end_status = EOFC; s->cursor.r.limit = s->cursor.w.limit; } + /* Initialize a reusable stream for reading a string. */ static void s_string_reusable_reset(stream *s) @@ -1231,15 +1259,23 @@ s_close_filters(stream **ps, stream *target) while (*ps != target) { stream *s = *ps; gs_memory_t *mem = s->state->memory; + gs_memory_t *cbuf_string_memory = s->cbuf_string_memory; byte *sbuf = s->cbuf; + byte *cbuf = s->cbuf_string.data; stream *next = s->strm; int status = sclose(s); stream_state *ss = s->state; /* sclose may set this to s */ if (status < 0) return status; + + if (s->cbuf_string_memory != NULL) { /* stream owns string buffer, so free it */ + gs_free_object(cbuf_string_memory, cbuf, "s_close_filters(cbuf)"); + } + if (mem) { - gs_free_object(mem, sbuf, "s_close_filters(buf)"); + if (sbuf != cbuf) + gs_free_object(mem, sbuf, "s_close_filters(buf)"); gs_free_object(mem, s, "s_close_filters(stream)"); if (ss != (stream_state *)s) gs_free_object(mem, ss, "s_close_filters(state)"); |