Skip to content

Linux madvise() advice values incorrectly combined via bitwise OR in GCToOSInterface::VirtualReset #126950

@BenV

Description

@BenV

Description

As part of our (@neiljohari and @andyblox) investigation into GC heap corruption we discovered that VirtualReset in gcenv.unix.cpp combines MADV_FREE and MADV_DONTDUMP via bitwise OR and passes the result to a single madvise() call. madvise() takes a single advice constant, not a bitmask. The combined value is either rejected or misinterpreted by the kernel.

This change was made here: #95643 in an effort to reduce the number of
syscalls (#87173)

Reproduction Steps

The bug is in this code path:

// gcenv.unix.cpp — GCToOSInterface::VirtualReset

int madviseFlags = 0;

#ifdef MADV_DONTDUMP
    madviseFlags |= MADV_DONTDUMP;   // 16
#endif

#ifdef HAVE_MADV_FREE
    madviseFlags |= MADV_FREE;       // 8
#endif

st = madvise(address, size, madviseFlags);  // passes 24

When both are defined (standard on modern glibc/x86_64 Linux), madviseFlags = 16 | 8 = 24.

From asm-generic/mman-common.h:

Constant Value
MADV_FREE 8
MADV_DONTDUMP 16
MADV_DONTNEED_LOCKED 24

Note that VirtualReserveInner in the same file correctly uses a standalone madvise(pRetVal, size, MADV_DONTDUMP) call.

Expected behavior

VirtualReset should apply MADV_FREE and MADV_DONTDUMP independently via separate madvise() calls.

Actual behavior

The combined value 24 is passed as a single advice:

  • Kernel < 5.18: 24 is unrecognized, madvise() returns EINVAL. Neither advice takes effect. (We were able to confirm this with Ubuntu 22 + Kernel 5.15)
  • Kernel >= 5.18: 24 matches MADV_DONTNEED_LOCKED, which immediately discards pages (including locked pages) — different semantics from both MADV_FREE and MADV_DONTDUMP.

This also has the side effect of dropping MADV_DONTDUMP which will lead to extra pages being included in dumps.

Regression?

The change was introduced with #95643

Known Workarounds

No response

Configuration

Other information

The VirtualReset madvise calls should likely be reverted to the behavior in the previous version where two individual calls are made.

Metadata

Metadata

Labels

area-GC-coreclros-linuxLinux OS (any supported distro)untriagedNew issue has not been triaged by the area owner

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions