rustdesk/rustdesk · error · io::Error
drm: refusing a dma-buf descriptor with num_planes {} (1..=4
Error message
drm: refusing a dma-buf descriptor with num_planes {} (1..=4) What it means
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.
Source
Thrown at libs/scrap/src/common/drm_render.rs:80
/// Returns context-owned linear pixels valid ONLY until the next `convert()`; row stride is `len / height`.
pub fn convert(
&mut self,
desc: &mut drmtap_dmabuf_desc,
received_fd: RawFd,
) -> io::Result<(&[u8], u32, u32, Pixfmt)> {
{
let (w, h) = (desc.width, desc.height);
if w == 0 || h == 0 || w > MAX_DIM || h > MAX_DIM {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("drm: refusing a dma-buf descriptor with geometry {w}x{h}"),
));
}
// Reject, do not clamp, and write the normalized count back so the C reads the count bounded here.
let planes = if desc.num_planes == 0 { 1 } else { desc.num_planes };
if planes > 4 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"drm: refusing a dma-buf descriptor with num_planes {} (1..=4)",
desc.num_planes
),
));
}
desc.num_planes = planes;
let planes = planes as usize;
for p in 0..planes {
let extent = (desc.pitches[p] as usize)
.checked_mul(h as usize)
.and_then(|rows| rows.checked_add(desc.offsets[p] as usize));
match extent {
Some(end) if end <= MAX_FRAME_BYTES => {}
other => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,View on GitHub (pinned to 7aa98d43cf)
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.
Defensive patterns
Strategy: validation
Validate before calling
fn plane_count_acceptable(desc: &DrmtapDmabufDesc) -> bool {
desc.num_planes <= 4 // 0 allowed; normalized to 1
} Type guard
fn is_converter_plane_refusal(e: &std::io::Error) -> bool {
e.kind() == std::io::ErrorKind::InvalidData
&& e.to_string().contains("num_planes")
} Try / catch
match converter.convert(&mut desc, fd) {
Ok(frame) => frame,
Err(e) if is_converter_plane_refusal(&e) => {
log::error!("bad plane count from peer: {e}");
return reimport_or_fallback();
}
Err(e) => return Err(e),
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- DRM scanout num_planes {} out of range (1..=4)
- DRM scanout geometry {w}x{h} out of range
- DRM scanout plane {p} out of range (offset {} pitch {} over
- drm: refusing a dma-buf descriptor with geometry {w}x{h}
- drm: refusing dma-buf plane {p} (offset {} pitch {} over {h}
AI-assisted analysis of rustdesk/rustdesk@7aa98d43cf (2026-08-16).
Data as JSON: /api/errors/00f10ad0b3e8a93e.
Report an issue: GitHub.