JDK-8351376 : Potential data race in java.lang.reflect.ProxyGenerator.addProxyMethod related to ProxyGenerator.hashCodeMethod
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 21
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2025-03-07
  • Updated: 2025-03-24
  • Resolved: 2025-03-24
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 23
23Resolved
Related Reports
Duplicate :  
Description
We are seeing data race reported by tsan in java.lang.reflect.ProxyGenerator.addProxyMethod(). I did some analysis of the tsan report. It appears there are two different threads read/write a shared (same) ProxyMethod concurrently, while processing two different annotations. One thread is reading a ProxyMethod.exceptionTypes and the other thread is writing the ProxyMethod.exceptionTypes field from the same object.

ProxyGenerator.generateProxyClass() always creates a new ProxyGenerator instance and ProxyGenerator.proxyMethods map is private to each ProxyGenerator instance. In most cases, the ProxyMethod instances handled by generateProxyClass() are private to the invoking thread. 

[~jcking] noticed there are following static fields in ProxyGenerator class. Those could be accessed from different threads. 

```
    /* Preloaded ProxyMethod objects for methods in java.lang.Object */
    private static final ProxyMethod hashCodeMethod;
    private static final ProxyMethod equalsMethod;
    private static final ProxyMethod toStringMethod;
``` 

It would be good for someone who's familiar with the ProxyGenerator area to take a close look to see if the reported race is benign or needs to be fixed.

Comments
Thanks for looking into this issue and pointing to JDK-8333854 changes in the newer code base, [~liach]. Indeed in the newer code base generateClassFile() creates new ProxyMethod instances for OBJECT_HASH_CODE_METHOD and etc, which avoids the race caused by the shared ProxyMethods. ``` private byte[] generateClassFile() { ... addProxyMethod(new ProxyMethod(OBJECT_HASH_CODE_METHOD, OBJECT_HASH_CODE_SIG, "m0")); addProxyMethod(new ProxyMethod(OBJECT_EQUALS_METHOD, OBJECT_EQUALS_SIG, "m1")); addProxyMethod(new ProxyMethod(OBJECT_TO_STRING_METHOD, OBJECT_TO_STRING_SIG, "m2")); ... ```
24-03-2025

On second check, this is no longer in mainline - a refactor in JDK-8333854 accidentally fixed this, and was backported to 23. This should no longer happen in 23, 24, or 25. To be exact, that patch makes ProxyMethod store the CONSTANT_FieldRef for the Method static final field in the Proxy class to reduce class file generation lookup overhead. This blocked sharing of object methods for ProxyMethod and accidentally fixed this bug by making ProxyMethod truly inner to ProxyGenerator.
24-03-2025

The racy writes should always be writing empty Class[0] arrays to the field. The race thus should be technically benign (as any observed Class array of length 0 is freely interchangeable) but something like a comment or asserts that check initial exceptions array for the 3 fields is empty (which can ensure later merges via collectCompatibleTypes remains empty) can be nice.
24-03-2025

