JDK-8224980 : FLAG_SET_ERGO silently ignores invalid values
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 13
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2019-05-29
  • Updated: 2023-07-26
  • Resolved: 2023-02-16
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 21
21 b11Fixed
Related Reports
Duplicate :  
Relates :  
Description
If code tries to set a flag with a value that violates its constraint, the setting will be ignored and no warning is reported.

This can be seen by applying the following patch:
diff --git a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp
--- a/src/hotspot/share/runtime/flags/jvmFlag.cpp
+++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp
@@ -1444,7 +1444,7 @@
 }
 
 void JVMFlag::printError(bool verbose, const char* msg, ...) {
-  if (verbose) {
+  if (true) {
     va_list listPointer;
     va_start(listPointer, msg);
     jio_vfprintf(defaultStream::error_stream(), msg, listPointer);

And running:
test/hotspot/jtreg/runtime/CommandLine/OptionsValidation

The failing test shows that with the following command line:
$ ../build/release/images/jdk/bin/java -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=TestOptionsWithRanges.jsa -Xshare:dump -XX:SharedBaseAddress=0

We try to set NonNMethodCodeHeapSize to 0:
uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ]

And if we check -XX:+PrintFlagsFinal | grep NonNMethodCodeHeapSize we see that the flag has not been set:
uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ]
    uintx NonNMethodCodeHeapSize                   = 5242880                                {pd product} {default}

The code that tries to set the flag to a value outside its range is here:
  if (SegmentedCodeCache) {                                                                                                                                                                   
    // Use multiple code heaps                                                                                                                                                                
    initialize_heaps();                                                                                                                                                                       
  } else {                                                                                                                                                                                    
    // Use a single code heap                                                                                                                                                                 
    FLAG_SET_ERGO(NonNMethodCodeHeapSize, 0);                                                                                                                                                 
    FLAG_SET_ERGO(ProfiledCodeHeapSize, 0);                                                                                                                                                   
    FLAG_SET_ERGO(NonProfiledCodeHeapSize, 0);                                                                                                                                                
    ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize);                                                                                                                        
    add_heap(rs, "CodeCache", CodeBlobType::All);                                                                                                                                             
  }

The code that swallows the warning is here:
static JVMFlag::Error apply_constraint_and_check_range_size_t(const JVMFlag* flag, size_t new_value, bool verbose) {
  JVMFlag::Error status = JVMFlag::SUCCESS;
  JVMFlagRange* range = JVMFlagRangeList::find(flag);
  if (range != NULL) {
    status = range->check_size_t(new_value, verbose);
  }
  if (status == JVMFlag::SUCCESS) {
    JVMFlagConstraint* constraint = JVMFlagConstraintList::find_if_needs_check(flag);
    if (constraint != NULL) {
      status = constraint->apply_size_t(new_value, verbose);
    }
  }
  return status;
}


JVMFlag::Error JVMFlag::size_tAtPut(JVMFlag* flag, size_t* value, JVMFlag::Flags origin) {
  if (flag == NULL) return JVMFlag::INVALID_FLAG;
  if (!flag->is_size_t()) return JVMFlag::WRONG_FORMAT;
  JVMFlag::Error check = apply_constraint_and_check_range_size_t(flag, *value, !JVMFlagConstraintList::validated_after_ergo());
  if (check != JVMFlag::SUCCESS) return check;
  size_t old_value = flag->get_size_t();
  trace_flag_changed<EventUnsignedLongFlagChanged, u8>(flag, old_value, *value, origin);
  check = flag->set_size_t(*value);
  *value = old_value;
  flag->set_origin(origin);
  return check;
}

!JVMFlagConstraintList::validated_after_ergo() starts to return false in the middle of the JVM initialization. This value is passed to the verbose parameter, and check_size_t(...) calls printError(...) with verbose set to false

void JVMFlag::printError(bool verbose, const char* msg, ...) {
  if (verbose) {
    va_list listPointer;
    va_start(listPointer, msg);
    jio_vfprintf(defaultStream::error_stream(), msg, listPointer);
    va_end(listPointer);
  }
}

Since the check returned an error size_tAtPut returns at this line:
  if (check != JVMFlag::SUCCESS) return check;

and the setting of the flag is silently ignored.

If found one problematic instance of this, maybe there are more?
Comments
Changeset: 573c316c Author: Ioi Lam <iklam@openjdk.org> Date: 2023-02-16 03:44:48 +0000 URL: https://git.openjdk.org/jdk/commit/573c316c5764ccd8d483f1f187fd6eb21ceeea63
16-02-2023

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/12549 Date: 2023-02-14 04:03:36 +0000
14-02-2023

