{"record":{"id":"f39141c68c810b19","repo":"jstedfast/MailKit","slug":"md4","errorCode":null,"errorMessage":"MD4","messagePattern":"MD4","errorType":"validation","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"MailKit/Security/Ntlm/MD4.cs","lineNumber":299,"sourceCode":"\t\t\tstate [0] += a;\n\t\t\tstate [1] += b;\n\t\t\tstate [2] += c;\n\t\t\tstate [3] += d;\n\t\t}\n\n\t\tpublic byte[] ComputeHash (byte[] buffer, int offset, int count)\n\t\t{\n\t\t\tif (buffer == null)\n\t\t\t\tthrow new ArgumentNullException (nameof (buffer));\n\n\t\t\tif (offset < 0 || offset > buffer.Length)\n\t\t\t\tthrow new ArgumentOutOfRangeException (nameof (offset));\n\n\t\t\tif (count < 0 || offset > buffer.Length - count)\n\t\t\t\tthrow new ArgumentOutOfRangeException (nameof (count));\n\n\t\t\tif (disposed)\n\t\t\t\tthrow new ObjectDisposedException (nameof (MD4));\n\n\t\t\tHashCore (buffer, offset, count);\n\t\t\thashValue = HashFinal ();\n\t\t\tInitialize ();\n\n\t\t\treturn hashValue;\n\t\t}\n\n\t\tpublic byte[] ComputeHash (byte[] buffer)\n\t\t{\n\t\t\tif (buffer == null)\n\t\t\t\tthrow new ArgumentNullException (nameof (buffer));\n\n\t\t\treturn ComputeHash (buffer, 0, buffer.Length);\n\t\t}\n\n\t\tpublic byte[] ComputeHash (Stream inputStream)\n\t\t{","sourceCodeStart":281,"sourceCodeEnd":317,"githubUrl":"https://github.com/jstedfast/MailKit/blob/9d3859a7855e3e17582c07fd01972b8e262bf176/MailKit/Security/Ntlm/MD4.cs#L281-L317","documentation":"ComputeHash(byte[]) throws ObjectDisposedException named \"MD4\" when the hasher has already been disposed. MD4 implements IDisposable like other HashAlgorithms; after Dispose(), its internal state is cleared and further hashing is invalid. This guards against silently producing wrong hashes from a reset/cleared instance.","triggerScenarios":"Calling ComputeHash(buffer) (or the offset/count overload, which routes here after its own checks) on an MD4 instance after a using block ended or Dispose() was called explicitly; caching an MD4 instance in a static field while also using it in a using statement.","commonSituations":"Long-lived NTLM helper class that holds an MD4 across requests while some code path disposes it; reusing a hasher created inside a using scope; double-dispose patterns where a wrapper disposes the hasher the caller still uses.","solutions":["Create a new MD4 instance for each hash operation instead of reusing a disposed one.","Remove the Dispose call (or the using block) on the path that still needs the hasher, and manage lifetime in one owner.","If reuse is needed, wrap access so Dispose happens only after all hashing completes (e.g. lazy dispose at service shutdown).","Optionally catch ObjectDisposedException at the boundary to detect lifetime bugs, but prefer fixing ownership."],"exampleFix":"// before\nusing (var md4 = new MD4()) { } \nmd4.ComputeHash(data); // disposed\n// after\nusing (var md4 = new MD4()) {\n    var hash = md4.ComputeHash(data);\n}","handlingStrategy":"validation","validationCode":"if (md4 == null) throw new InvalidOperationException(\"MD4 not created\");\n// avoid the situation: do not keep using an instance after 'using' or Dispose(); create a fresh one per operation.","typeGuard":"static bool IsUsable(MD4 md4) => md4 != null; // MD4 does not expose a disposed flag; enforce single-owner lifetime instead of probing","tryCatchPattern":"try { hash = md4.ComputeHash(data); }\ncatch (ObjectDisposedException) { md4 = new MD4(); hash = md4.ComputeHash(data); } // recovery: recreate; prefer fixing lifetime ownership","preventionTips":["Create one MD4 per hash operation; they are cheap","Never store IDisposable hashers in static fields shared across requests","Keep hashing inside the same using scope that created the instance"],"tags":["object-disposed","hash","lifetime","md4"],"backgroundTag":"invalid-state-transition","analyzedSha":"9d3859a7855e3e17582c07fd01972b8e262bf176","analyzedAt":"2026-09-15T15:46:11.592Z","contentChangedAt":"2026-09-15T15:46:11.592Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}