|
| 1 | +From 009008e2f4d5a54dd71f422070df3af7b3dbc931 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Kazuma Matsumoto <269371721+y637F9QQ2x@users.noreply.github.com> |
| 3 | +Date: Sun, 22 Mar 2026 20:35:49 +0100 |
| 4 | +Subject: [PATCH] libvncclient: add bounds checks to UltraZip subrectangle |
| 5 | + parsing |
| 6 | + |
| 7 | +HandleUltraZipBPP() iterates over sub-rectangles using numCacheRects |
| 8 | +(derived from the attacker-controlled rect.r.x) without validating |
| 9 | +that the pointer stays within the decompressed data buffer. A malicious |
| 10 | +server can set a large numCacheRects value, causing heap out-of-bounds |
| 11 | +reads via the memcpy calls in the parsing loop. |
| 12 | + |
| 13 | +Add bounds checks before reading the 12-byte subrect header and before |
| 14 | +advancing the pointer by the raw pixel data size. Use uint64_t for the |
| 15 | +raw data size calculation to prevent integer overflow on 32-bit platforms. |
| 16 | +--- |
| 17 | + src/libvncclient/ultra.c | 16 +++++++++++++++- |
| 18 | + 1 file changed, 15 insertions(+), 1 deletion(-) |
| 19 | + |
| 20 | +diff --git a/src/libvncclient/ultra.c b/src/libvncclient/ultra.c |
| 21 | +index 1d3aaba..5633b8c 100644 |
| 22 | +--- a/src/libvncclient/ultra.c |
| 23 | ++++ b/src/libvncclient/ultra.c |
| 24 | +@@ -126,6 +126,7 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) |
| 25 | + int toRead=0; |
| 26 | + int inflateResult=0; |
| 27 | + unsigned char *ptr=NULL; |
| 28 | ++ unsigned char *ptr_end=NULL; |
| 29 | + lzo_uint uncompressedBytes = ry + (rw * 65535); |
| 30 | + unsigned int numCacheRects = rx; |
| 31 | + |
| 32 | +@@ -194,11 +195,18 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) |
| 33 | + |
| 34 | + /* Put the uncompressed contents of the update on the screen. */ |
| 35 | + ptr = (unsigned char *)client->raw_buffer; |
| 36 | ++ ptr_end = ptr + uncompressedBytes; |
| 37 | + for (i=0; i<numCacheRects; i++) |
| 38 | + { |
| 39 | + unsigned short sx, sy, sw, sh; |
| 40 | + unsigned int se; |
| 41 | + |
| 42 | ++ /* subrect header: sx(2) + sy(2) + sw(2) + sh(2) + se(4) = 12 bytes */ |
| 43 | ++ if (ptr + 12 > ptr_end) { |
| 44 | ++ rfbClientLog("UltraZip: subrect %d header exceeds decompressed data bounds\n", i); |
| 45 | ++ return FALSE; |
| 46 | ++ } |
| 47 | ++ |
| 48 | + memcpy((char *)&sx, ptr, 2); ptr += 2; |
| 49 | + memcpy((char *)&sy, ptr, 2); ptr += 2; |
| 50 | + memcpy((char *)&sw, ptr, 2); ptr += 2; |
| 51 | +@@ -213,8 +221,13 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) |
| 52 | + |
| 53 | + if (se == rfbEncodingRaw) |
| 54 | + { |
| 55 | ++ uint64_t rawBytes = (uint64_t)sw * sh * (BPP / 8); |
| 56 | ++ if (rawBytes > (size_t)(ptr_end - ptr)) { |
| 57 | ++ rfbClientLog("UltraZip: subrect %d raw data exceeds decompressed data bounds\n", i); |
| 58 | ++ return FALSE; |
| 59 | ++ } |
| 60 | + client->GotBitmap(client, (unsigned char *)ptr, sx, sy, sw, sh); |
| 61 | +- ptr += ((sw * sh) * (BPP / 8)); |
| 62 | ++ ptr += (size_t)rawBytes; |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | +@@ -222,3 +235,4 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh) |
| 67 | + } |
| 68 | + |
| 69 | + #undef CARDBPP |
| 70 | ++ |
0 commit comments