Tentative proposal: Change JVMFlag::printError from: void JVMFlag::printError(bool verbose, const char* msg, ...) { if (verbose) { va_list listPointer; va_start(listPointer, msg); jio_vfprintf(defaultStream::error_stream(), msg, listPointer); va_end(listPointer); } } to: void JVMFlag::printError(JVMFlag::Flags origin, const char* msg, ...) { if (JVMFlagLimit::_validating_phase == JVMFlagConstraintPhase::AtParse) { // This is the stage where the VM is parsing options from the command-line, // config files, etc. Print warnings message to alert the user. va_list listPointer; va_start(listPointer, msg); jio_vfprintf(defaultStream::error_stream(), msg, listPointer); va_end(listPointer); } else { log_info(arguments)(..msg.....); switch (origin) { case JVMFlag::DEFAULT: case JVMFlag::COMMAND_LINE: case JVMFlag::ENVIRON_VAR: case JVMFlag::CONFIG_FILE: case JVMFlag::JIMAGE_RESOURCE: assert(0, "origin %d should not be used after AtParse phase"); break; JVMFlag::MANAGEMENT: JVMFlag::ATTACH_ON_DEMAND: JVMFlag::INTERNAL: // whitebox // The caller should handle the error. break; JVMFlag::ERGONOMIC: // This is called by FLAG_SET_ERGO(). The caller typically does not // check for error result. Let's make sure this never happens. fatal("FLAG_SET_ERGO cannot be used to set an invalid value"); break; default: ShouldNotReachHere(); } } }
12-10-2020

If we print error messages for the programmatic errors, like: $ java -Xint -version uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ] java version "16-internal" 2021-03-16 .... That would be confusing to the user who didn't specify the NonNMethodCodeHeapSize option at all. If our intention is to catch errors in the code , maybe we should generate a fatal error (so we get an hs_err file) to tell us exactly where the problem is? void JVMFlag::printError(bool verbose, const char* msg, ...) { va_list listPointer; va_start(listPointer, msg); if (verbose) { jio_vfprintf(defaultStream::error_stream(), msg, listPointer); } else { stringStream ss; ss.vprint(msg, listPointer); fatal("%s", ss.as_string(true)); } va_end(listPointer); } $ java -Xint # To suppress the following error report, specify this argument # after -XX: or in .hotspotrc: SuppressErrorAt=/jvmFlag.cpp:749 # # A fatal error has been detected by the Java Runtime Environment: # # Internal Error (/jdk2/fro/open/src/hotspot/share/runtime/flags/jvmFlag.cpp:749), pid=3549, tid=3550 # fatal error: uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ] # # JRE version: (16.0) (slowdebug build ) # Java VM: Java HotSpot(TM) 64-Bit Server VM (slowdebug 16-internal+0-adhoc.iklam.open, interpreted mode, sharing, compressed oops, g1 gc, linux-amd64) # Problematic frame: # V [libjvm.so+0xb1f712] JVMFlag::printError(bool, char const*, ...)+0xf4 # # Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport %p %s %c %d %P" (or dumping to /jdk2/fro/open/src/hotspot/core.3549) # # An error report file with more information is saved as: # /jdk2/fro/open/src/hotspot/hs_err_pid3549.log V [libjvm.so+0xb1f712] JVMFlag::printError(bool, char const*, ...)+0xf4 V [libjvm.so+0xb2819b] VMPageSizeConstraintFunc(unsigned long, bool)+0x78 V [libjvm.so+0xb21e1a] FlagAccessImpl_uintx::typed_check_constraint(void*, unsigned long, bool) const+0x2c V [libjvm.so+0xb25a4e] TypedFlagAccessImpl<unsigned long, 4, EventUnsignedLongFlagChanged>::check_constraint_and_set(JVMFlag*, void*, JVMFlag::Flags, bool) const+0x9c V [libjvm.so+0xb24ba5] RangedFlagAccessImpl<unsigned long, 4, EventUnsignedLongFlagChanged>::set_impl(JVMFlag*, void*, JVMFlag::Flags) const+0xfb V [libjvm.so+0xb219d6] FlagAccessImpl::set(JVMFlag*, void*, JVMFlag::Flags) const+0x32 V [libjvm.so+0xb1fb6d] JVMFlagAccess::set_impl(JVMFlag*, int, void*, JVMFlag::Flags)+0x87 V [libjvm.so+0xb1fd24] JVMFlagAccess::set_impl(JVMFlagsEnum, int, void*, JVMFlag::Flags)+0xaa V [libjvm.so+0x45eedc] JVMFlag::Error JVMFlagAccess::set<unsigned long, 4>(JVMFlagsEnum, unsigned long, JVMFlag::Flags)+0x28 V [libjvm.so+0x6c752a] Flag_NonNMethodCodeHeapSize_set(unsigned long, JVMFlag::Flags)+0x23 V [libjvm.so+0x6c4644] CodeCache::initialize()+0x13c V [libjvm.so+0x6c46fe] codeCache_init()+0x9 V [libjvm.so+0x9be02b] init_globals()+0x26 V [libjvm.so+0x10b88e5] Threads::create_vm(JavaVMInitArgs*, bool*)+0x36b V [libjvm.so+0xac6823] JNI_CreateJavaVM_inner(JavaVM_**, void**, void*)+0xda V [libjvm.so+0xac6b2b] JNI_CreateJavaVM+0x32 C [libjli.so+0x371a] JavaMain+0x8a C [libjli.so+0x7b09] ThreadJavaMain+0x9
29-09-2020

ILW = MML = P4
04-06-2019