{"record":{"id":"f85726da409d58ac","repo":"t8y2/dbx","slug":"unterminated-pe-string","errorCode":null,"errorMessage":"unterminated PE string","messagePattern":"unterminated PE string","errorType":"exception","errorClass":"PeFormatError","httpStatus":null,"severity":"error","filePath":"agents/scripts/validate_windows_pe_dependencies.py","lineNumber":29,"sourceCode":"\ndef _read_u16(data: bytes, offset: int) -> int:\n    if offset < 0 or offset + 2 > len(data):\n        raise PeFormatError(\"unexpected end of PE file\")\n    return struct.unpack_from(\"<H\", data, offset)[0]\n\n\ndef _read_u32(data: bytes, offset: int) -> int:\n    if offset < 0 or offset + 4 > len(data):\n        raise PeFormatError(\"unexpected end of PE file\")\n    return struct.unpack_from(\"<I\", data, offset)[0]\n\n\ndef _read_c_string(data: bytes, offset: int) -> str:\n    if offset < 0 or offset >= len(data):\n        raise PeFormatError(\"PE string offset is outside the file\")\n    end = data.find(b\"\\0\", offset)\n    if end < 0:\n        raise PeFormatError(\"unterminated PE string\")\n    try:\n        return data[offset:end].decode(\"ascii\")\n    except UnicodeDecodeError as error:\n        raise PeFormatError(\"PE import name is not ASCII\") from error\n\n\ndef imported_dlls(path: Path) -> list[str]:\n    data = path.read_bytes()\n    if len(data) < 64 or data[:2] != b\"MZ\":\n        raise PeFormatError(\"missing DOS header\")\n\n    pe_offset = _read_u32(data, 0x3C)\n    if data[pe_offset : pe_offset + 4] != b\"PE\\0\\0\":\n        raise PeFormatError(\"missing PE signature\")\n\n    section_count = _read_u16(data, pe_offset + 6)\n    optional_header_size = _read_u16(data, pe_offset + 20)\n    optional_header_offset = pe_offset + 24","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/scripts/validate_windows_pe_dependencies.py#L11-L47","documentation":"PeFormatError raised by _read_c_string when data.find(b\"\\0\", offset) returns -1, meaning there is no NUL terminator between the given offset and end-of-file. The library throws it because an import DLL name is required to be a NUL-terminated ASCII string; running off the end indicates a corrupt import table or a bad RVA-to-offset mapping.","triggerScenarios":"imported_dlls() calls _read_c_string(data, rva_to_offset(name_rva)) and the resolved offset points at bytes that continue to EOF without a \\0 — e.g. name_rva maps into the very tail of the file, or the last section's raw data was truncated mid-string.","commonSituations":"Parsing truncated downloads or incomplete writes of PE files, files whose final section was cut during packing/unpacking, deliberately corrupted malware samples, or concatenation errors where a PE was appended to another file and the tail is missing.","solutions":["Re-download / rebuild the binary; a missing terminator usually means the file is truncated at the end.","Check the file size against the size implied by the last section's PointerToRawData + SizeOfRawData; if smaller, the copy is incomplete.","Catch PeFormatError and skip/fail the file in batch-validation pipelines instead of crashing.","If you produce PE files yourself, ensure every import-name string is NUL-terminated and covered by a mapped section."],"exampleFix":"// before\nfor path in paths:\n    imports[path] = imported_dlls(path)  # aborts whole batch\n// after\nfor path in paths:\n    try:\n        imports[path] = imported_dlls(path)\n    except PeFormatError as error:\n        print(f\"skipping {path}: {error}\")","handlingStrategy":"try-catch","validationCode":"data = path.read_bytes()\nif len(data) < 64 or data[:2] != b\"MZ\":\n    raise SystemExit(\"not a PE\")\nif data.rstrip(b\"\\0\")[-1:] not in (b\"\\0\", b\"\") and len(data) < 0x400:\n    print(\"warning: file looks truncated\")","typeGuard":"def looks_truncated(path: Path, expected_min: int) -> bool:\n    return path.stat().st_size < expected_min","tryCatchPattern":"try:\n    imports = imported_dlls(path)\nexcept PeFormatError as error:\n    if \"unterminated\" in str(error) or \"outside the file\" in str(error):\n        print(f\"{path}: corrupt/truncated PE: {error}\")\n    else:\n        raise","preventionTips":["Verify download integrity (hash/size) before validating binaries","Ensure build pipelines fully flush and close output files","Reject files smaller than their last section's raw extent","In batch jobs, catch PeFormatError per file instead of aborting the run"],"tags":["pe","binary-parsing","string-termination","malformed-input"],"backgroundTag":"pe-format-invalid","analyzedSha":"c0390bff16418b651f4728520d99adf8ce48829a","analyzedAt":"2026-09-05T23:05:10.900Z","contentChangedAt":"2026-09-05T23:05:10.900Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}