Bug Description
PyJObject_New calls NewGlobalRef to create global references for both the Java object and its class, but never checks the return value for NULL. Per JNI specification, NewGlobalRef returns NULL when the global reference table is exhausted or native memory allocation fails. The NULL is silently stored in the struct, causing deferred crashes on any subsequent JNI operation that uses the wrapper.
Location
src/main/c/Objects/pyjobject.c, lines ~51-63
Code
PyObject* PyJObject_New(JNIEnv *env, PyTypeObject* type, jobject obj,
jclass class)
{
PyJObject *pyjob = (PyJObject*) type->tp_alloc(type, 0);
if (obj) {
pyjob->object = (*env)->NewGlobalRef(env, obj);
// No NULL check — if global ref table is full, pyjob->object = NULL
} else {
pyjob->object = NULL;
}
if (class) {
pyjob->clazz = (*env)->NewGlobalRef(env, class);
// No NULL check — same issue
} else {
class = (*env)->GetObjectClass(env, obj);
pyjob->clazz = (*env)->NewGlobalRef(env, class);
// No NULL check — same issue
(*env)->DeleteLocalRef(env, class);
class = NULL;
}
return (PyObject*) pyjob;
}
Context
PyJObject_New is the primary factory function for all Java objects exposed to Python. Every jep.java_import, attribute access that returns a Java object, and method call that returns a Java object goes through this function. When NewGlobalRef fails, the NULL stored in pyjob->object or pyjob->clazz will cause a crash on any subsequent operation — method calls, field access, type checks, toString, etc.
The crash happens far from the actual failure (global ref table exhaustion), making it extremely difficult to diagnose.
Impact
In long-running applications or those that create many Java wrappers (e.g., iterating over large Java collections from Python), the JNI global reference table can fill up. When it does, every new wrapper silently stores NULL and any use of that wrapper crashes the JVM.
Suggested Fix
if (obj) {
pyjob->object = (*env)->NewGlobalRef(env, obj);
if (pyjob->object == NULL) {
Py_DECREF(pyjob);
return NULL; // JVM has already thrown OutOfMemoryError
}
}
Same pattern for pyjob->clazz.
Environment
- Jep: current master branch (commit 247887d)
- Affected: all Java-to-Python object wrapping under global ref table pressure
Bug Description
PyJObject_NewcallsNewGlobalRefto create global references for both the Java object and its class, but never checks the return value for NULL. Per JNI specification,NewGlobalRefreturns NULL when the global reference table is exhausted or native memory allocation fails. The NULL is silently stored in the struct, causing deferred crashes on any subsequent JNI operation that uses the wrapper.Location
src/main/c/Objects/pyjobject.c, lines ~51-63Code
Context
PyJObject_Newis the primary factory function for all Java objects exposed to Python. Everyjep.java_import, attribute access that returns a Java object, and method call that returns a Java object goes through this function. WhenNewGlobalReffails, the NULL stored inpyjob->objectorpyjob->clazzwill cause a crash on any subsequent operation — method calls, field access, type checks,toString, etc.The crash happens far from the actual failure (global ref table exhaustion), making it extremely difficult to diagnose.
Impact
In long-running applications or those that create many Java wrappers (e.g., iterating over large Java collections from Python), the JNI global reference table can fill up. When it does, every new wrapper silently stores NULL and any use of that wrapper crashes the JVM.
Suggested Fix
Same pattern for
pyjob->clazz.Environment