JDK-6843484 : os::commit_memory() failures are not handled properly on linux
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 7
  • Priority: P3
  • Status: Resolved
  • Resolution: Duplicate
  • OS: linux_2.6
  • CPU: generic
  • Submitted: 2009-05-20
  • Updated: 2013-08-12
  • Resolved: 2013-07-23
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.
Other
hs25Resolved
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
A failed commit on linux can unreserve previously reserved address range.  This
can allow another thread to reserve that address range.

Comments
This bug is a duplicate of JDK-8013057.
23-07-2013

At this point, I think I've gone through all of the changes from the "SUGGESTED FIX" note above and I believe all the changes are accounted for by references to bugs that are already fixed or 8013057 which is about to be fixed. I need to check in with Jon M to make sure.
07-06-2013

Here are some notes on the proposed psVirtualspace.cpp changes from the "SUGGESTED FIX" note above: > --- hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp (revision 1013)+++ hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp (working copy) > @@ -96,6 +96,17 @@ > if (result) { > _committed_high_addr += bytes; > } > +#if defined(LINUX) > + else { > + if (!special()) { > + // commit_memory() was called and failed, we might lose "reserved" mapping on linux > + if (os::reserve_memory(bytes, base_addr, alignment()) == NULL) { > + vm_exit_out_of_memory(bytes, > + "reserve_memory() back after commit_memory() failure in PSVirtualSpace::expand_by()"); > + } > + } > + } > +#endif > > if (pre_touch || AlwaysPreTouch) { > for (char* curr = base_addr; > @@ -165,6 +176,15 @@ > _committed_high_addr += tmp_bytes; > bytes_needed -= tmp_bytes; > } else { > +#if defined(LINUX) > + if (!other_space->special()) { > + // commit_memory() was called and failed, we might lose "reserved" mapping on linux > + if (os::reserve_memory(tmp_bytes, commit_base, alignment()) == NULL) { > + vm_exit_out_of_memory(tmp_bytes, > + "reserve_memory() back after commit_memory() failure in PSVirtualSpace::expand_into()"); > + } > + } > +#endif > return bytes - bytes_needed; > } > } > @@ -273,6 +293,17 @@ > if (result) { > _committed_low_addr -= bytes; > } > +#if defined(LINUX) > + else { > + if (!special()) { > + // commit_memory() was called and failed, we might lose "reserved" mapping on linux > + if (os::reserve_memory(bytes, base_addr, alignment()) == NULL) { > + vm_exit_out_of_memory(bytes, > + "reserve_memory() back after commit_memory() failure in PSVirtualSpaceHighToLow::expand_by()"); > + } > + } > + } > +#endif > > if (pre_touch || AlwaysPreTouch) { > for (char* curr = base_addr; > @@ -342,6 +373,15 @@ > _committed_low_addr -= tmp_bytes; > bytes_needed -= tmp_bytes; > } else { > +#if defined(LINUX) > + if (!other_space->special()) { > + // commit_memory() was called and failed, we might lose "reserved" mapping on linux > + if (os::reserve_memory(tmp_bytes, commit_base, alignment()) == NULL) { > + vm_exit_out_of_memory(tmp_bytes, > + "reserve_memory() back after commit_memory() failure in PSVirtualSpaceHighToLow::expand_into()"); > + } > + } > +#endif > return bytes - bytes_needed; > } > } The fix for 8013057 adds vm_exit_out_of_memory() calls down in the Linux (and Solaris) specific implementation layer for os::commit_memory(). The above attempt to call os::reserve_memory() to reestablish the memory reservation is racey and does not guarantee that the reservation has not been lost to another thread.
07-06-2013

Here are some notes on the proposed virtualspace.cpp changes from the "SUGGESTED FIX" note above: > --- hotspot/src/share/vm/runtime/virtualspace.cpp (revision 1013) > +++ hotspot/src/share/vm/runtime/virtualspace.cpp (working copy) > @@ -535,6 +535,13 @@ > lower_high() + lower_needs <= lower_high_boundary(), > "must not expand beyond region"); > if (!os::commit_memory(lower_high(), lower_needs)) { > +#if defined(LINUX) > + // we could lose "reserved" mapping on linux since commit_memory() failed > + if (os::reserve_memory(lower_needs, lower_high()) == NULL) { > + vm_exit_out_of_memory(lower_needs, > + "reserve_memory() back after lower commit_memory() failure in VirtualSpace::expand_by()"); > + } > +#endif > debug_only(warning("os::commit_memory failed")); > return false; > } else { > @@ -546,6 +553,13 @@ > middle_high() + middle_needs <= middle_high_boundary(), > "must not expand beyond region"); > if (!os::commit_memory(middle_high(), middle_needs, middle_alignment())) { > +#if defined(LINUX) > + // we could lose "reserved" mapping on linux since commit_memory() failed > + if (os::reserve_memory(middle_needs, middle_high(), middle_alignment()) == NULL) { > + vm_exit_out_of_memory(middle_needs, > + "reserve_memory() back after middle commit_memory() failure in VirtualSpace::expand_by()"); > + } > +#endif > debug_only(warning("os::commit_memory failed")); > return false; > } > @@ -556,6 +570,13 @@ > upper_high() + upper_needs <= upper_high_boundary(), > "must not expand beyond region"); > if (!os::commit_memory(upper_high(), upper_needs)) { > +#if defined(LINUX) > + // we could lose "reserved" mapping on linux since commit_memory() failed > + if (os::reserve_memory(upper_needs, upper_high()) == NULL) { > + vm_exit_out_of_memory(upper_needs, > + "reserve_memory() back after upper commit_memory() failure in VirtualSpace::expand_by()"); > + } > +#endif > debug_only(warning("os::commit_memory failed")); > return false; > } else { The fix for 8013057 adds vm_exit_out_of_memory() calls down in the Linux (and Solaris) specific implementation layer for os::commit_memory(). The above attempt to call os::reserve_memory() to reestablish the memory reservation is racey and does not guarantee that the reservation has not been lost to another thread.
07-06-2013

Another note, the original note from Intel says: > 3) The above mmap() call fails with ENOMEM error (not enough memory). Further, > 'man mmap' says: "If mmap() fails for reasons other than [EBADF], [EINVAL], or > [ENOTSUP], some of the mappings in the address range starting at addr and > continuing for len bytes may have been unmapped.". and continues on to state: > At this point it can be seen via 'cat /proc/PID/maps' that there is a gap in > reserved in (1) address range now. The mmap() man page words above are not on the Linux mmap(2) man page as of 2009-09-26 (the date on the man page). However, these words are present: MAP_FIXED Don���t interpret addr as a hint: place the mapping at exactly that address. addr must be a multiple of the page size. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail. Because requiring a fixed address for a mapping is less portable, the use of this option is dis- couraged. In particular, I read this sentence: If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. as stating that when switching from a MAP_NORESERVE mapping to a (~MAP_NORESERVE && MAP_FIXED) mapping, we can lose the original mapping.
28-05-2013

JDK-6541756 and JDK-8012015 fixed a couple of the issues in this bug. JDK-8013057 will likely fix the rest (but in a different way than proposed).
28-05-2013

