1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.bcel.classfile;
21
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25
26 import java.io.ByteArrayInputStream;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34
35 import org.apache.bcel.Repository;
36 import org.apache.bcel.util.SyntheticRepository;
37 import org.junit.jupiter.api.AfterEach;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40
41
42
43
44
45
46
47
48
49
50 class JavaClassCyclicTest {
51
52 private JavaClass cyclicClassA;
53
54 private JavaClass cyclicClassB;
55
56 private JavaClass cyclicInterfaceA;
57
58 private JavaClass cyclicInterfaceB;
59
60 private JavaClass cyclicTestClass;
61
62 private SyntheticRepository repo;
63
64 @BeforeEach
65 void setUp() throws Exception {
66 repo = SyntheticRepository.getInstance();
67 Repository.setRepository(repo);
68
69 final byte[] interfaceABytes = JavaClassTest.createInterface("CyclicInterfaceA", "CyclicInterfaceB");
70 final byte[] interfaceBBytes = JavaClassTest.createInterface("CyclicInterfaceB", "CyclicInterfaceA");
71 final byte[] testClassBytes = JavaClassTest.createClass("CyclicTestClass", "java.lang.Object", "CyclicInterfaceA");
72 cyclicInterfaceA = new ClassParser(new ByteArrayInputStream(interfaceABytes), "CyclicInterfaceA.class").parse();
73 cyclicInterfaceB = new ClassParser(new ByteArrayInputStream(interfaceBBytes), "CyclicInterfaceB.class").parse();
74 cyclicTestClass = new ClassParser(new ByteArrayInputStream(testClassBytes), "CyclicTestClass.class").parse();
75 repo.storeClass(cyclicInterfaceA);
76 repo.storeClass(cyclicInterfaceB);
77 repo.storeClass(cyclicTestClass);
78
79 final byte[] classABytes = JavaClassTest.createClass("CyclicClassA", "CyclicClassB");
80 final byte[] classBBytes = JavaClassTest.createClass("CyclicClassB", "CyclicClassA");
81 cyclicClassA = new ClassParser(new ByteArrayInputStream(classABytes), "CyclicClassA.class").parse();
82 cyclicClassB = new ClassParser(new ByteArrayInputStream(classBBytes), "CyclicClassB.class").parse();
83 repo.storeClass(cyclicClassA);
84 repo.storeClass(cyclicClassB);
85 }
86
87 @AfterEach
88 void tearDown() {
89 if (cyclicInterfaceA != null) {
90 repo.removeClass(cyclicInterfaceA);
91 }
92 if (cyclicInterfaceB != null) {
93 repo.removeClass(cyclicInterfaceB);
94 }
95 if (cyclicTestClass != null) {
96 repo.removeClass(cyclicTestClass);
97 }
98 if (cyclicClassA != null) {
99 repo.removeClass(cyclicClassA);
100 }
101 if (cyclicClassB != null) {
102 repo.removeClass(cyclicClassB);
103 }
104 }
105
106
107
108
109
110
111 @Test
112 void testGetAllInterfacesCyclic() throws Exception {
113
114
115
116 final ExecutorService executor = Executors.newSingleThreadExecutor();
117 try {
118 final Future<JavaClass[]> future = executor.submit(() -> cyclicTestClass.getAllInterfaces());
119
120
121 final JavaClass[] interfaces = future.get(3, TimeUnit.SECONDS);
122 assertNotNull(interfaces, "getAllInterfaces() should return non-null array");
123 assertTrue(interfaces.length >= 2, "Should find at least CyclicInterfaceA and CyclicInterfaceB");
124 } catch (final TimeoutException e) {
125 fail("getAllInterfaces() timed out - infinite queue growth vulnerability detected");
126 } catch (final ExecutionException e) {
127 if (e.getCause() instanceof OutOfMemoryError) {
128 fail("getAllInterfaces() caused OutOfMemoryError - infinite queue growth vulnerability detected");
129 }
130 throw e;
131 } finally {
132 executor.shutdownNow();
133 }
134 }
135
136
137
138
139
140 @Test
141 void testGetSuperClassesCyclic() throws Exception {
142 test(cyclicClassA::getSuperClasses);
143 }
144
145 void test(final Callable<JavaClass[]> callable) throws Exception {
146 final ExecutorService executor = Executors.newSingleThreadExecutor();
147 try {
148 final Future<JavaClass[]> future = executor.submit(callable);
149
150
151 future.get(3, TimeUnit.SECONDS);
152 fail("Should have thrown ClassCircularityError for cyclic hierarchy");
153 } catch (final TimeoutException e) {
154 fail("Timeout: infinite loop vulnerability detected");
155 } catch (final ExecutionException e) {
156 if (e.getCause() instanceof ClassFormatException) {
157
158 return;
159 }
160 throw e;
161 } finally {
162 executor.shutdownNow();
163 }
164 }
165 }