由于工作原因,一直没有时间把在线上遇到的问题总结一下。还好,今天我来了。
废话少说了。
主要说一下“java 正则表达式中的一个漏洞”,详细问题描述
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6988218
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507
目前使用 1.6 和 1.7 都没有修补该漏洞。
来个白话文吧
案例代码 Test.java
1 final Pattern pattern = Pattern.compile("(0*)*A"); 2 final String input = "0000000000000000000000000000000000000000000000"; 3 4 long startTime = System.currentTimeMillis(); 5 Matcher matcher = pattern.matcher(input); 6 System.out.println(matcher.find()); 7 System.out.println("Regex took:" + (System.currentTimeMillis() - startTime) + "ms");
-
意思是说匹配器在输入的末尾并没有检测到”A”。现在外侧的限定符后退一次,内存的则前进一次,如此重复,无法得到结果。
-
因此,匹配器逐步回退,并尝试所有的组合以找出匹配符号。它最终将返回(没有匹配的结果),但是该过程的复杂性是指数型的(输入中添加一个字符加倍了运行时间)
赶紧查看cpu占用率(top)
尼玛呀,这都上99.9了。吓死宝宝了。赶紧看看这货到底是啥?
查看进程信息(ps -ef | grep 17837)(17837为进程id)
这不是我写的那个测试类吗?看来漏洞复现了。赶紧看一下这货暂用CPU的情况
ps mp 17837 -o THREAD,tid,time (注意逗号之间不要加空格)
这家伙已经占用CPU快一个小时了。应该是死循环了。赶紧看看出啥幺蛾子了。
这时我们可以通过jdk提供的工具查看具体的堆栈信息(jstack )
jstack 17837
#4/13日 发现可以使用 kill -3 pid 来查看dump信息。高兴
问题复现了。这就是我们说说的jdk正则的漏洞。
主要表象就是长时间占用CPU,应用表象就是:页面访问白板,无响应。
具体的解决方案,我不赘述网上其他人说的c,Python的解决方法(因为我不会吗。。。。。)
1)优化正则,别写的那个正则别人一看就吓一跳。其实业务没有那么简单
2)使用线程,并且捕获异常,详见 http://stackoverflow.com/questions/910740/cancelling-a-long-running-regex-match