Hmbown/CodeWhale · error

loaded-image mapping for updater marker has no file inode

Error message

loaded-image mapping for updater marker has no file inode

What it means

When parsing the /proc/self/maps line backing the updater image, a zero inode means the mapping is anonymous (not backed by a file on disk). Since the purpose is to prove which real file the executable was loaded from, an anonymous mapping cannot serve as proof and this error is thrown.

Solutions

  1. Launch the binary from a real filesystem file so its exec mapping has a nonzero inode.
  2. Continue scanning /proc/self/maps for a file-backed (inode != 0) executable mapping covering the marker.
  3. Use /proc/self/exe as an alternate source of executable identity when available.
  4. Rely on the platform package/update channel rather than self-update in memfd execution contexts.

Example fix

// before
// executing binary via memfd
let fd = memfd_create(...)?; execveat(fd, ...)?;
// after
std::fs::write("/data/local/tmp/codewhale", bytes)?;
Command::new("/data/local/tmp/codewhale")  // file-backed exec, inode != 0
Defensive patterns

Strategy: validation

Validate before calling

// anonymous mappings have inode 0 — skip them
let inode = line.split_whitespace().nth(4)
    .and_then(|f| f.parse::<u64>().ok())
    .unwrap_or(0);
if inode == 0 { /* not file-backed; skip */ }

Type guard

fn is_file_backed(inode: u64) -> bool { inode != 0 }

Try / catch

match parse_android_image_mapping(line) {
    Err(e) if e.to_string().contains("no file inode") => continue,
    other => other?,
}

Prevention

When it happens

Trigger: android_loaded_executable_proof_report parsing a maps line for the marker whose inode field is 0 — e.g. memfd, shared memory, or deleted-anonymous executable mapping.

Common situations: Binary executed from memory (memfd_create) or via an unpacking loader; overlay/tmpfs environments reporting inode 0; the earlier dladdr path already failed so maps fallback hit an anonymous exec mapping.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/0bd038a119871a32. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/update.rs:388

            .context("loaded-image mapping is missing permissions")?;
        let _offset = fields
            .next()
            .context("loaded-image mapping is missing its file offset")?;
        let device = fields
            .next()
            .context("loaded-image mapping is missing its device")?;
        let inode = fields
            .next()
            .context("loaded-image mapping is missing its inode")?
            .parse::<u64>()
            .context("loaded-image mapping has an invalid inode")?;
        let path = fields.collect::<Vec<_>>().join(" ");

        if permissions.as_bytes().get(2) != Some(&b'x') {
            bail!("loaded-image mapping for updater marker is not executable");
        }
        if inode == 0 {
            bail!("loaded-image mapping for updater marker has no file inode");
        }
        let (device_major, device_minor) = device
            .split_once(':')
            .context("loaded-image mapping has an invalid device")?;
        let device_major = u32::from_str_radix(device_major, 16)
            .context("loaded-image mapping has an invalid device major number")?;
        let device_minor = u32::from_str_radix(device_minor, 16)
            .context("loaded-image mapping has an invalid device minor number")?;
        if path.is_empty() {
            bail!("loaded-image mapping for updater marker has no pathname");
        }

        let mapping = AndroidImageMapping {
            start,
            end,
            device_major,
            device_minor,
            inode,

View on GitHub (pinned to 73e0f67d83)