An example stack trace reported by ThreadSanitizer on JDK 21.0.6: WARNING: ThreadSanitizer: data race (pid=3557) Read of size 4 at 0x000082c213f4 by thread T93 (mutexes: write M0, write M1, write M2): #0 java.lang.reflect.ProxyGenerator.addProxyMethod(Ljava/lang/reflect/Method;Ljava/lang/Class;)V ProxyGenerator.java:538 #1 java.lang.reflect.ProxyGenerator.generateClassFile()[B ProxyGenerator.java:478 #2 java.lang.reflect.ProxyGenerator.generateProxyClass(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/util/List;I)[B ProxyGenerator.java:178 #3 java.lang.reflect.Proxy$ProxyBuilder.defineProxyClass(Ljava/lang/reflect/Proxy$ProxyBuilder$ProxyClassContext;Ljava/util/List;)Ljava/lang/Class; Proxy.java:544 #4 java.lang.reflect.Proxy$ProxyBuilder.build()Ljava/lang/reflect/Constructor; Proxy.java:657 #5 java.lang.reflect.Proxy.lambda$getProxyConstructor$0(Ljava/lang/ClassLoader;Ljdk/internal/loader/AbstractClassLoaderValue$Sub;)Ljava/lang/reflect/Constructor; Proxy.java:429 #6 java.lang.reflect.Proxy$$Lambda+0x00000001000dc958.apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; ?? #7 jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get()Ljava/lang/Object; AbstractClassLoaderValue.java:329 #8 jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(Ljava/lang/ClassLoader;Ljava/util/function/BiFunction;)Ljava/lang/Object; AbstractClassLoaderValue.java:205 #9 java.lang.reflect.Proxy.getProxyConstructor(Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor; Proxy.java:427 #10 java.lang.reflect.Proxy.newProxyInstance(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object; Proxy.java:1034 #11 sun.reflect.annotation.AnnotationParser$1.run()Ljava/lang/annotation/Annotation; AnnotationParser.java:301 #12 sun.reflect.annotation.AnnotationParser$1.run()Ljava/lang/Object; AnnotationParser.java:299 #13 java.security.AccessController.executePrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;Ljava/lang/Class;)Ljava/lang/Object; AccessController.java:778 #14 java.security.AccessController.doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object; AccessController.java:319 #15 sun.reflect.annotation.AnnotationParser.annotationForMap(Ljava/lang/Class;Ljava/util/Map;)Ljava/lang/annotation/Annotation; AnnotationParser.java:299 #16 sun.reflect.annotation.AnnotationParser.parseAnnotation2(Ljava/nio/ByteBuffer;Ljdk/internal/reflect/ConstantPool;Ljava/lang/Class;Z[Ljava/lang/Class;)Ljava/lang/annotation/Annotation; AnnotationParser.java:288 #17 sun.reflect.annotation.AnnotationParser.parseAnnotations2([BLjdk/internal/reflect/ConstantPool;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/util/Map; AnnotationParser.java:121 #18 sun.reflect.annotation.AnnotationParser.parseAnnotations([BLjdk/internal/reflect/ConstantPool;Ljava/lang/Class;)Ljava/util/Map; AnnotationParser.java:73 #19 java.lang.Class.createAnnotationData(I)Ljava/lang/Class$AnnotationData; Class.java:4246 #20 java.lang.Class.annotationData()Ljava/lang/Class$AnnotationData; Class.java:4235 #21 java.lang.Class.createAnnotationData(I)Ljava/lang/Class$AnnotationData; Class.java:4251 #22 java.lang.Class.annotationData()Ljava/lang/Class$AnnotationData; Class.java:4235 #23 java.lang.Class.getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; Class.java:4118 #24 java.lang.reflect.AnnotatedElement.isAnnotationPresent(Ljava/lang/Class;)Z AnnotatedElement.java:292 #25 java.lang.Class.isAnnotationPresent(Ljava/lang/Class;)Z Class.java:4128 ... Previous write of size 4 at 0x000082c213f4 by thread T22 (mutexes: write M3, write M4, write M5, write M6, write M7, write M8, write M9, write M10, write M11, write M12, write M13, write M14, write M15): #0 java.lang.reflect.ProxyGenerator.addProxyMethod(Ljava/lang/reflect/Method;Ljava/lang/Class;)V ProxyGenerator.java:542 #1 java.lang.reflect.ProxyGenerator.generateClassFile()[B ProxyGenerator.java:478 #2 java.lang.reflect.ProxyGenerator.generateProxyClass(Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/util/List;I)[B ProxyGenerator.java:178 #3 java.lang.reflect.Proxy$ProxyBuilder.defineProxyClass(Ljava/lang/reflect/Proxy$ProxyBuilder$ProxyClassContext;Ljava/util/List;)Ljava/lang/Class; Proxy.java:544 #4 java.lang.reflect.Proxy$ProxyBuilder.build()Ljava/lang/reflect/Constructor; Proxy.java:657 #5 java.lang.reflect.Proxy.lambda$getProxyConstructor$0(Ljava/lang/ClassLoader;Ljdk/internal/loader/AbstractClassLoaderValue$Sub;)Ljava/lang/reflect/Constructor; Proxy.java:429 #6 java.lang.reflect.Proxy$$Lambda+0x00000001000dc958.apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; ?? #7 jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get()Ljava/lang/Object; AbstractClassLoaderValue.java:329 #8 jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(Ljava/lang/ClassLoader;Ljava/util/function/BiFunction;)Ljava/lang/Object; AbstractClassLoaderValue.java:205 #9 java.lang.reflect.Proxy.getProxyConstructor(Ljava/lang/Class;Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor; Proxy.java:427 #10 java.lang.reflect.Proxy.newProxyInstance(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object; Proxy.java:1034 #11 sun.reflect.annotation.AnnotationParser$1.run()Ljava/lang/annotation/Annotation; AnnotationParser.java:301 #12 sun.reflect.annotation.AnnotationParser$1.run()Ljava/lang/Object; AnnotationParser.java:299 #13 java.security.AccessController.executePrivileged(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;Ljava/lang/Class;)Ljava/lang/Object; AccessController.java:778 #14 java.security.AccessController.doPrivileged(Ljava/security/PrivilegedAction;)Ljava/lang/Object; AccessController.java:319 #15 sun.reflect.annotation.AnnotationParser.annotationForMap(Ljava/lang/Class;Ljava/util/Map;)Ljava/lang/annotation/Annotation; AnnotationParser.java:299 #16 sun.reflect.annotation.AnnotationParser.parseAnnotation2(Ljava/nio/ByteBuffer;Ljdk/internal/reflect/ConstantPool;Ljava/lang/Class;Z[Ljava/lang/Class;)Ljava/lang/annotation/Annotation; AnnotationParser.java:288 #17 sun.reflect.annotation.AnnotationParser.parseAnnotations2([BLjdk/internal/reflect/ConstantPool;Ljava/lang/Class;[Ljava/lang/Class;)Ljava/util/Map; AnnotationParser.java:121 #18 sun.reflect.annotation.AnnotationParser.parseAnnotations([BLjdk/internal/reflect/ConstantPool;Ljava/lang/Class;)Ljava/util/Map; AnnotationParser.java:73 #19 java.lang.reflect.Executable.declaredAnnotations()Ljava/util/Map; Executable.java:646 #20 java.lang.reflect.Executable.declaredAnnotations()Ljava/util/Map; Executable.java:644 #21 java.lang.reflect.Executable.getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; Executable.java:612 #22 java.lang.reflect.Method.getAnnotation(Ljava/lang/Class;)Ljava/lang/annotation/Annotation; Method.java:793 #23 java.lang.reflect.AnnotatedElement.isAnnotationPresent(Ljava/lang/Class;)Z AnnotatedElement.java:292 #24 java.lang.reflect.AccessibleObject.isAnnotationPresent(Ljava/lang/Class;)Z AccessibleObject.java:558 ...
07-03-2025