探索协程世界:原理、现实与漏洞模式(下)

在本文的上篇关于协程相关理论的介绍后,相信读者已经对协程有了大致的认识,所以接下来将会介绍在过去发生过的与协程相关的安全问题。因为协程在不同语言的实现差异,会导致其呈现的形式也会有所差异。本篇将选取几个比较有特征的案例介绍。 一、anyio - Race Condition 这个问题并没有获得CVE编号,是用户进行测试的时候发现的。anyio是一个 Python 异步网络并发库,可以在trio或者asyncio上运行。其完美的兼容了asyncio和trio,可以很方便的让代码进行重构。 然而这个库存在一个奇怪的问题,当尝试使用多线程访问这个库的时候,有概率触发一个库未初始化的问题。经过开发者定位,错误的代码位置如下: def get_async_backend(asynclib_name: str | None = None) -> AsyncBackend: if asynclib_name is None: asynclib_name = sniffio.current_async_library() modulename = "anyio._backends._" + asynclib_name try: module = sys.modules[modulename] except KeyError: module = import_module(modulename) return getattr(module, "backend_class") 这段代码的逻辑本质上为取出anyio中视线的另一个module对象并返回。实际上,在普通程序中他也被这样使用: async def sleep(delay: float) -> None: """ Pause the current task for the specified duration. :param delay: the duration, in seconds """ return await get_async_backend().sleep(delay) 可以看到,其取出来的库是提供支持协程异步函数的库。然而,这种写法其实存在一定的迷惑性。例如: async def func(d): await asy()....

2025年02月19日 · 10 分钟 · l1nk

探索协程世界:原理、实现与漏洞模式(上)

一、前言 在操作系统中,每当谈及异步,通常都是使用多线程/多进程来实现多任务的执行。然而在现代开发过程中,往往会听到一个新的概念:协程。这是一种更轻量化的并发处理模型,协程被广泛集成到诸如 Go、JavaScript、Rust 和 Python 等主流语言中,成为异步编程的核心工具。为了更好地理解协程,从而探究协程中可能存在的相关的安全问题,文章将简要介绍协程的起源与基本概念,然后通过分析协程模型的特点揭示潜在的安全风险,最后结合真实的漏洞案例,展示协程可能引发的安全隐患,并讨论如何更有效地规避这些问题。 由于内容较多,文章将分为上下两篇。在本文上篇中,会介绍协程的定义,包括常见的协程的用途以及协程在代码中的形式,接下来会给大家一个直观的感受,推断协程是如何诞生的。 二、什么是协程 关于协程的定义,这里直接摘自AI: 协程与传统函数调用的主要区别在于控制权的转移和状态的保存,这使得协程可以在中途挂起并在稍后恢复。 总结来看,协程具有如下的特征: 它不是一个由操作系统提供的特性,它是基于语言层面模拟的一个效果; 调度需要显示的声明起转换过程。 这里需要注意的是,协程于线程并非是同一个层级的概念,协程是一个用户态模拟的概念,而线程属于操作系统的原生概念。 通常,协程挂起的时候,会保存当前的上下文,包括栈堆以及寄存器等等。 由于协程非一个存在于操作系统的概念,所以对于操作系统而言,它并非真实存在,这就导致传统的调试器无法直接看到它,也就不存在直接观察它的办法。为了能对其有一个比较感性的认知,我们可以尝试先从编程语言中感受它的存在形式。在不同于语言中,协程的呈现形式也不同。比如在python中,协程的函数一般长成这样: import asyncio async def my_coroutine(): print("Start coroutine") await asyncio.sleep(1) print("End coroutine") async def main(): print("Start main") await my_coroutine() print("End main") ## Run the main function asyncio.run(main()) 其输出如下: Start main Start coroutine End coroutine End main 可以看到有几个关键点: 程序以异步的形式运行; 存在async/await关键字,被声明的函数才具有协程异步的能力; 需要使用asyncio.run将其包裹。 在协程使用更加广泛的go语言中,协程的代码如下所示: package main import ( "fmt" "time" ) func asyncTask(done chan string) { time.Sleep(2 * time....

2025年02月12日 · 6 分钟 · l1nk