screen: fix framebuffer height parse and stray byte consume

Two RFB protocol bugs in the /screen client:

1. ServerInit parsed framebuffer-height from byte offset 1 instead of
   2. RFB 3.8 ServerInit is width@0-1, height@2-3; u16be(b,1) mixes
   the low byte of width with the high byte of height. The wrong
   height went into the initial non-incremental FramebufferUpdate-
   Request; neatvnc feeds the client's width/height straight into
   pixman_region_union_rect un-clipped (src/server.c), so an
   out-of-range height corrupts the server damage region and pixman
   reports 'Invalid rectangle passed'.

2. FramebufferUpdate handler had a stray drainTo(1) after the 3-byte
   padding+nRects header, consuming the first byte of the first
   rectangle and desyncing every update.

Closes #128
This commit is contained in:
iris 2026-05-20 22:11:28 +02:00
parent 08913484cc
commit a5ef10c10c

View file

@ -467,7 +467,8 @@ canvas { display: block; cursor: default; }
case 'server-init': {
const b = drainTo(24);
if (!b) return false;
fbW = u16be(b, 0); fbH = u16be(b, 1);
// RFB ServerInit: width @ bytes 0-1, height @ bytes 2-3.
fbW = u16be(b, 0); fbH = u16be(b, 2);
// pixel format: bpp=b[4], depth=b[5], big-endian=b[6], true-colour=b[7]
// red/green/blue max/shift at b[8..17]
pixelFormat = {
@ -493,12 +494,10 @@ canvas { display: block; cursor: default; }
if (!b) return false;
const msgType = b[0];
if (msgType === 0) {
// FramebufferUpdate
// FramebufferUpdate: type(1) + padding(1) + nRects(2). The type
// byte is already consumed above; hdr covers padding + nRects.
const hdr = drainTo(3);
if (!hdr) { chunks.unshift(b); totalBytes += 1; return false; }
drainTo(1); // padding (already consumed with hdr? no — hdr is 3 bytes after the type)
// Actually: message type (1) + padding (1) + nRects (2) = 4 bytes total after type byte
// Let's re-do: type byte consumed, then 1 pad + 2 nRects = 3 bytes
updateRects = u16be(hdr, 1);
state = 'rect-header';
} else if (msgType === 2) {