JDK-7178846 : IterateThroughHeap: heap_iteration_callback passes a negative size for big array
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 7
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_7
  • CPU: x86
  • Submitted: 2012-06-21
  • Updated: 2013-06-26
  • Resolved: 2012-07-03
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 7 JDK 8 Other
7u40Fixed 8Fixed hs24Fixed
Description
FULL PRODUCT VERSION :


A DESCRIPTION OF THE PROBLEM :
If there is an array of size close to 2 GB, its size is reported as a negative value to the heap_iteration_callback in IterateThroughHeap

THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: Did not try

THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Did not try

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Compile a Java class NegativeArraySize (see source code) which creates a big char[] and calls a native method to trigger IterateThroughHeap

2. Compile a C++ agent as a 64-bit dll to get agent.dll

3. Run the test:

<path to 64-bit Java 7 or Java 6>/java  -Xmx4G -agentlib:agent -cp . NegativeArraySize

An object with negative size will be reported.


EXPECTED VERSUS ACTUAL BEHAVIOR :
Actual:

"negative size: array_length=1074790398"

Expected:

a negative size is never reported

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
===========================================
The Java class:
===========================================

public class NegativeArraySize {
  static native void iterateHeap();

  static char[] array = new char[1074790398]; // keep from GC

  public static void main(String[] args) {
    iterateHeap();
  }
}

===========================================
The C++ agent:
===========================================
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <jvmti.h>

static jvmtiEnv* ourJVMTI;

#define ZERO(X) memset(&X, 0, sizeof(X))

/* Check for JVMTI error */
void check_JVMTI_error_impl(jvmtiError err, const char* file, int line) {
  if (err != JVMTI_ERROR_NONE) {
    char* name = NULL;
    ourJVMTI->GetErrorName(err, &name);
    fprintf(stderr, "ERROR: JVMTI error err=%d(%s) in %s:%d\n", err, name, file, line);
    fflush(stderr);
    ourJVMTI->Deallocate((unsigned char *)name);
    assert(false);
  }
}

#define CALL_JVMTI(call_expr) check_JVMTI_error_impl(ourJVMTI->call_expr, __FILE__, __LINE__)

jint JNICALL my_callback(jlong class_tag, jlong size, jlong* tag_ptr, jint array_length, void*) {
  if (size < 0) {
    printf("negative size: array_length=%d\n", array_length);
  }

  return JVMTI_VISIT_OBJECTS;
}

extern "C" JNIEXPORT void JNICALL Java_NegativeArraySize_iterateHeap(JNIEnv* jni, jclass) {
  jvmtiHeapCallbacks callbacks;
  ZERO(callbacks);
  callbacks.heap_iteration_callback = &my_callback;

  CALL_JVMTI(
    IterateThroughHeap(JVMTI_HEAP_FILTER_TAGGED, NULL, &callbacks, NULL)
  );
}

extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* /*reserved*/) {
  vm->GetEnv((void **)&ourJVMTI, JVMTI_VERSION);

  assert(ourJVMTI);

  jvmtiCapabilities capabilities;
  ZERO(capabilities);
  capabilities.can_tag_objects = 1;

  CALL_JVMTI(AddCapabilities(&capabilities));

  return 0;
}


---------- END SOURCE ----------

Comments
EVALUATION http://hg.openjdk.java.net/lambda/lambda/hotspot/rev/588f559105c1
29-06-2012

EVALUATION http://hg.openjdk.java.net/hsx/hotspot-rt/hotspot/rev/588f559105c1
25-06-2012

EVALUATION class CallbackWrapper in jvmtiTagMap.cpp has a missing cast which leads to an integer overflow. The code in question is _obj_size = _o->size() * wordSize; _obj_size is a jlong _o->size() is an int wordSize is an int Changing to: _obj_size = (jlong)_o->size() * wordSize; fixes the overflow.
25-06-2012