Skip to content

Commit 8ea9e7d

Browse files
committed
Avoid double-free when calling tp_deallocs during shutdown that may cause GC
1 parent 88b5bbb commit 8ea9e7d

File tree

1 file changed

+20
-8
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions

1 file changed

+20
-8
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/CApiTransitions.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -498,18 +498,30 @@ public static int pollReferenceQueue() {
498498
} else {
499499
assert nativeLookupGet(handleContext, reference.pointer) != null : Long.toHexString(reference.pointer);
500500
LOGGER.finer(() -> PythonUtils.formatJString("releasing native stub lookup for managed object with replacement %x => %s", reference.pointer, reference));
501-
nativeLookupRemove(handleContext, reference.pointer);
502-
if (reference.isAllocatedFromJava()) {
503-
LOGGER.finer(() -> PythonUtils.formatJString("freeing managed object %s replacement", reference));
504-
freeNativeStruct(reference);
501+
if (nativeLookupRemove(handleContext, reference.pointer) != null) {
502+
// The reference was still in our lookup table, it was not otherwise
503+
// freed and we can process it now.
504+
if (reference.isAllocatedFromJava()) {
505+
LOGGER.finer(() -> PythonUtils.formatJString("freeing managed object %s replacement", reference));
506+
freeNativeStruct(reference);
507+
} else {
508+
referencesToBeFreed.add(reference.pointer);
509+
}
505510
} else {
506-
referencesToBeFreed.add(reference.pointer);
511+
// This handle was removed from the native lookup table before,
512+
// probably during an explicit collection. This can happen during
513+
// shutdown when tp_dealloc is called for some objects and that
514+
// causes upcalls and reference queue polling on references that
515+
// were already removed and had their memory freed
507516
}
508517
}
509518
} else if (entry instanceof NativeObjectReference reference) {
510-
LOGGER.finer(() -> PythonUtils.formatJString("releasing native lookup for native object %x => %s", reference.pointer, reference));
511-
nativeLookupRemove(handleContext, reference.pointer);
512-
processNativeObjectReference(reference, referencesToBeFreed);
519+
if (nativeLookupRemove(handleContext, reference.pointer) != null) {
520+
// The reference was still in our lookup table, it was not otherwise
521+
// freed and we can process it now
522+
LOGGER.finer(() -> PythonUtils.formatJString("releasing native lookup for native object %x => %s", reference.pointer, reference));
523+
processNativeObjectReference(reference, referencesToBeFreed);
524+
}
513525
} else if (entry instanceof NativeStorageReference reference) {
514526
handleContext.nativeStorageReferences.remove(reference);
515527
processNativeStorageReference(reference);

0 commit comments

Comments
 (0)