JDK-8292083 : Detected container memory limit may exceed physical machine memory
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: openjdk8u345,11.0.16,17.0.4,20
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: generic
  • Submitted: 2022-08-09
  • Updated: 2022-12-19
  • Resolved: 2022-08-26
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 11 JDK 17 JDK 20 Other
11.0.18-oracleFixed 17.0.6-oracleFixed 20 b13Fixed openjdk8u372Fixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
The current Hotspot code in osContainer_linux.cpp uses this code snippet in OSContainer::init() to set the physical memory of the VM based on the detected container limits:

  // We need to update the amount of physical memory now that
  // cgroup subsystem files have been processed.
  if ((mem_limit = cgroup_subsystem->memory_limit_in_bytes()) > 0) {
    os::Linux::set_physical_memory(mem_limit);
    log_info(os, container)("Memory Limit is: " JLONG_FORMAT, mem_limit);
  }

If the detection mechanism so happens to find a value in cgroup limit files that is larger than physical memory of the host system the container runs on this will happily proceed, resulting in a broken JVM at risk to getting OOM killed etc.

This seems to be present since the initial JDK-8146115 code done in JDK 10:
https://hg.openjdk.java.net/jdk/jdk/rev/7f22774a5f42#l4.43

We have seen cgroup v1 systems (see trace attachment) that didn't have any cgroups limits in effect, and had this value in /sys/fs/cgroup/memory/memory.limit_in_bytes: 92233720365056. That value exceeded the physical host's memory of 8 GB total. Nevertheless, the cgroups v1 files don't have a unique value to say "max" or unlimited like in cgroups v2. Therefore a contrived "unlimited" value is being used to check if the value is a limit or "unlimited". _unlimited_memory is set for cgroups v1 to '(LONG_MAX / os::vm_page_size()) * os::vm_page_size(), taking on value 9223372036854771712 on some systems. Thus, the limit ends up being 92233720365056 as that's less than 9223372036854771712[1]. Any larger value in memory.limit_in_bytes cgroup interface files that are smaller than (LONG_MAX / os::vm_page_size()) * os::vm_page_size(), but exceeding physical memory would run afoul of this bug.

We should bound the container memory above by the physical host's memory at the very least.

[1] https://github.com/openjdk/jdk/blob/3677b55b45746c3c955a8fcf1fbbf15694baa873/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp#L94
Comments
Fix request (8u) Please consider for 8u cgroups v2 support. It's not clean: context issues, replace the use of some log_debug and log_trace, and an adjustment to Asserts class location for hotspot tests. Thanks!
19-12-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk8u-dev/pull/180 Date: 2022-11-16 11:44:39 +0000
16-11-2022

Fix Request (11u) Same rationale as for 11u. Likewise not clean, same reasons (slightly different unrelated context in src/hotspot/os/linux/os_linux.hpp )
31-08-2022

> Please only add the approval tag once the 17u review has been approved. Oops, sorry!
31-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk11u-dev/pull/1346 Date: 2022-08-27 07:08:28 +0000
27-08-2022

Please only add the approval tag once the 17u review has been approved.
26-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk17u-dev/pull/646 Date: 2022-08-26 13:50:10 +0000
26-08-2022

Fix Request (17u) I wish to backport this to get the bug fix out to JDK17 users as we have seen it in live deployments. The backport is not clean: * unrelated changes in src/hotspot/os/linux/os_linux.hpp * in src /hotspot/os/linux/cgroupV1Subsystem_linux.cpp, the original patch modified CgroupV1Subsystem::kernel_memory_limit_in_bytes, which does not yet exist in jdk17u. It is introduced in 8287011. I don't know whether that will get backported to 17u in future.
26-08-2022

Changeset: f694f8a7 Author: Jonathan Dowland <jdowland@openjdk.org> Committer: Severin Gehwolf <sgehwolf@openjdk.org> Date: 2022-08-26 12:25:16 +0000 URL: https://git.openjdk.org/jdk/commit/f694f8a7671002559e7d23fdb65d5e9c768f9c03
26-08-2022

A pull request was submitted for review. URL: https://git.openjdk.org/jdk/pull/9880 Date: 2022-08-15 14:51:51 +0000
15-08-2022

Nice work!
11-08-2022

src/hotspot/os/linux/os_linux.cpp os::physical_memory() needs a similar fix, too - I've got it working (https://github.com/jmtd/jdk/tree/8292083-cgroups-badmaxmem), I need to: clean this up; double check what flags are affected by the change and make sure they're sensible; and ideally, rework cgtest.sh into a jtreg-style and include it in the patchset.
11-08-2022

I wrote the attached shell script harness for testing purposes (cgtest.sh). The idea is to create a new cgroup with a memory limit set to 2*physical RAM, then ask Java to set InitialRAMPercentage to 25% of available memory. We can check the resulting InitialHeapSize. After the proposed patch its still being set incorrectly. I'm going to look at Java Metrics impl classes now. Script needs a cgroup v1 host, superuser privileges (to create cgroups), JAVA_HOME set (use sudo -E to pass it through) and awk available.
11-08-2022

It looks like the Java Metrics impl classes have a similar problem for cgroups v1. There, the cut-off value seems to be 9223372036837998592 (also larger than observed 92233720365056). public long getMemoryLimit() { long retval = getLongValue(memory, "memory.limit_in_bytes"); if (retval > CgroupV1SubsystemController.UNLIMITED_MIN) { if (memory.isHierarchical()) { // memory.limit_in_bytes returned unlimited, attempt // hierarchical memory limit String match = "hierarchical_memory_limit"; retval = CgroupV1SubsystemController.getLongValueMatchingLine(memory, "memory.stat", match); } } return CgroupV1SubsystemController.longValOrUnlimited(retval); }
09-08-2022

Workarounds on such systems is to use -XX:-UseContainerSupport, use -XX:MaxRAM=<physical_memory_amount> or actually apply a container limit that is smaller than physical memory.
09-08-2022

Attached a proposed patch.
09-08-2022