Почему мой пользовательский SecurityManager вызывает исключения в 16-й раз, когда я создаю объект с помощью Constructor.newInstance?
В настоящее время я работаю над созданием небольшого Java-приложения, в котором доверенный код должен выполняться вместе с ненадежным кодом. Для этого я установил кастом SecurityManager
это бросает SecurityException
s в любое время проверяется разрешение.
В качестве моста между доверенным и ненадежным кодом, у меня есть поток, который использует Constructor.newInstance()
создать экземпляр объекта ненадежного типа. Во время выполнения этого вызова диспетчер безопасности настроен на блокировку всего. Интересно, что первые 15 раз, когда я пытаюсь создать объекты, используя Constructor.newInstance()
, все отлично работает, но в 16-й раз я получаю SecurityException
,
Мне удалось объяснить это простой тестовой программой:
import java.lang.reflect.*;
import java.security.*;
public class Main {
/* Track how many instances have been created so that we can see when the exception
* is thrown.
*/
private static int numInstances = 0;
public Main() {
System.out.println("Number created: " + ++numInstances);
}
public static void main(String[] args) {
/* Get the constructor for Main so that we can instantiate everything
* later on.
*/
Constructor<Main> ctor;
try {
ctor = Main.class.getConstructor();
} catch (NoSuchMethodException e) {
e.printStackTrace();
return;
}
/* Install a super prohibitive security manager that disallows all operations. */
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission p) {
/* Nothing is allowed - any permission check causes a security
* exception.
*/
throw new SecurityException("Not permitted: " + p);
}
});
/* Continuously create new Main objects. */
try {
while (true) {
ctor.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
Эта программа устанавливает SecurityManager
чья checkPermission
всегда выдает исключение независимо от того, какое разрешение запрашивается. Затем он сидит в цикле и использует ctor.newInstance()
создать экземпляр безобидного Main
объект, который выводит количество сгенерированных экземпляров. Вывод этой программы в моей системе выглядит следующим образом:
Number created: 1
Number created: 2
Number created: 3
Number created: 4
Number created: 5
Number created: 6
Number created: 7
Number created: 8
Number created: 9
Number created: 10
Number created: 11
Number created: 12
Number created: 13
Number created: 14
Number created: 15
java.lang.SecurityException: Not permitted: ("java.lang.RuntimePermission" "createClassLoader")
at Main$1.checkPermission(Main.java:32)
at java.lang.SecurityManager.checkCreateClassLoader(SecurityManager.java:611)
at java.lang.ClassLoader.checkCreateClassLoader(ClassLoader.java:274)
at java.lang.ClassLoader.<init>(ClassLoader.java:316)
at sun.reflect.DelegatingClassLoader.<init>(ClassDefiner.java:72)
at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:60)
at sun.reflect.ClassDefiner$1.run(ClassDefiner.java:58)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:57)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:399)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:396)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:395)
at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:94)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:48)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at Main.main(Main.java:39)
Согласно Javadoc дляRuntimePermission
, createClassLoader
Разрешение рискованно предоставлять:
Это чрезвычайно опасное разрешение для предоставления. Вредоносные приложения, которые могут создавать свои собственные загрузчики классов, могут затем загружать в систему свои собственные мошеннические классы. Эти вновь загруженные классы могут быть помещены в любой домен защиты загрузчиком классов, тем самым автоматически предоставляя классам разрешения для этого домена.
У меня есть два вопроса:
Что конкретно вызывает эту ошибку? Почему в 16-й раз я получаю запрос на загрузчик классов? Я подозреваю, что это связано с тем, что Java пытается оптимизировать отражение, генерируя байт-код для непосредственного создания объекта, но я не уверен.
Без внесения в белый список
createClassLoader
привилегия, которая опасна, есть ли способ создать недоверенные объекты из доверенного кода?Я фундаментально подхожу к этому неправильно?
Спасибо!
1 ответ
Проверьте это на GrepCode:
72 private static int infThreshold = 15;
15 - это значение по умолчанию для порога инфляции, в NativeConstructorAccessorImpl введено количество отражающих вызовов до более агрессивной оптимизации:
47 if (++ numInvocations> ReflectionFactory.lationThreshold ()) {
48 ConstructorAccessorImpl acc = ( ConstructorAccessorImpl)
49новый MethodAccessorGenerator ().
50 generateConstructor (c. GetDeclaringClass(),
51 в. getParameterTypes (),
52 в. getExceptionTypes (),
53 в. getModifiers ());
54 родитель. setDelegate (acc);
И этот конкретный код вызывает создание нового загрузчика классов, что приводит к вашему исключению на 16-й итерации. Генерация байт-кода происходит в классе MethodAccessorGenerator, и это самый интересный бит:
387// Класс нагрузки
388 vec. отделка ();
389последний байт [] байт = vec. getData();
390// Примечание: загрузчик классов - единственное, что действительно имеет значение
391// здесь - важно получить сгенерированный код в
392// то же пространство имен, что и у целевого класса. Поскольку сгенерированный код
393// в любом случае является привилегированным, домен защиты, вероятно, не имеет
394// дело.
395вернуть AccessController. doPrivileged (
396новых привилегированных действий< MagicAccessorImpl> () {
397 public MagicAccessorImpl run () {
398 попыток {
399 возврат ( MagicAccessorImpl)
400 ClassDefiner. defineClass
401 (генерируется имя,
402 байта,
403 0,
Длина 404 байта,
405 объявляя класс. getClassLoader()). newInstance();
406 } catch ( InstantiationException e) {
407 броска ( InternalError)
408 новых InternalError(). initCause(e);
409 } catch ( IllegalAccessException e) {
410 броска ( InternalError)
411 новая InternalError(). initCause(e);
412 }
413 }
414 });
Что касается предоставления этого разрешения, у вас все еще есть возможность тщательно сформировать домен защиты для своего кода, которому вы предоставляете разрешение, без предоставления его иностранному коду.