{"record":{"id":"6b98cebcb9be1075","repo":"rustdesk/rustdesk","slug":"failed-to-open-ipc-parent-dir-no-follow-postfix","errorCode":null,"errorMessage":"failed to open ipc parent dir (no-follow): postfix={}, parent={}, err={}","messagePattern":"failed to open ipc parent dir \\(no-follow\\): postfix=(.+?), parent=(.+?), err=(.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"src/ipc/fs.rs","lineNumber":281,"sourceCode":"                    let mkdir_err = std::io::Error::last_os_error();\n                    // Handle a race where another process created the directory first.\n                    if mkdir_err.raw_os_error() != Some(hbb_common::libc::EEXIST) {\n                        return Err(Error::new(\n                            mkdir_err.kind(),\n                            format!(\n                                \"failed to mkdir ipc parent dir: postfix={}, parent={}, err={}\",\n                                postfix,\n                                parent_dir.display(),\n                                mkdir_err\n                            ),\n                        )\n                        .into());\n                    }\n                }\n                match open_ipc_parent_dir_fd(&parent_c) {\n                    Ok(fd) => fd,\n                    Err(err) => {\n                        return Err(Error::new(\n                            err.kind(),\n                            format!(\n                                \"failed to open ipc parent dir (no-follow): postfix={}, parent={}, err={}\",\n                                postfix,\n                                parent_dir.display(),\n                                err\n                            ),\n                        )\n                        .into());\n                    }\n                }\n            } else {\n                return Err(Error::new(\n                    open_err.kind(),\n                    format!(\n                        \"failed to open ipc parent dir (no-follow): postfix={}, parent={}, err={}\",\n                        postfix,\n                        parent_dir.display(),","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/rustdesk/rustdesk/blob/91c9fccbb0f7bfe5f11644d5fbdec9b23fa10540/src/ipc/fs.rs#L263-L299","documentation":"After creating (or confirming existence of) the IPC parent directory via mkdir, ensure_secure_ipc_parent_dir re-opens it with O_NOFOLLOW|O_DIRECTORY to get an fd for fchown/fchmod hardening. If this open fails, the errno is wrapped into this error. A failure here usually means the freshly-created path is not actually an openable real directory — most notably ELOOP when it is a symlink, or EACCES/EPERM.","triggerScenarios":"new_listener or new_drm_listener when open_ipc_parent_dir_fd fails after a successful/raced mkdir: the created 'directory' is a symlink planted between mkdir and open, permissions changed so the user cannot open it, or another component in the path is not a directory.","commonSituations":"A malicious or misconfigured pre-existing symlink at the IPC directory location (TOCTOU hardening doing its job); an attacker or leftover state replaced the dir; restricted environments where O_NOFOLLOW directory opens are denied.","solutions":["Inspect the parent path: if it is a symlink, remove it and let the code create a real directory (the O_NOFOLLOW check exists to reject exactly this).","Verify ownership and permissions of the directory after recreation (owned by the running user, mode 0700).","If another process races on the same path, coordinate cleanup — stop the other instance, remove stale dirs, and retry.","Re-run after fixing environment restrictions that block opening directories with O_NOFOLLOW."],"exampleFix":"// before: /tmp/rustdesk is a symlink planted by another user\nlrwxrwxrwx /tmp/rustdesk -> /home/attacker/x\n// after: replace with a real directory owned by the running user\nrm /tmp/rustdesk && mkdir -m 700 /tmp/rustdesk","handlingStrategy":"try-catch","validationCode":"let m = std::fs::symlink_metadata(\"/tmp/rustdesk\");\nif let Ok(m) = m {\n    if m.file_type().is_symlink() {\n        eprintln!(\"/tmp/rustdesk is a symlink; remove it before starting\");\n    }\n}","typeGuard":"fn is_real_dir(path: &str) -> bool {\n    std::fs::symlink_metadata(path)\n        .map(|m| m.is_dir() && !m.file_type().is_symlink())\n        .unwrap_or(false)\n}","tryCatchPattern":"match new_drm_listener(&path) {\n    Err(e) if e.to_string().contains(\"failed to open ipc parent dir\") => {\n        // likely symlink or permission issue; recreate the dir\n        let _ = std::fs::remove_dir_all(parent_if_symlink(&path));\n        new_drm_listener(&path)\n    }\n    other => other,\n}","preventionTips":["Remove stale IPC directories on uninstall/upgrade instead of leaving them","Monitor for symlinked IPC paths on shared/multi-user machines","Create IPC dirs with mode 0700 owned by the service user","Stop competing instances before cleanup so nothing replaces the dir mid-run"],"tags":["ipc","filesystem","symlink","toctou","no-follow"],"backgroundTag":"file-open-failed","analyzedSha":"91c9fccbb0f7bfe5f11644d5fbdec9b23fa10540","analyzedAt":"2026-09-10T19:53:44.083Z","contentChangedAt":"2026-09-10T19:53:44.083Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}