RFR: 8365526: Crash with null Symbol passed to SystemDictionary::resolve_or_null [v2]
Ioi Lam
iklam at openjdk.org
Sat Nov 22 01:08:23 UTC 2025
On Fri, 21 Nov 2025 18:57:28 GMT, Coleen Phillimore <coleenp at openjdk.org> wrote:
>> The vm was crashing because the constant pool couldn't find the resolution error in the ResolutionErrorEntry error field.
>>
>> There are two uses of ResolutionErrorEntry in the ResolutionErrorTable. The key to this table is {ConstantPool, cp-index}. In this crash, multiple threads were racing to record nest_host_errors in the case where resolution failed. In this case, there was already a ResolutionErrorEntry in the table for the constant pool resolution failure. In the 'if' case of add_nest_host_error we check to see if there's already a nest_host_error assuming it's the same error, then the 'else' case was unconditionally adding a ResolutionErrorEntry with just the nest host message. Calling HashTable::put() with this entry with just the nest host message, was overwriting the entry with the constant pool resolution error, ie. the other fields. The crash happened in ConstantPool::throw_resolution_error() because the error field was overwritten (and leaked too).
>>
>> Add a null check before calling ResolutionErrorEntry add entry. Also added an assert that we only add a resolution error for nest host errors in the case of success since in the case of failure there will always already be a ResolutionErrorEntry for the failing constant pool and cp index and we don't want to overwrite that again.
>>
>> Tested with submitted reproducer and tier1-4.
>
> Coleen Phillimore has updated the pull request incrementally with one additional commit since the last revision:
>
> Reorder if statement and add an assert.
src/hotspot/share/classfile/resolutionErrors.cpp line 90:
> 88: bool created = false;
> 89: _resolution_error_table->put_if_absent(key, entry, &created);
> 90: assert(created, "should be created not updated");
The above 3 lines can be replaced with `_resolution_error_table->put_when_absent(key, entry)`.
src/hotspot/share/classfile/systemDictionary.cpp line 1870:
> 1868: // Only add a new resolution error if one hasn't been found for this constant pool index. In this case,
> 1869: // resolution succeeded but there's an error in this nest host.
> 1870: assert(pool->resolved_klass_at(which) != nullptr, "klass is should be resolved if there is no entry");
typo: `"klass should be resolved ...`
src/hotspot/share/classfile/systemDictionary.cpp line 1877:
> 1875: // only reach here under the same error condition, so we can ignore the potential race with setting
> 1876: // the message. If we see it is already set then we can ignore it.
> 1877: entry->set_nest_host_error(message);
Existing -- shouldn't we free the old `entry->_nest_host_error`?
Also, there's a related memory leak here:
// Add entry to resolution error table to record the error when the first
// attempt to resolve a reference to a class has failed.
void SystemDictionary::add_resolution_error(const constantPoolHandle& pool, int which,
Symbol* error, const char* message,
Symbol* cause, const char* cause_msg) {
{
MutexLocker ml(Thread::current(), SystemDictionary_lock);
ResolutionErrorEntry* entry = ResolutionErrorTable::find_entry(pool, which);
if (entry == nullptr) {
ResolutionErrorTable::add_entry(pool, which, error, message, cause, cause_msg);
} else {
// message and cause_msg are leaked <<<<<<<<<<
}
}
}
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/28438#discussion_r2551404434
PR Review Comment: https://git.openjdk.org/jdk/pull/28438#discussion_r2551510022
PR Review Comment: https://git.openjdk.org/jdk/pull/28438#discussion_r2551420213
More information about the hotspot-runtime-dev
mailing list