RFR(S): 8205946: JVM crash after call to ClassLoader::setup_bootstrap_search_path()

Calvin Cheung calvin.cheung at oracle.com
Tue Jul 10 22:27:16 UTC 2018


Hi David,

Thanks for chiming in.

I take that I should go with webrev.01 of the change?

thanks,
Calvin

On 7/10/18, 3:01 PM, David Holmes wrote:
> Calling vm_exit_during_initialization is very unfriendly for 
> applications that host the JVM in-process directly. With that in mind 
> decisions to call vm_exit_* should done at as a high a level as 
> feasible in the code - ie. a low-level method like os::set_boot_path 
> should never IMHO be making a decision as to whether an error it 
> encounters is fatal to the whole VM initialization process. That's a 
> decision to be made higher up.
>
> My 2c.
>
> David
>
> On 11/07/2018 7:17 AM, Calvin Cheung wrote:
>> Hi Jiangli,
>>
>> Thanks for reviewing.
>>
>> On 7/10/18, 12:18 PM, Jiangli Zhou wrote:
>>> Hi Calvin,
>>>
>>> The error handling code in platform specific code are identical. I 
>>> like Lois’ suggestion to check and exit in os::set_boot_path() to 
>>> avoid duplicating the code.
>> If you want changes in os::set_boot_path() instead of in platform 
>> specific code, I'm proposing the following:
>> diff --git a/src/hotspot/share/runtime/os.cpp 
>> b/src/hotspot/share/runtime/os.cpp
>> --- a/src/hotspot/share/runtime/os.cpp
>> +++ b/src/hotspot/share/runtime/os.cpp
>> @@ -1270,7 +1270,7 @@
>>     return file;
>>   }
>>
>> -bool os::set_boot_path(char fileSep, char pathSep) {
>> +void os::set_boot_path(char fileSep, char pathSep) {
>>     const char* home = Arguments::get_java_home();
>>     int home_len = (int)strlen(home);
>>
>> @@ -1278,26 +1278,30 @@
>>
>>     // modular image if "modules" jimage exists
>>     char* jimage = format_boot_path("%/lib/" MODULES_IMAGE_NAME, 
>> home, home_len, fileSep, pathSep);
>> -  if (jimage == NULL) return false;
>> +  if (jimage == NULL) {
>> +    vm_exit_during_initialization("Failed setting boot class path.", 
>> NULL);
>> +  }
>>     bool has_jimage = (os::stat(jimage, &st) == 0);
>>     if (has_jimage) {
>>       Arguments::set_sysclasspath(jimage, true);
>>       FREE_C_HEAP_ARRAY(char, jimage);
>> -    return true;
>> +    return;
>>     }
>>     FREE_C_HEAP_ARRAY(char, jimage);
>>
>>     // check if developer build with exploded modules
>>     char* base_classes = format_boot_path("%/modules/" 
>> JAVA_BASE_NAME, home, home_len, fileSep, pathSep);
>> -  if (base_classes == NULL) return false;
>> +  if (base_classes == NULL) {
>> +    vm_exit_during_initialization("Failed setting boot class path.", 
>> NULL);
>> +  }
>>     if (os::stat(base_classes, &st) == 0) {
>>       Arguments::set_sysclasspath(base_classes, false);
>>       FREE_C_HEAP_ARRAY(char, base_classes);
>> -    return true;
>> +    return;
>>     }
>>     FREE_C_HEAP_ARRAY(char, base_classes);
>> -
>> -  return false;
>> +  vm_exit_during_initialization("Failed setting boot class path.", 
>> NULL);
>> +  return;
>>   }
>>
>> The function fails if os::stat() fails or allocation of buffer for 
>> "jimage" or "base_classes" fails.
>> Since vm should exit on failure in the above function, it is 
>> unnecessary for the function to return a bool.
>>
>>>
>>> Also, under low memory condition, set_value() might fail to allocate 
>>> and not trigger any error with a release binary.  
>>> os::set_boot_path() probably should also check and make sure sys 
>>> path is not NULL after Arguments::set_sysclasspath().
>> The AllocateHeap() you listed below will call into the following 
>> which will exit on out of memory. So I don' think we need to do NULL 
>> check again.
>>
>> // allocate using malloc; will fail if no memory available
>> char* AllocateHeap(size_t size,
>>                     MEMFLAGS flags,
>>                     const NativeCallStack& stack,
>>                     AllocFailType alloc_failmode /* = 
>> AllocFailStrategy::EXIT_OOM*/) {
>>    char* p = (char*) os::malloc(size, flags, stack);
>>    if (p == NULL && alloc_failmode == AllocFailStrategy::EXIT_OOM) {
>>      vm_exit_out_of_memory(size, OOM_MALLOC_ERROR, "AllocateHeap");
>>    }
>>    return p;
>> }
>>
>> thanks,
>> Calvin
>>>
>>> bool PathString::set_value(const char *value) {
>>> if (_value != NULL) {
>>>     FreeHeap(_value);
>>>   }
>>>   _value = AllocateHeap(strlen(value)+1, mtArguments);
>>>   assert(_value != NULL, "Unable to allocate space for new path 
>>> value");
>>> if (_value != NULL) {
>>>     strcpy(_value, value);
>>>   } else {
>>> // not able to allocate
>>> return false;
>>>   }
>>> returntrue;
>>> }
>>>
>>> Thanks,
>>> Jiangli
>>>
>>>> On Jul 10, 2018, at 10:31 AM, Calvin Cheung 
>>>> <calvin.cheung at oracle.com <mailto:calvin.cheung at oracle.com>> wrote:
>>>>
>>>> Updated webrev with the changes mentioned below:
>>>> http://cr.openjdk.java.net/~ccheung/8205946/webrev.01/ 
>>>> <http://cr.openjdk.java.net/%7Eccheung/8205946/webrev.01/>
>>>>
>>>> I've rerun hs-tier{1,2,3} tests.
>>>>
>>>> thanks,
>>>> Calvin
>>>>
>>>> On 7/9/18, 1:43 PM, Calvin Cheung wrote:
>>>>> Hi Lois,
>>>>>
>>>>> Thanks for your review.
>>>>>
>>>>> On 7/9/18, 11:58 AM, Lois Foltan wrote:
>>>>>> On 7/9/2018 1:29 PM, Calvin Cheung wrote:
>>>>>>
>>>>>>> bug: https://bugs.openjdk.java.net/browse/JDK-8205946
>>>>>>>
>>>>>>> webrev: http://cr.openjdk.java.net/~ccheung/8205946/webrev.00/ 
>>>>>>> <http://cr.openjdk.java.net/%7Eccheung/8205946/webrev.00/>
>>>>>>>
>>>>>>> The JVM crash could be simulated by renaming/removing the 
>>>>>>> modules file under the jdk/lib directory.
>>>>>>> The proposed simple fix is to perform a 
>>>>>>> vm_exit_during_initialization().
>>>>>>
>>>>>> Hi Calvin,
>>>>>>
>>>>>> Some clarifying questions.  Is this just an issue for exploded 
>>>>>> builds?
>>>>> I don't think so. As mentioned above, I could reproduce the crash 
>>>>> with a regular jdk image build by renaming the modules file under 
>>>>> the jdk/lib directory.
>>>>>>  I would prefer the exit to occur if the os::stat() fails for the 
>>>>>> system class path in os::set_boot_path().
>>>>> Instead of exiting in os::set_boot_path(), how about checking the 
>>>>> return status of os::set_boot_path() in the caller and exiting 
>>>>> there like the following:
>>>>> bash-4.2$ hg diff os_linux.cpp
>>>>> diff --git a/src/hotspot/os/linux/os_linux.cpp 
>>>>> b/src/hotspot/os/linux/os_linux.cpp
>>>>> --- a/src/hotspot/os/linux/os_linux.cpp
>>>>> +++ b/src/hotspot/os/linux/os_linux.cpp
>>>>> @@ -367,7 +367,9 @@
>>>>>       }
>>>>>     }
>>>>>     Arguments::set_java_home(buf);
>>>>> -    set_boot_path('/', ':');
>>>>> +    if (!set_boot_path('/', ':')) {
>>>>> +      vm_exit_during_initialization("Failed setting boot class 
>>>>> path.", NULL);
>>>>> +    }
>>>>>   }
>>>>>
>>>>> Note that before the above change, the return status of 
>>>>> set_boot_path() isn't checked.
>>>>> The above would involve changing 5 of those os_*.cpp files, one 
>>>>> for each O/S.
>>>>>
>>>>>>  With certainly an added assert later in 
>>>>>> ClassLoader::setup_bootstrap_search_path() to ensure that the 
>>>>>> system class path is never NULL.
>>>>> Sure, I can add an assert there.
>>>>> I'll post updated webrev once I've made the change and done testing.
>>>>>
>>>>> thanks,
>>>>> Calvin
>>>>>>
>>>>>> Thanks,
>>>>>> Lois
>>>>>>
>>>>>>>
>>>>>>> Ran hs-tier{1,2,3} tests successfully including the new test case.
>>>>>>>
>>>>>>> thanks,
>>>>>>> Calvin
>>>>>>
>>>


More information about the hotspot-runtime-dev mailing list