{"record":{"id":"cf8217554dabc24c","repo":"Hmbown/CodeWhale","slug":"android-loaded-image-identity-changed-proc-self","errorCode":null,"errorMessage":"Android loaded-image identity changed: /proc/self/maps has device/inode {:x}:{:x}:{}, but update target {} is {:x}:{:x}:{}; refusing to replace it","messagePattern":"Android loaded-image identity changed: /proc/self/maps has device/inode (.+?):(.+?):(.+?), but update target (.+?) is (.+?):(.+?):(.+?); refusing to replace it","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/cli/src/update.rs","lineNumber":487,"sourceCode":"#[cfg(any(target_os = \"android\", all(test, unix)))]\nfn validate_android_mapping_identity(\n    mapping: &AndroidImageMapping,\n    candidate: &Path,\n) -> Result<()> {\n    use std::os::unix::fs::MetadataExt;\n\n    let candidate_metadata = std::fs::metadata(candidate).with_context(|| {\n        format!(\n            \"failed to stat Android update target {}\",\n            candidate.display()\n        )\n    })?;\n    let (candidate_major, candidate_minor) = android_device_parts(candidate_metadata.dev());\n    let identity_matches = mapping.device_major == candidate_major\n        && mapping.device_minor == candidate_minor\n        && mapping.inode == candidate_metadata.ino();\n    if !identity_matches {\n        bail!(\n            \"Android loaded-image identity changed: /proc/self/maps has device/inode {:x}:{:x}:{}, but update target {} is {:x}:{:x}:{}; refusing to replace it\",\n            mapping.device_major,\n            mapping.device_minor,\n            mapping.inode,\n            candidate.display(),\n            candidate_major,\n            candidate_minor,\n            candidate_metadata.ino()\n        );\n    }\n    Ok(())\n}\n\n#[cfg(any(target_os = \"android\", all(test, unix)))]\nfn android_device_parts(device: u64) -> (u32, u32) {\n    // Linux/Bionic's dev_t encoding, matching makedev(3), major(3), and\n    // minor(3). `/proc/self/maps` renders these components in hexadecimal.\n    let major = ((device >> 8) & 0xfff) as u32;","sourceCodeStart":469,"sourceCodeEnd":505,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/cli/src/update.rs#L469-L505","documentation":"validate_android_mapping_identity stats the candidate update target and compares device major/minor plus inode against the /proc/self/maps row captured for the running image. A mismatch means the file at that path was replaced after the process started, so replacing it now could overwrite an unrelated file; the updater refuses (a deliberate TOCTOU guard).","triggerScenarios":"Android self-update while the underlying file changed since process start: a second updater ran concurrently, a package manager reinstalled the package, or the file was swapped by an installer between launch and update.","commonSituations":"Two codewhale update runs racing, pm/app-store upgrade racing a self-update, scripts that replace the binary while a long-lived session is open.","solutions":["Restart the CLI so it captures the new file's identity, then update again","Run only one self-update at a time; serialize updates in scripts","Avoid package-manager upgrades and self-update operating on the same install simultaneously"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"Catch the identity-mismatch error and prompt a restart of the CLI; a fresh process re-captures device/inode from the new file and the next update attempt passes.","preventionTips":["Serialize self-updates: one updater per install at a time","Keep package-manager upgrades and self-update from touching the same path concurrently","Restart the process after any in-place replacement before updating again"],"tags":["android","inode","toctou","self-update"],"backgroundTag":"binary-replaced-while-running","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}