-
Bug
-
Resolution: Fixed
-
P4
-
17, 21, 22
-
b27
-
generic
GetClassFields JVMTI function is supposed to filter out some class fields which are not real java object to avoid crashes accessing it (like reflection did).
Currently the only such field is constantPoolOop from jdk.internal.reflect.ConstantPool class.
Reproducer:
FilteredFieldsTest.java:
====================================================
import java.lang.reflect.Field;
public class FilteredFieldsTest {
static {
System.loadLibrary("FilteredFieldsTest");
}
private native static int getJVMTIFieldCount(Class cls);
private static int getDeclaredFieldsCount(Class cls) {
Field[] declaredFields = cls.getDeclaredFields();
System.out.println("Class.getDeclaredFields reported " + declaredFields.length + " fields:");
for (int i = 0; i < declaredFields.length; i++) {
System.out.println(" [" + i + "] : " + declaredFields[i]);
}
return declaredFields.length;
}
public static void main(String args[]) throws Exception {
Class cls = Class.forName("jdk.internal.reflect.ConstantPool");
int declaredCount = getDeclaredFieldsCount(cls);
int jvmtiCount = getJVMTIFieldCount(cls);
if (declaredCount != jvmtiCount) {
throw new Exception("declaredCount != jvmtiCount: " + declaredCount + " != " + jvmtiCount);
}
}
}
====================================================
FilteredFieldsTest.cpp:
====================================================
#include <stdio.h>
#include <string.h>
#include "jvmti.h"
extern "C" {
static jvmtiEnv *jvmti = NULL;
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
if (res != JNI_OK || jvmti == NULL) {
printf("Wrong result of a valid call to GetEnv!\n");
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
Java_FilteredFieldsTest_getJVMTIFieldCount(JNIEnv *env, jclass cls, jclass clazz) {
if (jvmti == NULL) {
printf("JVMTI agent was not properly loaded\n");
return -1;
}
jint fcount = 0;
jfieldID *fields = nullptr;
jvmtiError err = jvmti->GetClassFields(clazz, &fcount, &fields);
if (err != JVMTI_ERROR_NONE) {
printf("GetClassFields returned error: %d\n", err);
return -1;
}
printf("GetClassFields returned %d fields:\n", (int)fcount);
for (int i = 0; i < fcount; i++) {
char *name;
err = jvmti->GetFieldName(clazz, fields[i], &name, nullptr, nullptr);
if (err != JVMTI_ERROR_NONE) {
printf("GetFieldName(%d) returned error: %d\n", i, err);
continue;
}
printf(" [%d]: %s\n", i, name);
jvmti->Deallocate((unsigned char *)name);
}
return fcount;
}
}
====================================================
The test fails with java.lang.Exception: declaredCount != jvmtiCount: 0 != 1
Output:
Class.getDeclaredFields reported 0 fields:
GetClassFields returned 1 fields:
[0]: constantPoolOop
Currently the only such field is constantPoolOop from jdk.internal.reflect.ConstantPool class.
Reproducer:
FilteredFieldsTest.java:
====================================================
import java.lang.reflect.Field;
public class FilteredFieldsTest {
static {
System.loadLibrary("FilteredFieldsTest");
}
private native static int getJVMTIFieldCount(Class cls);
private static int getDeclaredFieldsCount(Class cls) {
Field[] declaredFields = cls.getDeclaredFields();
System.out.println("Class.getDeclaredFields reported " + declaredFields.length + " fields:");
for (int i = 0; i < declaredFields.length; i++) {
System.out.println(" [" + i + "] : " + declaredFields[i]);
}
return declaredFields.length;
}
public static void main(String args[]) throws Exception {
Class cls = Class.forName("jdk.internal.reflect.ConstantPool");
int declaredCount = getDeclaredFieldsCount(cls);
int jvmtiCount = getJVMTIFieldCount(cls);
if (declaredCount != jvmtiCount) {
throw new Exception("declaredCount != jvmtiCount: " + declaredCount + " != " + jvmtiCount);
}
}
}
====================================================
FilteredFieldsTest.cpp:
====================================================
#include <stdio.h>
#include <string.h>
#include "jvmti.h"
extern "C" {
static jvmtiEnv *jvmti = NULL;
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res = jvm->GetEnv((void **)&jvmti, JVMTI_VERSION_1_1);
if (res != JNI_OK || jvmti == NULL) {
printf("Wrong result of a valid call to GetEnv!\n");
return JNI_ERR;
}
return JNI_OK;
}
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
Java_FilteredFieldsTest_getJVMTIFieldCount(JNIEnv *env, jclass cls, jclass clazz) {
if (jvmti == NULL) {
printf("JVMTI agent was not properly loaded\n");
return -1;
}
jint fcount = 0;
jfieldID *fields = nullptr;
jvmtiError err = jvmti->GetClassFields(clazz, &fcount, &fields);
if (err != JVMTI_ERROR_NONE) {
printf("GetClassFields returned error: %d\n", err);
return -1;
}
printf("GetClassFields returned %d fields:\n", (int)fcount);
for (int i = 0; i < fcount; i++) {
char *name;
err = jvmti->GetFieldName(clazz, fields[i], &name, nullptr, nullptr);
if (err != JVMTI_ERROR_NONE) {
printf("GetFieldName(%d) returned error: %d\n", i, err);
continue;
}
printf(" [%d]: %s\n", i, name);
jvmti->Deallocate((unsigned char *)name);
}
return fcount;
}
}
====================================================
The test fails with java.lang.Exception: declaredCount != jvmtiCount: 0 != 1
Output:
Class.getDeclaredFields reported 0 fields:
GetClassFields returned 1 fields:
[0]: constantPoolOop