在生产环境中调试多线程

2022-03-221329

原文发布在DZone,原作者为:Shai Almog 传送门

​  当应用程序使用多个线程访问共享资源时,就可能会出现线程竞争的情况。如果我们没有适当的,结果就可能取决于哪个线程首先运行。当外部状态发生变化时,情况就会变得更加不可控制。   线程竞争的不仅可能会导致运行错误,还会引发安全漏洞。例如,通过使用问题的资源运行从而损坏资源,引发安全漏洞。这就是一个线程竞争导致破坏内存引发安全漏洞的例子。再例如应用程序有一个名为“admin”的管理员账号。由于验证限制,我们无法将自己的用户名更改为“admin”,但可以把它改成其他任何东西......通过恶意代码,将用户名重复设置“admix”、“xdmin”等值。如果系统按顺序写入字符,最终可能会出现“admin”。我们可以重复运行这段代码,直到获得正确的结果,完成权限提升攻击,让我们可以访问原本不应该访问到的信息和功能,例如文件系统等。因而存在严重的安全隐患。   就我个人而言,线程竞争最大的问题是可能会引发难以捉摸的错误与未定义的行为。

序列化一切?

  不幸的是,线程竞争条件很难解决。设置独占访问或临界区,会大大降低应用程序的性能。它们会阻止使用计算机资源,并降低我们的 CPU 缓存利用率。我们希望尽可能减少同步操作,但也不希望出现未定义的行为。有许多优化和策略能够构建高性能的多线程应用程序,最大化只读状态、不变性等都是很好的方案。   但真正的问题是,知道你有一个竞争条件情况下,如何去检测它?

检测线程静态条件

  市面上有一些优秀的线程检测工具,其中就有一些通过使用静态分析来审查应用程序代码并显示风险区域;也有的在运行时进行检查线程的活动,但并不常见,因为调试环境不能代表生产环境。   我们的生产环境非常复杂,而且复杂程度是日益剧增的。在这种环境中检测多线程运行并验证,是具有挑战性的......

使用静态分析工具解决漏洞

  静态分析工具的问题通常是它们经常发出假阳性的结果,它们指出是的风险,而并非是会在生产环境中真实发生的问题,这导致开发人员也不会完全认同这些工具的分析。你会通过什么方法去验证潜在的线程竞争风险呢?   使用 Lightrun 很容易做到这一点。我们可以记录怀疑的代码块中的线程。如果多个线程同时进入该区域,则存在竞争条件。   我们可以做的最简单的事情是添加一个日志条目,如下所示:

创建日志   日志打印“线程{ Thread.currentThread().getName()}进入”,我们可以在代码段末尾添加对应的“退出”版本,但这也给我们带来了一个新问题。   在简单的情况下,这种操作非常合适。但如果我们有很多请求需要执行该代码块,就可能会遇到限制。当操作占用过多 CPU 时,Lightrun 会限制,这导致我们可能会出现不均匀的进入/退出的打印输出。   该解决方案是一个多部分解决方案。首先,我们可以像上面那样使用日志来获取访问该代码块线程的名称。   接下来,我们需要验证是否有大量请求。为此,我们可以添加一个计数器: 创建计数器   我们还可以通过将计数限制,为特定线程来进一步缩小范围,例如对于“Thread 1”,我们可以将条件设置为:img

Thread.currentThread().getName().equals("Thread 1")

  最后,我们可以使用具有多个捕获的快照:

img

!Thread.currentThread().getName().equals("Thread 1")

  注意开头的“NOT”运算符。假设只有 “Thread 1” 会发生突变,因此我们会从字面上看到堆栈,从不同线程跟踪到访问此操作。我们可以在长时间运行的系统中保留这样的操作数天(不要忘记在高级模式下更新到期),如果可以访问该代码块,该过程将触发此操作。

分享
点赞1
打赏
上一篇:Docker常用命令笔记(一)
下一篇:SpringBoot 在项目启之后执行自定义方法的两种方式