{"record":{"id":"00f10ad0b3e8a93e","repo":"rustdesk/rustdesk","slug":"drm-refusing-a-dma-buf-descriptor-with-num-planes","errorCode":null,"errorMessage":"drm: refusing a dma-buf descriptor with num_planes {} (1..=4)","messagePattern":"drm: refusing a dma-buf descriptor with num_planes (.+?) \\(1\\.\\.=4\\)","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"libs/scrap/src/common/drm_render.rs","lineNumber":80,"sourceCode":"\n    /// Returns context-owned linear pixels valid ONLY until the next `convert()`; row stride is `len / height`.\n    pub fn convert(\n        &mut self,\n        desc: &mut drmtap_dmabuf_desc,\n        received_fd: RawFd,\n    ) -> io::Result<(&[u8], u32, u32, Pixfmt)> {\n        {\n            let (w, h) = (desc.width, desc.height);\n            if w == 0 || h == 0 || w > MAX_DIM || h > MAX_DIM {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\"drm: refusing a dma-buf descriptor with geometry {w}x{h}\"),\n                ));\n            }\n            // Reject, do not clamp, and write the normalized count back so the C reads the count bounded here.\n            let planes = if desc.num_planes == 0 { 1 } else { desc.num_planes };\n            if planes > 4 {\n                return Err(io::Error::new(\n                    io::ErrorKind::InvalidData,\n                    format!(\n                        \"drm: refusing a dma-buf descriptor with num_planes {} (1..=4)\",\n                        desc.num_planes\n                    ),\n                ));\n            }\n            desc.num_planes = planes;\n            let planes = planes as usize;\n            for p in 0..planes {\n                let extent = (desc.pitches[p] as usize)\n                    .checked_mul(h as usize)\n                    .and_then(|rows| rows.checked_add(desc.offsets[p] as usize));\n                match extent {\n                    Some(end) if end <= MAX_FRAME_BYTES => {}\n                    other => {\n                        return Err(io::Error::new(\n                            io::ErrorKind::InvalidData,","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/rustdesk/rustdesk/blob/7aa98d43cf1962a7a29ec16ffef42974377ef11e/libs/scrap/src/common/drm_render.rs#L62-L98","documentation":"The render-side converter refuses a dma-buf descriptor whose num_planes is greater than 4 (0 is normalized to 1 and written back so the C side reads the bounded count). The fixed pitches/offsets arrays hold 4 entries, so a larger count would read out of bounds during plane extent validation and conversion.","triggerScenarios":"convert() receives a descriptor with num_planes > 4 due to IPC corruption, struct layout mismatch between the serializer and the converter, or a malformed descriptor.","commonSituations":"Build/version skew between the service half and converter half; a compromised or buggy peer process; changed drmtap_dmabuf_desc layout after a libdrmtap upgrade without rebuilding both sides.","solutions":["Rebuild and redeploy the service and converter together so drmtap_dmabuf_desc layouts match.","Verify the libdrmtap version both halves load at runtime.","Drop and re-establish the capture session so a fresh descriptor is sent.","If reproducible with matched builds, report the descriptor contents upstream."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn plane_count_acceptable(desc: &DrmtapDmabufDesc) -> bool {\n    desc.num_planes <= 4 // 0 allowed; normalized to 1\n}","typeGuard":"fn is_converter_plane_refusal(e: &std::io::Error) -> bool {\n    e.kind() == std::io::ErrorKind::InvalidData\n        && e.to_string().contains(\"num_planes\")\n}","tryCatchPattern":"match converter.convert(&mut desc, fd) {\n    Ok(frame) => frame,\n    Err(e) if is_converter_plane_refusal(&e) => {\n        log::error!(\"bad plane count from peer: {e}\");\n        return reimport_or_fallback();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Deploy matched builds of both halves; drmtap_dmabuf_desc layout is part of their contract.","Never serialize descriptors from a newer build into an older converter.","Unit-test the round-trip of the desc struct across your IPC boundary in CI."],"tags":["drm","linux","rust","validation","multi-planar","trust-boundary"],"backgroundTag":null,"analyzedSha":"7aa98d43cf1962a7a29ec16ffef42974377ef11e","analyzedAt":"2026-08-16T06:17:34.842Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}