summaryrefslogtreecommitdiff
path: root/base/stream.c
diff options
context:
space:
mode:
authorChris Liddell <chris.liddell@artifex.com>2021-06-04 16:45:56 +0100
committerChris Liddell <chris.liddell@artifex.com>2021-06-10 11:01:51 +0100
commitbd56e2ddd62e5706dfafdfe817d71e6d27a7fe49 (patch)
tree3d547b54e1429152d0f0b96c140009a9def3db56 /base/stream.c
parenta67318453d85336034f04229eeecb4a4b8944f9f (diff)
downloadghostpdl-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.c38
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)");