vnc_worker_thread_loop() copies z_stream stored in its local VncState to
the persistent VncState, and the copied one is freed with deflateEnd()
later. However, deflateEnd() refuses to operate with a copied z_stream
and returns Z_STREAM_ERROR, leaking the allocated memory.

Avoid copying the zlib state to fix the memory leak.

Fixes: bd023f953e5e ("vnc: threaded VNC server")
Signed-off-by: Akihiko Odaki <od...@rsg.ci.i.u-tokyo.ac.jp>
Reviewed-by: Philippe Mathieu-Daudé <phi...@linaro.org>
---
 ui/vnc.h          |  2 +-
 ui/vnc-enc-zlib.c | 30 +++++++++++++++---------------
 ui/vnc-jobs.c     |  2 --
 ui/vnc.c          |  2 +-
 4 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/ui/vnc.h b/ui/vnc.h
index 1dc76c270c35..78a5fdfdc0d0 100644
--- a/ui/vnc.h
+++ b/ui/vnc.h
@@ -265,6 +265,7 @@ typedef struct VncWorker {
     uint8_t lossy_rect[VNC_STAT_ROWS][VNC_STAT_COLS];
 
     VncTight tight;
+    VncZlib zlib;
     VncZrle zrle;
 } VncWorker;
 
@@ -345,7 +346,6 @@ struct VncState
     /* Encoding specific, if you add something here, don't forget to
      *  update vnc_async_encoding_start()
      */
-    VncZlib zlib;
     VncHextile hextile;
     VncZywrle zywrle;
 
diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c
index 605030730a44..d8b4cf038b02 100644
--- a/ui/vnc-enc-zlib.c
+++ b/ui/vnc-enc-zlib.c
@@ -48,21 +48,21 @@ void vnc_zlib_zfree(void *x, void *addr)
 
 static void vnc_zlib_start(VncState *vs)
 {
-    buffer_reset(&vs->zlib.zlib);
+    buffer_reset(&vs->worker->zlib.zlib);
 
     // make the output buffer be the zlib buffer, so we can compress it later
-    vs->zlib.tmp = vs->output;
-    vs->output = vs->zlib.zlib;
+    vs->worker->zlib.tmp = vs->output;
+    vs->output = vs->worker->zlib.zlib;
 }
 
 static int vnc_zlib_stop(VncState *vs)
 {
-    z_streamp zstream = &vs->zlib.stream;
+    z_streamp zstream = &vs->worker->zlib.stream;
     int previous_out;
 
     // switch back to normal output/zlib buffers
-    vs->zlib.zlib = vs->output;
-    vs->output = vs->zlib.tmp;
+    vs->worker->zlib.zlib = vs->output;
+    vs->output = vs->worker->zlib.tmp;
 
     // compress the zlib buffer
 
@@ -85,24 +85,24 @@ static int vnc_zlib_stop(VncState *vs)
             return -1;
         }
 
-        vs->zlib.level = vs->worker->tight.compression;
+        vs->worker->zlib.level = vs->worker->tight.compression;
         zstream->opaque = vs;
     }
 
-    if (vs->worker->tight.compression != vs->zlib.level) {
+    if (vs->worker->tight.compression != vs->worker->zlib.level) {
         if (deflateParams(zstream, vs->worker->tight.compression,
                           Z_DEFAULT_STRATEGY) != Z_OK) {
             return -1;
         }
-        vs->zlib.level = vs->worker->tight.compression;
+        vs->worker->zlib.level = vs->worker->tight.compression;
     }
 
     // reserve memory in output buffer
-    buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64);
+    buffer_reserve(&vs->output, vs->worker->zlib.zlib.offset + 64);
 
     // set pointers
-    zstream->next_in = vs->zlib.zlib.buffer;
-    zstream->avail_in = vs->zlib.zlib.offset;
+    zstream->next_in = vs->worker->zlib.zlib.buffer;
+    zstream->avail_in = vs->worker->zlib.zlib.offset;
     zstream->next_out = vs->output.buffer + vs->output.offset;
     zstream->avail_out = vs->output.capacity - vs->output.offset;
     previous_out = zstream->avail_out;
@@ -147,8 +147,8 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, 
int y, int w, int h)
 
 void vnc_zlib_clear(VncState *vs)
 {
-    if (vs->zlib.stream.opaque) {
-        deflateEnd(&vs->zlib.stream);
+    if (vs->worker->zlib.stream.opaque) {
+        deflateEnd(&vs->worker->zlib.stream);
     }
-    buffer_free(&vs->zlib.zlib);
+    buffer_free(&vs->worker->zlib.zlib);
 }
diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c
index d019d88536b7..dbbf62212c79 100644
--- a/ui/vnc-jobs.c
+++ b/ui/vnc-jobs.c
@@ -189,7 +189,6 @@ static void vnc_async_encoding_start(VncState *orig, 
VncState *local)
     local->write_pixels = orig->write_pixels;
     local->client_pf = orig->client_pf;
     local->client_be = orig->client_be;
-    local->zlib = orig->zlib;
     local->hextile = orig->hextile;
     local->client_width = orig->client_width;
     local->client_height = orig->client_height;
@@ -198,7 +197,6 @@ static void vnc_async_encoding_start(VncState *orig, 
VncState *local)
 static void vnc_async_encoding_end(VncState *orig, VncState *local)
 {
     buffer_free(&local->output);
-    orig->zlib = local->zlib;
     orig->hextile = local->hextile;
 }
 
diff --git a/ui/vnc.c b/ui/vnc.c
index 17fcf377d7f8..b55d80b9b003 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -3258,7 +3258,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket 
*sioc,
 #ifdef CONFIG_PNG
     buffer_init(&vs->worker->tight.png,      "vnc-tight-png/%p", sioc);
 #endif
-    buffer_init(&vs->zlib.zlib,              "vnc-zlib/%p", sioc);
+    buffer_init(&vs->worker->zlib.zlib,      "vnc-zlib/%p", sioc);
     buffer_init(&vs->worker->zrle.zrle,      "vnc-zrle/%p", sioc);
     buffer_init(&vs->worker->zrle.fb,        "vnc-zrle-fb/%p", sioc);
     buffer_init(&vs->worker->zrle.zlib,      "vnc-zrle-zlib/%p", sioc);

-- 
2.49.0


Reply via email to