{"record":{"id":"2ddb7175ad4cdce3","repo":"t8y2/dbx","slug":"pe-import-descriptor-table-is-not-terminated","errorCode":null,"errorMessage":"PE import descriptor table is not terminated","messagePattern":"PE import descriptor table is not terminated","errorType":"exception","errorClass":"PeFormatError","httpStatus":null,"severity":"error","filePath":"agents/scripts/validate_windows_pe_dependencies.py","lineNumber":101,"sourceCode":"                    break\n                return raw_offset + delta\n        raise PeFormatError(f\"PE RVA 0x{rva:x} is not backed by file data\")\n\n    descriptor_offset = rva_to_offset(import_directory_rva)\n    imports = []\n    for _ in range(4096):\n        if descriptor_offset + 20 > len(data):\n            raise PeFormatError(\"truncated PE import descriptor\")\n        descriptor = data[descriptor_offset : descriptor_offset + 20]\n        if descriptor == b\"\\0\" * 20:\n            return sorted(set(imports), key=str.casefold)\n        name_rva = _read_u32(data, descriptor_offset + 12)\n        if name_rva == 0:\n            raise PeFormatError(\"PE import descriptor has no DLL name\")\n        imports.append(_read_c_string(data, rva_to_offset(name_rva)))\n        descriptor_offset += 20\n\n    raise PeFormatError(\"PE import descriptor table is not terminated\")\n\n\ndef forbidden_msvc_runtime_dlls(imports: list[str]) -> list[str]:\n    return sorted(\n        {name for name in imports if name.casefold().startswith((\"msvcp\", \"vcruntime\"))},\n        key=str.casefold,\n    )\n\n\ndef main() -> int:\n    parser = argparse.ArgumentParser(description=\"Reject Windows PE files that require the Visual C++ runtime\")\n    parser.add_argument(\"binary\", type=Path)\n    args = parser.parse_args()\n\n    try:\n        imports = imported_dlls(args.binary)\n    except (OSError, PeFormatError) as error:\n        parser.error(str(error))","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/t8y2/dbx/blob/c0390bff16418b651f4728520d99adf8ce48829a/agents/scripts/validate_windows_pe_dependencies.py#L83-L119","documentation":"A PE import descriptor array must end with an all-zero (20-byte) terminator. The parser walks at most 4096 descriptors; if it exhausts that bound without hitting the terminator, it raises this error. This guards against runaway or cyclic descriptor tables that would otherwise cause unbounded parsing.","triggerScenarios":"imported_dlls(path) on a PE with more than 4096 import descriptors, or with a descriptor array that never contains the all-zero terminator (corrupt/forged import directory RVA pointing at non-descriptor data, or a crafted cyclic table).","commonSituations":"Extremely large binaries linking thousands of DLLs (rare but possible in monolithic builds); deliberately malformed PE samples; import directory RVA pointing into unrelated data so terminator bytes never appear; automated fuzzing corpora.","solutions":["Check the real import count with dumpbin /imports or pefile; if it exceeds 4096, raise the loop bound in the script.","Verify the import directory RVA/size (data directory index 1) actually points at a descriptor array; a wrong RVA makes the terminator unreachable.","Rebuild the binary with a standard linker; a missing terminator indicates a malformed or hand-patched import table.","Treat the file as unverifiable: catch PeFormatError and fail the specific artifact rather than the whole batch."],"exampleFix":"// before\nfor _ in range(4096):\n    ...\nraise PeFormatError(\"PE import descriptor table is not terminated\")\n// after\nmax_descriptors = import_directory_size // 20\nfor _ in range(max_descriptors):\n    ...","handlingStrategy":"validation","validationCode":"import pefile\n\ndef import_count_within_limits(path, max_descriptors: int = 4096) -> bool:\n    pe = pefile.PE(path)\n    return len(getattr(pe, \"DIRECTORY_ENTRY_IMPORT\", [])) <= max_descriptors","typeGuard":null,"tryCatchPattern":"try:\n    imports = imported_dlls(path)\nexcept PeFormatError as err:\n    if \"not terminated\" in str(err):\n        fail_artifact(path, \"unterminated or oversized import table\")\n    else:\n        raise","preventionTips":["If you link binaries with thousands of imports, raise the 4096 descriptor bound before validating.","Confirm the import directory RVA points at actual descriptor data (index 1 of the data directories).","Sanity-check that the last descriptor read via pefile is the zero terminator.","Treat fuzzed/untrusted samples in a sandbox with pre-validation via pefile."],"tags":["pe-format","import-table","binary-parsing","malformed-file","python"],"backgroundTag":"pe-import-table-not-terminated","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"}