{"record":{"id":"f67404801c6bd1a1","repo":"jdx/mise","slug":"elf-uses-pn-xnum-program-header-counts","errorCode":null,"errorMessage":"ELF uses PN_XNUM program header counts","messagePattern":"ELF uses PN_XNUM program header counts","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/system/packages/brew/elf.rs","lineNumber":117,"sourceCode":"#[derive(Clone, Copy)]\nstruct Phdr {\n    p_type: u32,\n    p_offset: u64,\n    p_vaddr: u64,\n    p_filesz: u64,\n    p_memsz: u64,\n    p_align: u64,\n}\n\nfn read_phdrs(content: &[u8]) -> Result<Vec<Phdr>> {\n    let e_phoff = rd_u64(content, 32)? as usize;\n    let e_phentsize = rd_u16(content, 54)? as usize;\n    let e_phnum = rd_u16(content, 56)? as usize;\n    if e_phentsize != PHDR_SIZE {\n        bail!(\"unexpected ELF e_phentsize {e_phentsize}\");\n    }\n    if e_phnum >= 0xffff {\n        bail!(\"ELF uses PN_XNUM program header counts\");\n    }\n    let mut phdrs = Vec::with_capacity(e_phnum);\n    for i in 0..e_phnum {\n        let off = e_phoff + i * PHDR_SIZE;\n        phdrs.push(Phdr {\n            p_type: rd_u32(content, off)?,\n            p_offset: rd_u64(content, off + 8)?,\n            p_vaddr: rd_u64(content, off + 16)?,\n            p_filesz: rd_u64(content, off + 32)?,\n            p_memsz: rd_u64(content, off + 40)?,\n            p_align: rd_u64(content, off + 48)?,\n        });\n    }\n    Ok(phdrs)\n}\n\nfn vaddr_to_offset(phdrs: &[Phdr], vaddr: u64) -> Option<usize> {\n    phdrs","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/system/packages/brew/elf.rs#L99-L135","documentation":"The minimal ELF parser does not implement extended program header numbering (PN_XNUM): when e_phnum is 0xffff or more, the real count lives in sh_info of section header 0. Such binaries — only possible with 65535+ program headers — are rejected instead of misparsed.","triggerScenarios":"elf::patch -> read_phdrs: e_phnum = rd_u16(content, 56) >= 0xffff while pouring a Linux bottle. Requires a pathological binary with an enormous program header table, e.g. custom-built test artifacts or fuzzing corpora; ordinary bottles never hit it.","commonSituations":"Pouring hand-crafted or fuzz-generated ELF binaries; binaries from research/experimental toolchains that emit thousands of segments.","solutions":["Pour the affected formula with upstream Homebrew (PatchELF/ruby handle PN_XNUM differently) instead of the internal rewriter","Rebuild the binary with a normal number of program headers if you control its build","Report upstream — PN_XNUM support would need to read the count from section header 0"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Reject PN_XNUM binaries before calling the rewriter.\nfn phnum_supported(content: &[u8]) -> bool {\n    content.len() >= 58\n        && u16::from_le_bytes(content[56..58].try_into().unwrap()) < 0xffff\n}","typeGuard":"fn elf_patchable(content: &[u8]) -> bool {\n    is_elf(content) && content.len() >= 58 && content[4] == 2 && content[5] == 1\n        && u16::from_le_bytes(content[54..56].try_into().unwrap()) == 56\n        && u16::from_le_bytes(content[56..58].try_into().unwrap()) < 0xffff\n}","tryCatchPattern":"// Detect the unsupported extension and route the formula to a fallback\n// pour path instead of failing the install.\nif u16::from_le_bytes(content[56..58].try_into().unwrap()) >= 0xffff {\n    debug!(\"{}: PN_XNUM not supported, skipping internal relocation\", path.display());\n    return Ok(false);\n}","preventionTips":["Don't pour fuzzing corpora or research binaries with huge phdr counts through the internal rewriter","Check e_phnum with readelf -h first when automating pours of untrusted binaries"],"tags":["brew","elf","linux","binary-patching","unsupported-feature"],"backgroundTag":"elf-header-validation-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}