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 void test(final Callable<JavaClass[]> callable) throws Exception {
107 final ExecutorService executor = Executors.newSingleThreadExecutor();
108 try {
109 final Future<JavaClass[]> future = executor.submit(callable);
110
111
112 future.get(3, TimeUnit.SECONDS);
113 fail("Should have thrown ClassCircularityError for cyclic hierarchy");
114 } catch (final TimeoutException e) {
115 fail("Timeout: infinite loop vulnerability detected");
116 } catch (final ExecutionException e) {
117 if (e.getCause() instanceof ClassFormatException) {
118
119 return;
120 }
121 throw e;
122 } finally {
123 executor.shutdownNow();
124 }
125 }
126
127
128
129
130
131
132 @Test
133 void testGetAllInterfacesCyclic() throws Exception {
134
135
136
137 final ExecutorService executor = Executors.newSingleThreadExecutor();
138 try {
139 final Future<JavaClass[]> future = executor.submit(() -> cyclicTestClass.getAllInterfaces());
140
141
142 final JavaClass[] interfaces = future.get(3, TimeUnit.SECONDS);
143 assertNotNull(interfaces, "getAllInterfaces() should return non-null array");
144 assertTrue(interfaces.length >= 2, "Should find at least CyclicInterfaceA and CyclicInterfaceB");
145 } catch (final TimeoutException e) {
146 fail("getAllInterfaces() timed out - infinite queue growth vulnerability detected");
147 } catch (final ExecutionException e) {
148 if (e.getCause() instanceof OutOfMemoryError) {
149 fail("getAllInterfaces() caused OutOfMemoryError - infinite queue growth vulnerability detected");
150 }
151 throw e;
152 } finally {
153 executor.shutdownNow();
154 }
155 }
156
157
158
159
160
161 @Test
162 void testGetSuperClassesCyclic() throws Exception {
163 test(cyclicClassA::getSuperClasses);
164 }
165 }