Java沙箱实现是重写类加载器和安全管理器,通过设置的全局安全管理器来控制执行程序的权限
说明: 安全策略只对安装安全管理器之后的类生效,之前的类不再此管理范围之内,利用这一点可以预先设置我们需要的操作,而对某个点之后的所有非法操作进行权限设置.
类加载器重写
1. /**
2. * [重写的类加载器]
3. * 沙箱程序类加载器,可根据指定路径加载制定类class文件.
4. *
5. * [说明]
6. * 仅包内可见
7. *
8. * @author 刘金鑫
9. * @version 1.0
10. * */
11. package org.hljoj.core.judge.sandbox;
12.
13. import java.io.File;
14. import java.io.FileInputStream;
15.
16. import org.hljoj.core.judge.util.ConstantParam;
17.
18. class SandboxClassLoader extends ClassLoader{
19. /**默认classPath*/
20. private String _classPath;
21.
22. /**
23. * 构造函数
24. * @param classPath 类加载器默认classPath
25. * */
26. public SandboxClassLoader(String classPath) {
27. this._classPath = classPath;
28. }
29.
30. @Override
31. protected Class<?> findClass(String className) throws ClassNotFoundException {
32. return loadClass(_classPath, className);
33. }
34.
35. /**
36. * 更改类加载器加载类的classpath,在制定路径下加载制定的类class文件
37. * @param classPath 要加载的类路径
38. * @param className 要加载的类名
39. * 最为限定,只能加载不含包的类.
40. * */
41. public Class<?> loadClass(String classPath, String className) throws ClassNotFoundException{
42. if(className.indexOf('.') >= 0) {
43. throw new ClassNotFoundException(className);
44. }
45.
46. new File(classPath + ConstantParam.SEPARATOR + className + ".class");
47. byte[] mainClass = new byte[(int) classFile.length()];
48. try {
49. new FileInputStream(classFile);
50. in.read(mainClass);
51. in.close();
52. catch (Exception e) {
53. //e.printStackTrace();
54. throw new ClassNotFoundException(className);
55. }
56.
57. return super.defineClass(className, mainClass, 0, mainClass.length);
58. }
59.
60. /**
61. * 获取classPath
62. * @return String classPath
63. * */
64. public String getClassPath(){
65. return _classPath + ConstantParam.SEPARATOR;
66. }
67. }
重写安全管理器
1. /**
2. * [重写的安全管理器]
3. * 安全管理器用来限制客户端提交的Java源程序运行的功能,
4. * 对程序读/写本地文件系统,修改系统属性连接网络,
5. * 数据库等一切可能对本地计算机系统造成危害的操作进行屏蔽,
6. * 如有这些操作将抛出SecurityException异常,并终止程序执行.
7. *
8. * [说明]:
9. * 仅包内可见
10. * 不允许提交的源程序执行exit(n)函数-即不允许源程序中途
11. * 终止虚拟机的运行,但是调用源代码端可执行exit(n)函数.
12. *
13. * @author 刘金鑫
14. * @version 1.0
15. * */
16.
17. package org.hljoj.core.judge.sandbox;
18.
19. import java.io.FilePermission;
20. import java.lang.reflect.ReflectPermission;
21. import java.security.Permission;
22. import java.security.SecurityPermission;
23. import java.util.PropertyPermission;
24.
25. import org.hljoj.core.judge.util.ConstantParam;
26.
27. class SandboxSecurityManager extends SecurityManager {
28.
29. public static final int EXIT = ConstantParam.RANDOM.nextInt();
30.
31. /**
32. * 重写强行退出检测
33. * 防止用户自行终止虚拟机的运行,但是调用程序端可以执行退出
34. * */
35. public void checkExit(int status) {
36. if (status != EXIT)
37. throw new SecurityException("Exit On Client Is Not Allowed!");
38. }
39.
40. /**
41. * 策略权限查看
42. * 当执行操作时调用,如果操作允许则返回,操作不允许抛出SecurityException
43. * */
44. private void sandboxCheck(Permission perm) throws SecurityException {
45. // 设置只读属性
46. if (perm instanceof SecurityPermission) {
47. if (perm.getName().startsWith("getProperty")) {
48. return;
49. }
50. else if (perm instanceof PropertyPermission) {
51. if (perm.getActions().equals("read")) {
52. return;
53. }
54. else if (perm instanceof FilePermission) {
55. if (perm.getActions().equals("read")) {
56. return;
57. }
58. else if (perm instanceof RuntimePermission || perm instanceof ReflectPermission){
59. return;
60. }
61.
62. throw new SecurityException(perm.toString());
63. }
64.
65. @Override
66. public void checkPermission(Permission perm) {
67. this.sandboxCheck(perm);
68. }
69.
70. @Override
71. public void checkPermission(Permission perm, Object context) {
72. this.sandboxCheck(perm);
73. }
74.
75. }
沙箱操作
1. /**
2. * [沙箱]
3. * 模拟沙箱功能,限制执行的Java程序的所有可能对本地机器的危害.
4. * 被执行的Java程序对于本机只有读属性,其他文件操作,更改系统属性,
5. * Socket网络连接,连接数据库等功能全部禁止.
6. *
7. * 沙箱与主模块之间使用Socket进行通信,完全独立于系统接收执行信
8. * 息类(包含要执行程序的相关信息)后执行,执行完毕后返回给主模块
9. * 结果类(包含执行结果的相关信息)
10. *
11. * @author 刘金鑫
12. * @version 1.0
13. * */
14.
15. package org.hljoj.core.judge.sandbox;
16.
17. import java.io.BufferedInputStream;
18. import java.io.BufferedOutputStream;
19. import java.io.ByteArrayInputStream;
20. import java.io.ByteArrayOutputStream;
21. import java.io.IOException;
22. import java.io.ObjectInputStream;
23. import java.io.ObjectOutputStream;
24. import java.io.PrintStream;
25. import java.lang.management.ManagementFactory;
26. import java.lang.management.MemoryMXBean;
27. import java.lang.reflect.InvocationTargetException;
28. import java.lang.reflect.Method;
29. import java.lang.reflect.Modifier;
30. import java.net.ServerSocket;
31. import java.net.Socket;
32. import java.util.Timer;
33. import java.util.TimerTask;
34. import java.util.concurrent.Callable;
35. import java.util.concurrent.FutureTask;
36.
37. import org.hljoj.core.judge.util.ConstantParam;
38. import org.hljoj.core.judge.util.JudgeResult;
39.
40. public final class Sandbox {
41.
42. //用来收集线程的内存使用量
43. private static MemoryMXBean _memoryBean = ManagementFactory.getMemoryMXBean();
44.
45. //定向输出
46. private static ByteArrayOutputStream _baos = new ByteArrayOutputStream(1024);
47.
48. //Socket通信
49. private static Socket _socket = null;
50.
51. private static ServerSocket _serverSocket = null;
52.
53. private static ObjectInputStream _inputStream = null;
54.
55. private static ObjectOutputStream _outputStream = null;
56.
57. //执行提交程序线程
58. private static Thread _thread = null;
59.
60. //系统提供的默认的classpath
61. private static String _classPath = null;
62.
63. private static long _timeStart = 0;
64. //程序运行时间
65. private static long _timeUsed = 0;
66. private static int _baseMemory = 0;
67. //程序运行空间
68. private static int _memoryUsed = 0;
69.
70. //测评结果
71. private static String _result = null;
72.
73. /**
74. * 核心执行函数<br>
75. * 用于执行指定Main.class的main函数.<br>
76. * 根据线程ID可获取运行时间.
77. * */
78. private static String process(int runId, final int timeLimit) throws Exception {
79. null;
80. final Timer timer = new Timer(true);
81.
82. try {
83. final Method mainMethod = getMainMethod(runId);
84.
85. new FutureTask<String>(new Callable<String>() {
86. public String call() throws Exception {
87. try {
88. _timeStart = System.currentTimeMillis();
89. //启动计时器
90. 1);
91. // 执行main方法
92. null, new Object[] { new String[0] });
93. catch (InvocationTargetException e) {
94. Throwable targetException = e.getTargetException();
95. // 超过内存限制
96. if (targetException instanceof OutOfMemoryError)
97. setResult(JudgeResult.MEMORY_LIMIT_EXCEED);
98. // 非法操作处理
99. else if (targetException instanceof SecurityException || targetException instanceof ExceptionInInitializerError) {
100. setResult(JudgeResult.RESTRICT_FUNCTION);
101. // 运行超时
102. else if (targetException instanceof InterruptedException) {
103. setResult(JudgeResult.TIME_LIMITE_EXCEED);
104. else {
105. // 运行时错误处理
106. if (e.getCause().toString().contains("Output Limit Exceed"))
107. setResult(JudgeResult.OUTPUT_LIMIT_EXCEED);
108. else
109. setResult(JudgeResult.RUNTIME_ERROR, e.getCause().toString());
110. }
111.
112. throw new RuntimeException("Runtime Exception");
113. finally {
114. timer.cancel();
115. }
116. return _baos.toString();
117. }
118. });
119. catch (Exception e) {
120. throw new RuntimeException("Initalization Error");
121. }
122.
123. int) _memoryBean.getHeapMemoryUsage().getUsed();
124. new Thread(task);
125. _thread.start();
126. return task.get();
127. }
128.
129. /**
130. * 启动计时器,对执行程序进行时间限制.<br>
131. * 若超时,则中断执行线程<br>
132. * @return TimerTask
133. * */
134. private static TimerTask getTimerTask(){
135. return new TimerTask(){
136. public void run() {
137. if (_thread != null)
138. _thread.interrupt();
139. }
140. };
141. }
142.
143. private static int _outputSize = 0;
144. /**
145. * 初始化
146. * @param classPath 系统默认classPath路径
147. * @param port socket服务器监听端口
148. * */
149. private static void inita(String classPath, int port) throws Exception{
150. _classPath = classPath;
151.
152. new ServerSocket(port);
153. _socket = _serverSocket.accept();
154.
155. new ObjectOutputStream(_socket.getOutputStream());
156. new ObjectInputStream(_socket.getInputStream());
157.
158. //重新定向输出流
159. new PrintStream(new BufferedOutputStream(_baos) {
160. public void write(byte[] b, int off, int len) throws IOException {
161. _outputSize += len - off;
162. try {
163. super.write(b, off, len);
164. if (_outputSize > ConstantParam.OUTPUT_LIMIT)
165. throw new RuntimeException("Output Limit Exceed" + _outputSize);
166. catch (IOException e) {
167. if(e.getMessage().equals("Output Limit Exceed")){
168. throw e;
169. }
170. }
171. }
172. }));
173. }
174.
175. private static SandboxClassLoader _classLoader = null;
176. /**
177. * 获取指定路径下Main.class类的main入口函数.
178. * @param runId 指定类路径
179. * @return Method 返回的main方法
180. * */
181. private static Method getMainMethod(int runId) throws Exception{
182. new SandboxClassLoader(_classPath);
183. "Main");
184.
185. null;
186. "main", String[].class);
187.
188. if(!Modifier.isStatic(mainMethod.getModifiers()))
189. throw new Exception("Method Of Main Is Not Static");
190.
191. true);
192. return mainMethod ;
193. }
194.
195. /**
196. * 测评接口.
197. * 运行接收到的Java程序.
198. *
199. * @param runId 执行id
200. * @param problemId 提交问题id
201. * @param submitId 提交用户id
202. * @param timeLimit 时间限制
203. * @param memoryLimit 空间限制
204. * @param standardInput 程序标准输入字符串
205. * @param standardOutput 程序标准输出字符串
206. * */
207. public static void run(int runId, int timeLimit, int memoryLimit,
208. String standardInput, String standardOutput) {
209. 0;
210. 0;
211. _baos.reset();
212. 0;
213. setResult(JudgeResult.WRONG_ANSWER);
214. //定向输入流
215. new BufferedInputStream(new ByteArrayInputStream(standardInput.getBytes())));
216.
217. null;
218.
219. try {
220. // 必须在执行前对垃圾回收,否则不准确.
221. System.gc();
222. output = process(runId, timeLimit);
223. // 将程序输出与标准输出作比较
224. setResult(matchOutput(standardOutput.getBytes(), output.getBytes()));
225. // 获取程序运行时间和空间
226. _timeUsed = System.currentTimeMillis() - _timeStart;
227. int) ((_memoryBean.getHeapMemoryUsage().getUsed() - _baseMemory) / 1000);
228. catch (Exception e) {
229. if (e.getMessage().equals("Initalization Error")){
230. setResult(JudgeResult.WRONG_ANSWER);
231. }
232. }
233.
234. if (_memoryUsed > memoryLimit)
235. setResult(JudgeResult.MEMORY_LIMIT_EXCEED);
236.
237. try {
238. //向主模块返回执行结果
239. int)_timeUsed, _memoryUsed, _result);
240. catch (IOException e) {
241. e.printStackTrace();
242. }
243. }
244.
245. /**
246. * 向主模块发送运行结果.
247. *
248. * @param runId 运行runId
249. * @param timeUsed 代码运行时间(MS)
250. * @param memoryUsed 代码运行空间(B)
251. * @param result 代码执行结果
252. * */
253. private static void sendResult(int runId,int timeUsed, int memoryUsed, String result) throws IOException{
254. _outputStream.writeInt(runId);
255. _outputStream.writeInt(timeUsed);
256. _outputStream.writeInt(memoryUsed);
257. _outputStream.writeUTF(result);
258. }
259.
260. /**
261. * 接收运行参数
262. *
263. * @param runId 运行runId
264. * @param timeLimit 限制代码运行时间(MS)
265. * @param memoryLimit 限制代码运行空间(B)
266. * @param standardInput 标准输入
267. * @param standardOutput 标准输出
268. * */
269. private static void receiveMsg() throws IOException{
270. int runId = _inputStream.readInt();
271. int timeLimit = _inputStream.readInt();
272. int memoryLimit = _inputStream.readInt();
273. String standardInput = _inputStream.readUTF();
274. String standardOutput = _inputStream.readUTF();
275.
276. run(runId, timeLimit, memoryLimit, standardInput, standardOutput);
277. }
278.
279. /**
280. * 比较程序输出和标准输出,并返回比较结果.<br>
281. * 标准返回1.Accepted 2.Wrong Answer 3.Presenting Error结果
282. *
283. * @param standOutput 标准输出结果
284. * @param output 程序输出结果
285. * @return int 比较结果
286. * */
287. private static int matchOutput(byte[] standardOutput, byte[] output){
288. int i = 0;
289. int j = 0;
290.
291. do{
292. while (i < standardOutput.length && (standardOutput[i] == ConstantParam.SPACE || standardOutput[i] == '/t' ||
293. '/r' || standardOutput[i] == '/n'))
294. i++;
295. while (j < output.length && (output[j] == ConstantParam.SPACE || output[j] == '/t' ||
296. '/r' || output[j] == '/n'))
297. j++;
298. if (i < standardOutput.length && j < output.length && standardOutput[i] != output[j])
299. return JudgeResult.WRONG_ANSWER;
300.
301. i++;
302. j++;
303. while(j <= i && i < standardOutput.length && j < output.length);
304.
305. if (i != j)
306. return JudgeResult.PRESENTING_ERROR;
307.
308. return JudgeResult.ACCEPTED;
309. }
310.
311. /**
312. * 设置测评结果.
313. * @param JudgeResult结果类型.
314. * */
315. private static void setResult(int resultType){
316. _result = JudgeResult.toString(resultType);
317. }
318.
319. /**
320. * 设置测评结果.
321. * @param JudgeResult结果类型.
322. * @param remark 测评结果的备注.
323. * */
324. private static void setResult(int resultType, String remark){
325. setResult(resultType);
326.
327. if (remark.endsWith("StackOverflowError"))
328. "(" + JudgeResult.toString(JudgeResult.RUNTIME_STACK_OVERFLOW) + ")";
329. else if (remark.endsWith("/ by zero"))
330. "(" + JudgeResult.toString(JudgeResult.RUNTIME_DIVIDE_BY_ZERO) + ")";
331. else if (remark.contains("ArrayIndexOutOfBoundsException"))
332. "(" + JudgeResult.toString(JudgeResult.RUNTIME_ACCESS_VIOLATION) + ")";
333. else
334. "(" + JudgeResult.toString(JudgeResult.RUNTIME_ARRAY_BOUNDS_EXCEEDED) + ")";
335. }
336.
337. /**
338. * 关闭网络连接
339. * */
340. private static void close(){
341. try {
342. if (_inputStream != null)
343. _inputStream.close();
344. if (_outputStream != null)
345. _outputStream.close();
346. if (_socket != null)
347. _socket.close();
348. catch (IOException e) {
349. e.printStackTrace();
350. }
351. }
352.
353. /**
354. * 沙盒入口
355. * 传入参数 : <br>
356. * classPath -- args[0] ------ 保存class的classpath<br>
357. * port -- args[1] ------- 监听端口
358. * */
359. public static void main(String[] args) throws Exception{
360. 0], Integer.parseInt(args[1]));
361.
362. SecurityManager security = System.getSecurityManager();
363. if (security == null)
364. new SandboxSecurityManager());
365.
366. while (!_socket.isClosed()){
367. receiveMsg();
368. }
369.
370. close();
371. }
372. }
373.
374.