From 24d8d790c22913a9a92ea70bad721b06a735cd46 Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Wed, 26 Aug 2020 00:08:57 +0200 Subject: [PATCH] Initializes classes prior to instrumentation to avoid uncontrolled code execution. Fixes \# #2011 --- .../bytebuddy/InlineBytecodeGenerator.java | 10 ++++++++ .../org/mockitoinline/InitializationTest.java | 25 +++++++++++++++++++ .../java/org/mockitoinline/PluginTest.java | 8 +++++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java diff --git a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java index 409f58201c..067dea7a04 100644 --- a/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java +++ b/src/main/java/org/mockito/internal/creation/bytebuddy/InlineBytecodeGenerator.java @@ -206,6 +206,7 @@ public Class mockClass(MockFeatures features) { Set> types = new HashSet<>(); types.add(features.mockedType); types.addAll(features.interfaces); + synchronized (this) { triggerRetransformation(types, false); } @@ -223,17 +224,26 @@ public synchronized void mockClassConstruction(Class type) { triggerRetransformation(Collections.singleton(type), false); } + private static void assureInitialization(Class type) { + try { + Class.forName(type.getName(), true, type.getClassLoader()); + } catch (Throwable ignore) { + } + } + private void triggerRetransformation(Set> types, boolean flat) { Set> targets = new HashSet>(); for (Class type : types) { if (flat) { if (!mocked.contains(type) && flatMocked.add(type)) { + assureInitialization(type); targets.add(type); } } else { do { if (mocked.add(type)) { + assureInitialization(type); if (!flatMocked.remove(type)) { targets.add(type); } diff --git a/subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java b/subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java new file mode 100644 index 0000000000..c4f62f400f --- /dev/null +++ b/subprojects/inline/src/test/java/org/mockitoinline/InitializationTest.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020 Mockito contributors + * This program is made available under the terms of the MIT License. + */ +package org.mockitoinline; + +import org.junit.Test; +import org.mockito.Mockito; + +import static junit.framework.TestCase.assertEquals; + +public class InitializationTest { + + @Test + public void assure_initialization_prior_to_instrumentation() { + @SuppressWarnings("unused") + SampleEnum mock = Mockito.mock(SampleEnum.class); + SampleEnum[] values = SampleEnum.values(); + assertEquals("VALUE", values[0].name()); + } + + public enum SampleEnum { + VALUE + } +} diff --git a/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java b/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java index 19526db2a9..16e2da2915 100644 --- a/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java +++ b/subprojects/inline/src/test/java/org/mockitoinline/PluginTest.java @@ -7,14 +7,20 @@ import org.junit.Test; import org.mockito.internal.configuration.plugins.Plugins; import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker; +import org.mockito.internal.util.reflection.ModuleMemberAccessor; import static org.junit.Assert.*; public class PluginTest { @Test - public void plugin_type_should_be_inline() throws Exception { + public void mock_maker_should_be_inline() throws Exception { assertTrue(Plugins.getMockMaker() instanceof InlineByteBuddyMockMaker); } + @Test + public void member_accessor_should_be_module() throws Exception { + assertTrue(Plugins.getMemberAccessor() instanceof ModuleMemberAccessor); + } + }