NationalSecurityAgency/ghidra · error · IllegalArgumentException

Invalid MD5 hash string: {md5}

Error message

Invalid MD5 hash string: {md5}

What it means

Thrown by ExecutableRecord.checkValidMD5 when the supplied md5 string does not match the md5Matcher regex. checkValidMD5 is called from the ExecutableRecord constructor, so any path that builds an ExecutableRecord (newExecutableRecord, restoreXml, etc.) with a malformed md5 triggers it. It is an IllegalArgumentException (unchecked), not an LSHException.

Source

Thrown at Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/description/ExecutableRecord.java:88

	private List<CategoryRecord> usercat; // Categories this executable belongs to
	private int xrefIndex; // Index for cross-referencing this executable from other records

	public static class Update {
		public ExecutableRecord update;
		public boolean name_exec; // Should name be updated
		public boolean architecture; // Should architecture be updated
		public boolean name_compiler;
		public boolean repository;
		public boolean path;
		public boolean date;
		public boolean categories; // True if there are either insertions or deletions
		public List<CategoryRecord> catinsert; // Non-null, if there are only insertions
	}

	private static void checkValidMD5(String md5) {
		Matcher matcher = md5Matcher.matcher(md5);
		if (!matcher.matches()) {
			throw new IllegalArgumentException("Invalid MD5 hash string: " + md5);
		}
	}

	/**
	 * Convert a 32-bit integer to hexadecimal ascii representation
	 * @param val is the integer to encode
	 * @param buf accumulates the resulting ascii
	 */
	private static void wordToAscii(int val, StringBuilder buf) {
		for (int i = 28; i >= 0; i -= 4) {
			final int nibble = (val >> i) & 0xf;
			if (nibble < 10) {
				buf.append((char) (nibble + '0'));
			}
			else {
				buf.append((char) (nibble - 10 + 'a'));
			}
		}

View on GitHub (pinned to d5f144c24d)

Solutions

  1. Compute a real md5 and pass the 32-character lowercase hex string the matcher expects.
  2. Validate the md5 against a hex regex (32 hex chars) before constructing the ExecutableRecord.
  3. Normalize case (toLowerCase) and trim whitespace before passing.
  4. Ensure you are not passing a different digest algorithm's output.

Example fix

// before
man.newExecutableRecord(maybeBadMd5, name, compiler, arch, date, repo, path, id);

// after
if (!md5.matches("[0-9a-fA-F]{32}")) throw new IllegalArgumentException("bad md5");
man.newExecutableRecord(md5.toLowerCase(), name, compiler, arch, date, repo, path, id);
Defensive patterns

Strategy: validation

Validate before calling

private static final java.util.regex.Pattern MD5 = java.util.regex.Pattern.compile("[0-9a-fA-F]{32}");
if (md5 == null || !MD5.matcher(md5).matches()) {
    throw new IllegalArgumentException("md5 must be 32 hex chars: " + md5);
}
man.newExecutableRecord(md5.toLowerCase(Locale.ROOT), ...);

Type guard

boolean isValidMd5(String s) { return s != null && s.length() == 32 && s.matches("[0-9a-fA-F]{32}"); }

Try / catch

try {
    man.newExecutableRecord(md5, ...);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Invalid MD5 hash string")) {
        // recompute md5 and retry
    } else throw e;
}

Prevention

When it happens

Trigger: Constructing an ExecutableRecord or calling newExecutableRecord with a md5 string that is not a valid hex md5 (wrong length, non-hex characters, null-content, upper/lower case outside the allowed pattern).

Common situations: Passing a raw file hash with uppercase or with spaces; truncating the md5; passing an SHA-256 or other hash type instead of md5; empty or null-derived string; copy-paste introducing stray characters.

Related errors


AI-assisted analysis of NationalSecurityAgency/ghidra@d5f144c24d (2026-08-14). Data as JSON: /api/errors/b74034f8e39166b6. Report an issue: GitHub.