{"record":{"id":"2d02e73a59131f05","repo":"NationalSecurityAgency/ghidra","slug":"duplicate-md5-hash-different-metadata","errorCode":null,"errorMessage":"Duplicate md5 hash, different metadata","messagePattern":"Duplicate md5 hash, different metadata","errorType":"exception","errorClass":"LSHException","httpStatus":null,"severity":"error","filePath":"Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java","lineNumber":232,"sourceCode":"\t * @param md5 is the MD5 hash of the executable\n\t * @param enm is the name of the executable\n\t * @param cnm is the name of the compiler used to build the executable\n\t * @param arc is the architecture of the executable\n\t * @param dt is the date (of ingest)\n\t * @param repo is the repository containing the executable\n\t * @param path is the path (within the repo) to the executable\n\t * @param id is the database (row) is associated with the executable (may be null)\n\t * @return the new ExecutableRecord object\n\t * @throws LSHException if attributes are invalid, or the executable \n\t *     already exists with different metadata\n\t */\n\tpublic ExecutableRecord newExecutableRecord(String md5, String enm, String cnm, String arc,\n\t\t\tDate dt, String repo, String path, RowKey id) throws LSHException {\n\t\tExecutableRecord newexe = new ExecutableRecord(md5, enm, cnm, arc, dt, id, repo, path);\n\t\tif (!exerec.add(newexe)) {\n\t\t\tExecutableRecord oldexe = exerec.floor(newexe);\n\t\t\tif (oldexe.compareMetadata(newexe) != 0) {\n\t\t\t\tthrow new LSHException(\"Duplicate md5 hash, different metadata\");\n\t\t\t}\n\t\t\tif ((oldexe.getRowId() != null) && (id != null) && (!oldexe.getRowId().equals(id))) {\n\t\t\t\tthrow new LSHException(\"Overwriting existing executable id\");\n\t\t\t}\n\t\t\tnewexe = oldexe;\n\t\t}\n\t\treturn newexe;\n\t}\n\n\t/**\n\t * Create a new \"library\" executable in the container.\n\t * Functions in this container (will) have no body or address\n\t * @param enm is the name of the library\n\t * @param arc is the architecture of the library\n\t * @param id is the database id associated with the library (may be null)\n\t * @return the new ExecutableRecord object\n\t * @throws LSHException if attributes are invalid or the\n\t *   library already exists with different metadata","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/NationalSecurityAgency/ghidra/blob/d5f144c24d6bc53c9cbf4448c6d11143e7696206/Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/DescriptionManager.java#L214-L250","documentation":"Thrown by DescriptionManager.newExecutableRecord when an executable with the same md5 hash already exists in the container but has different metadata (name, compiler, architecture, date, repo, or path). MD5 is the identity key for executables; conflicting metadata for the same md5 indicates an inconsistency the manager refuses to merge silently.","triggerScenarios":"Calling newExecutableRecord with an md5 that already exists in the DescriptionManager's exerec set, where oldexe.compareMetadata(newexe) returns non-zero. Happens when loading/describing the same binary twice with differing metadata fields — e.g., same md5 but different declared compiler or architecture, or different repository/path.","commonSituations":"Loading the same executable from two sources that annotate different metadata (compiler mismatch, name normalization differences). Inconsistent metadata in a BSim ingest pipeline. Re-describing a binary after partial changes. Merging two DescriptionManagers with conflicting metadata for shared md5s.","solutions":["Reconcile the metadata before re-adding: use the existing ExecutableRecord's metadata consistently across all ingest of the same md5.","If the new metadata is authoritative, remove the old ExecutableRecord first (and its functions) then re-add.","Detect the duplicate md5 upstream and reuse the existing record instead of calling newExecutableRecord again.","Audit the ingest pipeline to ensure deterministic metadata (same compiler/arch/path) for identical binaries."],"exampleFix":"// before\ndescMgr.newExecutableRecord(md5, name2, compiler2, arch2, date2, repo2, path2, id);\n// throws if md5 exists with different metadata\n\n// after\nExecutableRecord existing = descMgr.getExecutableRecord(md5);\nif (existing != null) {\n    if (existing.compareMetadata(newRecord) != 0) {\n        // resolve conflict or reuse existing\n        return existing;\n    }\n    return existing;\n}\nreturn descMgr.newExecutableRecord(md5, name, compiler, arch, date, repo, path, id);","handlingStrategy":"validation","validationCode":"// Detect duplicate md5 and reconcile metadata before adding.\nExecutableRecord existing = descMgr.findExecutableByMd5(md5);\nif (existing != null) {\n    if (existing.compareMetadata(proposedRecord) != 0) {\n        // reconcile or fail fast with context\n        throw new IllegalStateException(\"Metadata conflict for md5 \" + md5);\n    }\n    return existing; // reuse\n}\nreturn descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, id);","typeGuard":null,"tryCatchPattern":"try {\n    return descMgr.newExecutableRecord(md5, name, comp, arch, date, repo, path, id);\n} catch (LSHException e) {\n    if (e.getMessage().contains(\"different metadata\")) {\n        // log the conflicting fields and surface a clear error\n        throw new RuntimeException(\"Metadata conflict for md5 \" + md5 + \"; reconcile source data\", e);\n    }\n    throw e;\n}","preventionTips":["Canonicalize metadata (compiler, arch, name, path) for identical binaries across the pipeline.","Check for an existing md5 before calling newExecutableRecord.","Remove the old record (and functions) if new metadata is authoritative."],"tags":["bsim","metadata","duplicate-md5","conflict","validation","lsh-exception"],"backgroundTag":null,"analyzedSha":"d5f144c24d6bc53c9cbf4448c6d11143e7696206","analyzedAt":"2026-08-14T01:00:57.564Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}