Добрый вечер. Постараюсь подробно описать проблему. Необходимо было реализовать "песочницу" (Sandbox) для автоматической проверки олимпиадных решений по программированию через домены приложений (AppDomain). То есть участник соревнования присылает исходник с решением задачи, моя программа компилирует его и запускает exe-файл с помощью AppDomain.ExecuteAssembly(<имя_сборки>) в мною созданном AppDomain, в котором уже выставлены разрешения (Permissions), ограничивающие сборку в правах. Например, чтобы нельзя было работать с реестром, файловой системой, создавать новые процессы, новые домены приложений и т.д. Но я не смог найти ни на форумах, ни в MSDN информацию о том, как ограничить по времени выполнения и по выделению оперативной памяти запускаемую в домене сборку. Сейчас использую связку: AppDomain с его Permissions и Process с возможностью ограничивать время выполнения. Но этот вариант не подходит, так как последовательно сначала сборка исполняется в AppDomain без учета времени и памяти (в этот момент может произойти переполнение или же программа может "вечно" работать), а потом уже, по завершении, запускается в Process. Нужно чтобы все ограничения были в рамках AppDomain. Ниже приведен код. Подскажите, пожалуйста, как решить этот вопрос? Или же подскажите другие варианты, сочетающиеся с AppDomain (от доменов приложений отказываться не вижу смысла, ибо в них есть большая часть необходимых мне ограничений). Не исключаю, что решение может быть простым. Заранее прошу прощения, если его не разглядел.
Сам класс "песочницы" (убраны не относящиеся к теме методы и свойства):
| Код | public class Sandbox { private AppDomain SandboxAppDomain { get; set; } private PermissionSet SandboxPermissionSet { get; set; }
public Sandbox(string workingDirectory, PermissionSet permissionSet) { SandboxPermissionSet = permissionSet; AppDomainSetup sandboxAppDomainSetup = new AppDomainSetup(); sandboxAppDomainSetup.ApplicationBase = workindDirectory; SandboxAppDomain = AppDomain.CreateDomain("Sandbox", new Evidence(), sandboxAppDomainSetup, SandboxPermissionSet); }
public void ExecuteAssembly(string assemblyFile) { SandboxAppDomain.ExecuteAssembly(assemblyFile); } }
|
Вызов ExecuteAssembly из кода:
| Код | private string InputFileName { get; set; } private string OutputFileName { get; set; } private string ExecutableName { get; set; }
private bool RunInSandbox(string workingDirectory, int timeLimit) { string fileNameArg = System.IO.Path.Combine(workingDirectory, ExecutableName + ".exe");
var tempInput = System.IO.Path.Combine(workingDirectory, InputFileName); var tempOutput = System.IO.Path.Combine(workingDirectory, OutputFileName);
// изначально запрещаем все AppDomain'у PermissionSet permissionSet = new PermissionSet(PermissionState.None);
// добавляем необходимые разрешения permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); permissionSet.AddPermission(new UIPermission(PermissionState.Unrestricted)); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, workingDirectory)); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, tempInput)); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Write, tempOutput)); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Append, tempOutput)); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, fileNameArg));
// создаем новый домен приложений с выставленными разрешениями Sandbox sandbox = new Sandbox(workingDirectory, permissionSet);
// загружаем сборку в домен и исполняем sandbox.ExecuteAssembly(fileNameArg);
// выгружаем домен sandbox.Unload();
// а теперь проверяем сборку на ограничение по времени (после того, как она завершит исполняться в AppDomain) var startInfo = new ProcessStartInfo { FileName = fileNameArg, CreateNoWindow = true, UseShellExecute = false, WindowStyle = ProcessWindowStyle.Hidden, WorkingDirectory = workingDirectory, ErrorDialog = false, LoadUserProfile = false, };
var process = new Process(); process.StartInfo = startInfo;
if (process.Start()) { if (process.WaitForExit(timeLimit * 1000)) { return true; } else { process.Kill(); if (!process.HasExited) { process.WaitForExit(); } } } return false; }
|
|