Here are some notes on the proposed os_linux.cpp changes from the "SUGGESTED FIX" note above: > --- hotspot/src/os/linux/vm/os_linux.cpp (revision 1013) > +++ hotspot/src/os/linux/vm/os_linux.cpp (working copy) > @@ -2252,7 +2252,7 @@ > > bool os::uncommit_memory(char* addr, size_t size) { > return ::mmap(addr, size, > - PROT_READ|PROT_WRITE|PROT_EXEC, > + PROT_NONE, > MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE|MAP_ANONYMOUS, -1, 0) > != MAP_FAILED; > } The above change to os::uncommit_memory() was done via the following changeset: 6541756: Reduce executable C-heap author coleenp Wed Mar 25 14:19:20 2009 -0400 (4 years ago) changeset 652 6bdd6923ba16 parent 651 60bfce711da4 child 654 fe62b51b93f4 6541756: Reduce executable C-heap Summary: Add executable parameters to reserve_memory and commit_memory to reduce executable memory to only the Code Heap. Reviewed-by: xlu, kvn, acorn > @@ -2275,7 +2275,7 @@ > flags |= MAP_FIXED; > } > > - addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE|PROT_EXEC, > + addr = (char*)::mmap(requested_addr, bytes, PROT_NONE, > flags, -1, 0); > > if (addr != MAP_FAILED) { The above change to os::anon_mmap() was done via the following changeset: 8012015: Use PROT_NONE when reserving memory author mikael Mon Apr 29 11:03:49 2013 -0700 (3 weeks ago) changeset 4553 f32b6c267d2e parent 4552 c53e49efe6a8 child 4555 409d4b59e095 8012015: Use PROT_NONE when reserving memory Summary: Reserved memory had PROT_READ+PROT_WRITE access on Linux/bsd, now changed to PROT_NONE. Reviewed-by: dholmes, ctornqvi > @@ -3509,7 +3509,7 @@ > > if (!UseMembar) { > address mem_serialize_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); > - guarantee( mem_serialize_page != NULL, "mmap Failed for memory serialize page"); > + guarantee( mem_serialize_page != MAP_FAILED, "mmap Failed for memory serialize page"); > os::set_memory_serialize_page( mem_serialize_page ); > > #ifndef PRODUCT The above fix has not yet been made.
23-05-2013

SUGGESTED FIX --- hotspot/src/os/linux/vm/os_linux.cpp (revision 1013) +++ hotspot/src/os/linux/vm/os_linux.cpp (working copy) @@ -2252,7 +2252,7 @@ bool os::uncommit_memory(char* addr, size_t size) { return ::mmap(addr, size, - PROT_READ|PROT_WRITE|PROT_EXEC, + PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE|MAP_ANONYMOUS, -1, 0) != MAP_FAILED; } @@ -2275,7 +2275,7 @@ flags |= MAP_FIXED; } - addr = (char*)::mmap(requested_addr, bytes, PROT_READ|PROT_WRITE|PROT_EXEC, + addr = (char*)::mmap(requested_addr, bytes, PROT_NONE, flags, -1, 0); if (addr != MAP_FAILED) { @@ -3509,7 +3509,7 @@ if (!UseMembar) { address mem_serialize_page = (address) ::mmap(NULL, Linux::page_size(), PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - guarantee( mem_serialize_page != NULL, "mmap Failed for memory serialize page"); + guarantee( mem_serialize_page != MAP_FAILED, "mmap Failed for memory serialize page"); os::set_memory_serialize_page( mem_serialize_page ); #ifndef PRODUCT Index: hotspot/src/share/vm/runtime/virtualspace.cpp =================================================================== --- hotspot/src/share/vm/runtime/virtualspace.cpp (revision 1013) +++ hotspot/src/share/vm/runtime/virtualspace.cpp (working copy) @@ -535,6 +535,13 @@ lower_high() + lower_needs <= lower_high_boundary(), "must not expand beyond region"); if (!os::commit_memory(lower_high(), lower_needs)) { +#if defined(LINUX) + // we could lose "reserved" mapping on linux since commit_memory() failed + if (os::reserve_memory(lower_needs, lower_high()) == NULL) { + vm_exit_out_of_memory(lower_needs, + "reserve_memory() back after lower commit_memory() failure in VirtualSpace::expand_by()"); + } +#endif debug_only(warning("os::commit_memory failed")); return false; } else { @@ -546,6 +553,13 @@ middle_high() + middle_needs <= middle_high_boundary(), "must not expand beyond region"); if (!os::commit_memory(middle_high(), middle_needs, middle_alignment())) { +#if defined(LINUX) + // we could lose "reserved" mapping on linux since commit_memory() failed + if (os::reserve_memory(middle_needs, middle_high(), middle_alignment()) == NULL) { + vm_exit_out_of_memory(middle_needs, + "reserve_memory() back after middle commit_memory() failure in VirtualSpace::expand_by()"); + } +#endif debug_only(warning("os::commit_memory failed")); return false; } @@ -556,6 +570,13 @@ upper_high() + upper_needs <= upper_high_boundary(), "must not expand beyond region"); if (!os::commit_memory(upper_high(), upper_needs)) { +#if defined(LINUX) + // we could lose "reserved" mapping on linux since commit_memory() failed + if (os::reserve_memory(upper_needs, upper_high()) == NULL) { + vm_exit_out_of_memory(upper_needs, + "reserve_memory() back after upper commit_memory() failure in VirtualSpace::expand_by()"); + } +#endif debug_only(warning("os::commit_memory failed")); return false; } else { Index: hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp =================================================================== --- hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp (revision 1013)+++ hotspot/src/share/vm/gc_implementation/parallelScavenge/psVirtualspace.cpp (working copy) @@ -96,6 +96,17 @@ if (result) { _committed_high_addr += bytes; } +#if defined(LINUX) + else { + if (!special()) { + // commit_memory() was called and failed, we might lose "reserved" mapping on linux + if (os::reserve_memory(bytes, base_addr, alignment()) == NULL) { + vm_exit_out_of_memory(bytes, + "reserve_memory() back after commit_memory() failure in PSVirtualSpace::expand_by()"); + } + } + } +#endif if (pre_touch || AlwaysPreTouch) { for (char* curr = base_addr; @@ -165,6 +176,15 @@ _committed_high_addr += tmp_bytes; bytes_needed -= tmp_bytes; } else { +#if defined(LINUX) + if (!other_space->special()) { + // commit_memory() was called and failed, we might lose "reserved" mapping on linux + if (os::reserve_memory(tmp_bytes, commit_base, alignment()) == NULL) { + vm_exit_out_of_memory(tmp_bytes, + "reserve_memory() back after commit_memory() failure in PSVirtualSpace::expand_into()"); + } + } +#endif return bytes - bytes_needed; } } @@ -273,6 +293,17 @@ if (result) { _committed_low_addr -= bytes; } +#if defined(LINUX) + else { + if (!special()) { + // commit_memory() was called and failed, we might lose "reserved" mapping on linux + if (os::reserve_memory(bytes, base_addr, alignment()) == NULL) { + vm_exit_out_of_memory(bytes, + "reserve_memory() back after commit_memory() failure in PSVirtualSpaceHighToLow::expand_by()"); + } + } + } +#endif if (pre_touch || AlwaysPreTouch) { for (char* curr = base_addr; @@ -342,6 +373,15 @@ _committed_low_addr -= tmp_bytes; bytes_needed -= tmp_bytes; } else { +#if defined(LINUX) + if (!other_space->special()) { + // commit_memory() was called and failed, we might lose "reserved" mapping on linux + if (os::reserve_memory(tmp_bytes, commit_base, alignment()) == NULL) { + vm_exit_out_of_memory(tmp_bytes, + "reserve_memory() back after commit_memory() failure in PSVirtualSpaceHighToLow::expand_into()"); + } + } +#endif return bytes - bytes_needed; } }
20-05-2009