使用 Intel Control-flow Enforcement Technology (CET) 加强 C 程序的安全性

简介

Intel 的 Control-flow Enforcement Technology (CET) 是一种旨在提高软件安全性的硬件级特性,用于防止控制流劫持攻击,如返回导向编程(ROP)和跳转导向编程(JOP)。CET 主要通过两种机制实现:间接分支跟踪(IBT)和阴影栈(Shadow Stack)。

CET 实现原理

  1. **间接分支跟踪 (IBT)**:要求所有间接跳转(如函数指针调用)必须跳转到用 ENDBRANCH 指令标记的目标。这阻止了通过篡改内存来创建恶意跳转的尝试。

  2. **阴影栈 (Shadow Stack)**:为每个线程独立存储返回地址的副本。当函数返回时,处理器会比较当前的返回地址和阴影栈中的地址。如果它们不匹配,处理器会阻止返回操作,防止ROP攻击。

示例代码

下面的示例展示了一个简单的C程序,其中包含函数指针的使用和尝试非法访问的场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

void safe_function() {
printf("Safe function called.\n");
}

void unsafe_function() {
printf("Unsafe function called.\n");
}

int main() {
// 正常的函数指针使用
void (*func_ptr)() = safe_function;
func_ptr();

// 尝试非法修改函数指针
// 在一个CET支持的环境中,这种修改会被检测并阻止
func_ptr = (void (*)())((char*)safe_function + 2);
func_ptr(); // 这里的调用在CET环境下应该会失败

return 0;
}

在这个例子中,func_ptr 最初指向 safe_function。然后,代码尝试将 func_ptr 修改为 safe_function 地址的偏移,模拟一个非法的控制流修改尝试。在启用了 CET 的环境中,这种尝试应该会导致程序崩溃或异常终止。

编译和运行

使用支持 CET 的 GCC 编译器,编译上述代码:

1
gcc -fcf-protection=full -o cet_demo cet_demo.c

然后运行编译出的程序:

1
./cet_demo

测试 CET

在支持 CET 的环境中,程序中的第二次 func_ptr 调用(非法调用)将会失败,因为它尝试执行一个没有用 ENDBRANCH 指令标记的地址。这正是 CET 防止控制流劫持攻击的方式。

结论

CET 是在深度防御策略中的一个重要层面,但它并不是万能的。良好的编程实践和其他安全措施仍然是必要的。CET 提供了一个硬件级别的保护层,使得控制流劫持攻击更加困难,从而提高了软件的整体安全性。

eBPF 软件应用市场设计方案

背景

eBPF(扩展伯克利数据包过滤器)是 Linux 内核的一项技术,它允许在内核空间运行一些预定义的、有限的程序,不需要修改内核代码或加载任何内核模块。由于其高效和灵活的特性,eBPF 被广泛应用于网络流量过滤、性能监控、安全和其他领域。然而,目前社区中的 eBPF 程序分发不够统一和规范,不同的组件和工具集都有自己的管理和打包方式,例如 cilium、bcc 和 openEuler 内核的 eBPF 插件。eBPF 程序也可能使用多种用户态语言开发(如 Go,Rust,C/C++,Python 脚本等),具有各种不同的接口,甚至并没有预先编译好的二进制程序,用户必须自行配置环境和编译才能使用。

这种分散和缺乏标准化的情况带来了一些问题:首先,eBPF 程序的升级和功能添加通常依赖于整体软件的发布,这可能导致升级周期过长,单个 eBPF 组件的发布需要等待整体软件的发布周期;其次,开发 eBPF 程序需要对内核 eBPF 程序框架有深入的理解,这增加了开发难度。因此,这个项目的目标是希望能借鉴 docker hub 的管理模式,提供一种统一的 eBPF 程序管理方式、openEuler 内核 eBPF 开发模板,以及一个编译和分发工具,以解决上述问题。

综上,整个 eBPF 生态缺少对新手友好的开发方案,和一个类似于 Github 或 Docker hub 的通用分发和托管平台。

项目产出要求

项目产出要求主要分为两个部分:

  1. 构建 openEuler 应用市场的基础设施,提供类似于 docker hub 的 eBPF 程序管理模式:这意味着我们需要创建一个可以公开存储、管理和分发 eBPF 程序的平台,就像 docker hub 对 docker 镜像所做的那样。这个平台应该允许开发者上传他们的 eBPF 程序,用户可以下载、安装和升级这些程序。此外,这个平台应该支持版本管理,以便用户可以选择安装特定版本的 eBPF 程序。
  2. 提供 openEuler eBPF 软件编写模板,简化编译和打包及分发流程:这意味着我们需要创建一个模板,来帮助开发者更容易地编写、编译、打包和分发他们的 eBPF 程序。这个模板应该包含基本的代码结构、编译和打包脚本,以及使用说明。这样,开发者只需要按照模板来编写他们的代码,然后使用脚本来编译、打包和分发他们的程序。对于初学者而言,提供一个模板也可以帮助更快速的上手进行开发工作。

需求分析

  1. 理解用户需求:项目的主要用户为两类:开发者和用户。开发者需要一个方便的平台来上传、管理和分发他们的 eBPF 程序,而用户需要一个便利的方式来搜索、下载、安装和更新 eBPF 程序。
  2. 功能需求定义
    • eBPF 程序存储和管理平台(网页前端):平台需要支持开发者上传 eBPF 程序,并提供版本控制功能。用户应能下载、安装和更新程序。此外,平台应具备良好的用户界面和易用性,同时提供搜索功能,以帮助用户找到所需的 eBPF 程序。
    • eBPF 软件编写模板:提供一个模板以帮助开发者更方便地编写、编译、打包和分发他们的 eBPF 程序。模板应包含基本的代码结构、编译和打包脚本,以及使用说明。同时,模板需要满足以下特性:可移植性、隔离性、跨语言支持和轻量级。
    • 包管理器:用户可以用一行命令就能下载、启动程序,无需配置环境或重新编译,或者一行命令创建新项目、打包发布项目。管理器需要提供清晰的文档,方便用户使用。
  3. 非功能需求定义
    • 性能:平台需要能够快速处理上传和下载请求,即使在高并发请求的情况下,性能也不应下降。
    • 安全性:所有上传和下载的 eBPF 程序都应保证安全性。
    • 稳定性:平台需要具备高可用性,确保用户在任何时候都能访问。
    • 兼容性:编写模板需要兼容多种用户态语言(如 C、Go、Rust、Java、TypeScript等),以适应不同开发者的需求。

打包发布格式与存储格式

打包发布格式与存储格式是项目的关键部分,因为它们决定了如何将 eBPF 程序打包、分发和存储。

OCI 镜像

OCI(Open Container Initiative)是一个开放的行业标准,旨在定义容器格式和运行时的规范,以确保所有容器运行时(如 Docker、containerd、CRI-O 等)之间的互操作性。OCI 规范主要包括两部分:

  1. 运行时规范(runtime-spec):定义了容器运行时的行为,包括如何执行容器以及容器应该满足哪些条件等。
  2. 镜像规范(image-spec):定义了容器镜像的格式,包括镜像的层次结构、配置、文件系统等。

OCI registry 则是用于存储和分发 OCI 镜像的服务。Docker Hub 和 Google Container Registry 都是 OCI registry 的例子。它们提供了一个公开的平台,用户可以在上面上传、存储和分发他们的容器镜像。

OCI 镜像格式主要由两部分组成:manifestlayers。Manifest 是一个 JSON 文件,描述了镜像的元数据,包括镜像的配置以及构成镜像的各个层。Layers 则是镜像的实际内容,每一层都是一个文件系统的增量变化。当运行一个 OCI 镜像时,这些层会被叠加在一起,形成一个统一的文件系统。

首先,我们来看一下 OCI 镜像的 manifest。Manifest 是一个 JSON 文件,它包含了镜像的元数据,例如镜像的配置和构成镜像的各个层。一个典型的 manifest 文件可能看起来像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"size": 7023,
"digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"size": 32654,
"digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
},
...
]
}

在这个例子中,config 字段指向了一个包含镜像配置的 JSON 文件的摘要(digest),而 layers 字段则是一个数组,包含了构成镜像的各个层的信息。每一层都有一个媒体类型(mediaType)、大小(size)和摘要(digest)。

然后,我们来看一下 OCI 镜像的 layers。每一层都是一个文件系统的增量变化,它包含了从上一层到当前层的所有文件和目录的添加、修改和删除。当运行一个 OCI 镜像时,这些层会被叠加在一起,形成一个统一的文件系统。

例如,假设我们有一个 OCI 镜像,它有两层。第一层添加了一个文件 /etc/passwd,第二层修改了这个文件。当我们运行这个镜像时,我们会看到第二层的修改,因为它覆盖了第一层的文件。

这就是 OCI 镜像格式的基本概念。通过使用 manifest 和 layers,我们可以创建非常复杂和灵活的镜像,满足各种不同的需求。

Docker 和 OCI

Docker 镜像和 OCI(Open Container Initiative)镜像在很大程度上是相同的,因为 OCI 镜像规范实际上就是从 Docker 镜像规范中派生出来的。

Docker 是容器技术的先驱,它定义了自己的容器和镜像格式。然而,随着容器技术的发展和其他容器运行时(如 rkt、containerd 等)的出现,业界开始寻求一种标准化的容器和镜像格式,以确保不同的容器运行时之间的互操作性。这就是 OCI 的由来。

OCI 是一个开放的行业标准,它定义了容器格式和运行时的规范。OCI 镜像规范就是基于 Docker 镜像规范创建的,它保留了 Docker 镜像的主要特性,如镜像的层次结构、镜像的分发和存储等,同时也添加了一些新的特性,如更严格的规范定义、更多的安全特性等。

因此,你可以把 OCI 镜像看作是 Docker 镜像的一个超集。实际上,大多数现代的容器运行时,包括 Docker 自己,都支持 OCI 镜像格式。这意味着你可以在 Docker 中运行 OCI 镜像,也可以在其他支持 OCI 的容器运行时中运行 Docker 镜像。

标准容器的五个原则

定义了一个名为标准容器的软件交付单元。标准容器的目标是将软件组件及其所有依赖项封装在一个自描述和可移植的格式中,以便任何符合标准的运行时都可以在不需要额外依赖的情况下运行它,而不受底层机器和容器内容的影响。

标准容器的规范定义了:

  1. 配置文件格式
  2. 一组标准操作
  3. 执行环境。

这与运输行业使用的物理运输容器有很大的类比。运输容器是交付的基本单位,它们可以被举起、堆放、锁定、装载、卸载和标记。通过标准化容器本身,无论其内容如何,都可以定义一组一致、更流畅和高效的流程。对于软件,标准容器通过成为软件包的基本标准交付单元,提供了类似的功能。

1. 标准操作

标准容器定义了一组标准操作。可以使用标准容器工具创建、启动和停止它们;使用标准文件系统工具复制和快照它们;使用标准网络工具下载和上传它们。

2. 不受内容限制

标准容器是不受内容限制的:所有标准操作的效果都是相同的,无论其内容如何。无论其包含一个postgres数据库,一个带有其依赖项和应用程序服务器的php应用程序,还是Java构建工件,它们都以相同的方式启动。

3. 不受基础设施限制

标准容器是不受基础设施限制的:它们可以在任何OCI支持的基础设施中运行。例如,标准容器可以捆绑在笔记本电脑上,上传到云存储,由弗吉尼亚州的构建服务器运行和快照,上传到在自制私有云集群中的10个分段服务器,然后发送到3个公共云区域的30个生产实例。

4. 自动化设计

标准容器是为自动化设计的:因为它们提供相同的标准操作,无论内容和基础设施如何,标准容器都非常适合自动化。事实上,你可以说自动化是它们的秘密武器。

许多曾经需要耗费时间和容易出错的人力的事情现在可以编程完成。在标准容器之前,当一个软件组件在生产环境中运行时,它已经由10个不同的人在10台不同的计算机上分别构建、配置、打包、文档化、修补、供应商化、模板化、微调和仪器化。生成失败,库冲突,镜像崩溃,便笺丢失,日志错位,集群更新半破。这个过程缓慢、效率低下、花费巨大,而且完全取决于语言和基础设施提供者。

5. 工业级交付

标准容器使工业级交付成为现实。利用上述所有属性,标准容器使大型和小型企业能够简化和自动化其软件交付流程。无论是内部devOps流程还是外部基于客户的软件交付机制,标准容器正在改变社区对软件打包和交付的思考方式。.

https://github.com/opencontainers/runtime-spec/blob/main/runtime.md

eBPF OCI

我们可以为特定的 eBPF 程序定制专门的 OCI 镜像格式,并且使用标准的 OCI registry 来同时存储和分发不同的镜像,例如通常的 Docker 镜像,仅内核态的 eBPF 应用,eBPF 平台插件等。

要添加新的 eBPF OCI 类型,我们需要定义一个新的 OCI 镜像格式,这个格式应该包含运行 eBPF 程序所需的所有文件和配置。例如,我们可以定义一个包含 eBPF 程序、加载程序以及相关配置的 OCI 镜像。然后,我们可以使用标准的 OCI 工具(如 Docker 或 Buildah)来创建、管理和分发这些镜像。

以下是一些可能的存储格式:

  1. 用户态 + 内核态:这种格式的镜像包含了运行 eBPF 程序所需的所有用户态和内核态组件。这可能包括 eBPF 程序本身、加载程序到内核的工具(如 bpftool 或 libbpf)、以及任何必要的用户态库或服务。这种格式的优点是它提供了一个完整的运行环境,用户无需安装任何额外的依赖项。然而,这也可能使得镜像变得相对较大。
  2. 仅内核态:这种格式的镜像只包含 eBPF 程序本身和加载程序到内核的工具。这种格式的优点是它非常轻量,适合于资源受限的环境。然而,用户可能需要手动安装和配置任何必要的用户态组件。

打包、运行时分类:

  1. 仅内核态;
  2. 传统的 Docker 镜像;
  3. 内核态 + 一些配置文件、Shell 脚本,需要转发和解压到不同的地方;

优缺点

使用 OCI 镜像作为打包发布格式与存储格式的优点包括:

  1. 标准化:OCI 镜像格式是一个开放的标准,被广泛接受和使用。使用 OCI 镜像格式可以使得 eBPF 程序的打包、分发和部署流程与现有的容器化应用流程保持一致,降低了用户的学习成本。
  2. 易于管理:OCI 镜像可以被存储在各种容器镜像仓库中,可以利用现有的容器镜像管理工具进行管理。
  3. 易于分发:OCI 镜像可以被轻松地推送到远程的镜像仓库中,用户可以从镜像仓库中拉取镜像,进行部署。
  4. 对于内核态的应用而言,设计一种新的 OCI 镜像格式非常轻量级。
  5. 安全性:OCI 镜像格式支持数字签名和加密,可以确保镜像的完整性和安全性。
  6. 灵活性:OCI 镜像格式允许使用者根据自己的需求选择使用哪个镜像,以及如何配置镜像。由于OCI 镜像格式是开放的和标准化的,容器厂商和开发者可以基于此进行扩展和创新。

然而,使用 OCI 镜像作为打包发布格式与存储格式也可能有一些缺点:

  1. 镜像大小:OCI 镜像可能会比其他格式的二进制打包更大,这可能会增加存储和传输的成本。
  2. 兼容性问题:虽然 OCI 镜像格式是一个开放的标准,但是不同的容器运行时可能对 OCI 镜像的支持程度不同,这可能会导致一些兼容性问题。

总的来说,使用 OCI 镜像作为打包发布格式与存储格式是一个值得考虑的方案,它可以提供一种标准化、易于管理和分发的方式来处理 eBPF 程序。

案例

目前 bumblebee 项目和 eunomia-bpf 项目都使用了 OCI 镜像来进行存储,分别有 1.1k Github star 和 300+ Gitub star.

  • bumblebee

项目 bumblebee 是一个用于创建、管理和发布 eBPF 程序的工具,它使用 OCI 镜像作为打包发布格式与存储格式。这个项目的特点包括:

  1. 提供了一种新的方式来打包、分发和部署 eBPF 程序,使得管理 eBPF 程序变得更加方便。
  2. 使用 OCI 镜像作为打包发布格式与存储格式,这使得 eBPF 程序可以像容器镜像一样被管理和分发。
  3. 提供了一套完整的工具链,包括创建、构建、推送和运行 eBPF 程序的工具。

它的 OCI 镜像定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[
{
"mediaType": "application/ebpf.oci.image.config.v1+json",
"digest": "sha256:d0a165298ae270c5644be8e9938036a3a7a5191f6be03286c40874d761c18abf",
"size": 15,
"annotations": {
"org.opencontainers.image.title": "config.json"
}
},
{
"mediaType": "application/ebpf.oci.image.program.v1+binary",
"digest": "sha256:5e82b945b59d03620fb360193753cbd08955e30a658dc51735a0fcbc2163d41c",
"size": 1043056,
"annotations": {
"org.opencontainers.image.title": "program.o"
}
}
]

参考:https://github.com/solo-io/bumblebee

  • eunomia-bpf

eunomia-bpf 也提供了类似的使用方式,使用 OCI 镜像从云端运行 eBPF 程序,不过使用的是 wasm 的 OCI 镜像格式:

Untitled

用户体验设计

eBPF 软件编写模板

为了降低开发者的入门门槛并提高开发效率,我们提供了一系列的 eBPF 项目模板。这些模板基于不同的编程语言和框架,包括 C 语言和 libbpf、Go 语言和 cilium/ebpf、Rust 语言和 libbpf-rs,以及 C 语言和 eunomia-bpf。开发者可以根据自己的需求和熟悉的语言选择合适的模板进行开发。

这是目前 eunomia-bpf 社区已经提供的内容,我们准备了一系列 GitHub 模板,以便您快速启动一个全新的eBPF项目。只需在GitHub上点击 Use this template 按钮,即可开始使用。

libbpf-starter-template 为例,这是一个基于 C 语言和 libbpf 框架的 eBPF 项目模板。它提供了一套完整的项目结构,包括源代码目录、头文件目录、构建脚本等,以及一份详细的 README 文档,帮助开发者快速理解和使用模板。

此外,这个模板还内置了 Dockerfile 和 GitHub Actions,支持容器化环境的构建和自动化的构建、测试和发布流程。这意味着开发者可以专注于 eBPF 程序的开发,而无需花费大量时间在环境配置和流程管理上。

所有的模板都托管在 GitHub 上,并开放源代码,遵循 Apache-2.0 许可证。开发者可以自由使用和修改这些模板,以适应自己的项目需求。

我们计划将这些模板进一步完善,并且适配 Gitee 的基础设施,并在 openEuler 仓库中发布,以便更多的开发者可以使用。我们相信,这些模板将大大提高 eBPF 程序的开发效率,推动 eBPF 生态的发展。

包管理器

我们将使用 Docker 或 OCI(Open Container Initiative)来打包和存储 eBPF Hub 的内容。这些内容将被存储在 OCI 镜像仓库中,用户可以通过命令行工具在本地一键拉取和使用。

角色 1:普通用户/user

考虑一个开发人员的用例,他想使用 eBPF 二进制文件或程序,但不知道如何或在哪里找到它。他可以直接运行以下命令:

1
$ ecli run ghcr.io/eunomia-bpf/opensnoop:latest

这将运行一个名为 “opensnoop” 的程序。如果本地没有这个程序,命令将从网络上的相应仓库下载它。用户也可以指定版本号,使用 HTTP API,或者指定本地路径来运行程序。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ecli install ghcr.io/eunomia-bpf/opensnoop:latest
--> cp xxx
--> mv yyy
$ ecli run ghcr.io/eunomia-bpf/opensnoop:latest
根据 config.json 去执行具体的运行时
--> docker run xxx
--> bee run xxx
--> ./run.sh xxxx
--> exporter xxxx
--> 裸机运行 ./aaa
$ ecli stop ghcr.io/eunomia-bpf/opensnoop:latest
--> exporter stop ....
$ ecli run ./opensnoop

用户还可以使用参数来运行程序,例如:

1
$ ecli run ghcr.io/eunomia-bpf/opensnoop:latest -h

“run” 命令实际上包含了 “pull” 命令。如果本地没有对应的 eBPF 文件,命令将从网络上下载它。如果本地有,命令将直接使用本地的文件。例如:

1
2
$ ecli pull ghcr.io/eunomia-bpf/sigsnoop:latest
$ ecli run ghcr.io/eunomia-bpf/sigsnoop:latest

用户可以切换源,例如从 GitHub 切换到 ecli 静态服务器

角色 2:通用 ebpf 数据文件发布者/ebpf developer

我们的第二个角色是一个开发人员,他想要创建一个通用的 eBPF 工程,并在任何机器和操作系统上分发和运行它。这对于命令行工具或者可以直接在 Shell 中运行的任何东西都很有用,也可以作为大型项目的插件使用。

开发人员可以使用以下命令来生成 ebpf 数据文件:

1
2
3
4
5
6
$ ecli init opensnoop
$ cd opensnoop
$ ls
$ ecli build
$ sudo ./ecli run opensnoop -h

开发人员还可以发布 ebpf 数据文件。他们可以使用以下命令来登录,构建,发布,和推送新的文件:

1
2
3
4
$ ecli login
$ ecli build ghcr.io/eunomia-bpf/sigsnoop:latest
$ ecli publish
$ ecli push ghcr.io/eunomia-bpf/sigsnoop:latest

eBPF 程序存储和管理平台(网页前端)

我们希望创建一个平台,该平台可用于公开存储、管理和分发 eBPF 程序,就像 Docker Hub 对 Docker 镜像所做的那样。

我们将采用流行的前端技术来建立一个网页平台,以更友好的方式提供相关的发布和检索服务。

页面内容(以 Docker 举例)

主页:

主页将展示一些特色的 eBPF 程序,以及最新或最受欢迎的 eBPF 程序,以及简单的 logo 和介绍。此外,主页还将提供一个搜索框,用户可以输入关键词来搜索他们需要的 eBPF 程序。

例如:

Untitled

项目详情页面:

项目详情页面将展示特定 eBPF 程序的详细信息,包括其描述、版本、作者、源代码链接等。此外,这个页面还将提供一个“下载”按钮,用户可以点击这个按钮来下载 eBPF 程序。

Untitled

搜索结果页面:

搜索结果页面将展示用户搜索关键词的结果。每个结果将包括 eBPF 程序的名称、简短描述和一个链接,该链接将指向相应的项目详情页面。

Untitled

关于页面:

关于页面将提供关于这个平台的信息,包括其目的、如何使用、联系信息等。

额外页面

用户个人页面:

用户个人页面将展示用户的个人信息,包括他们上传的 eBPF 程序、他们的收藏、他们的关注等。用户可以在这个页面管理他们的 eBPF 程序。

Untitled

系统设计方案:Serverless 架构

我们希望采用前后端分离的方案,将前后端代码分别部署到 Vercel。这种设计架构具有灵活性、可维护性和高性能,可以让前端和后端代码分别部署和维护,更快地修复和更新代码,同时减少整个开发流程的时间成本和维护成本。

Serverless 架构本身具有弹性扩展能力、依托于 Vercel 平台的高可用性、更快的部署时间、更好的安全性,不仅让平台本身能够应对突发的高负载请求,而且可以让开发人员更好地应对业务需求和变化,提高开发效率和应用程序的性能和可维护性。

Untitled

API 设计

实现简单的登录注册功能、以及上传、查找 eBPF 项目的功能

Untitled

Serverless SQL 数据库

可以使用 Serverless SQL 数据库 来存储元信息,例如 Vercel Postgres 作为数据库应用存储平台的关键信息。

Untitled

进度规划

主要分工为三个部分:

  1. 模板(已完成一部分内容)
  2. 命令行包管理器(目前已有一些 demo)
  3. 前端网页(正在开发)

综述阅读:运行时软件补丁(热更新/动态更新?) 分类、调查和未来方向

https://arxiv.org/pdf/2203.12132.pdf

运行时软件修补:分类、调查和未来方向

运行时软件补丁旨在最小化或消除服务停机时间、用户中断和潜在数据损失,在部署补丁时。由于现代软件系统具有高度的差异性和异构性,没有通用的解决方案可用或提出以在运行时部署和执行补丁。现有的运行时软件补丁解决方案关注特定的情况、场景、编程语言和操作系统。本文旨在识别、调查和综合最先进的运行时软件补丁方法,并概述目前未解决的挑战。它还提供了有关运行时修补方法的多个方面的见解,如修补规模、一般策略和责任。这项研究确定了七个粒度级别、两个关键策略提供了三个责任实体的概念模型,以及四个运行时修补解决方案的能力。通过对现有文献的分析,本研究还揭示了阻碍运行时修补更全面采用的开放问题。最后,它提出了几个需要更多研究人员和实践者关注的重要未来方向。

1 INTRODUCTION

现代软件不断发展和适应不断变化的用户需求和要求,这导致需要对现有软件代码应用各种变更。在传统的软件开发生命周期中,软件变更是在代码编译和部署步骤中实现的。这些变更范围从简单的错误修复到全面的软件重构。现代软件的复杂性不断增加,用户需求不断增长,导致频繁的软件修改以更新和补丁的形式提供。这种代码修改通常旨在改进或扩展现有软件功能以满足新的用户需求。改进不一定涉及核心软件系统功能,但可以专注于增强辅助属性,如安全或隐私。
补丁和更新之间的区别没有普遍接受的正式定义。然而,普遍理解是引入新功能的软件变更称为更新(或升级)。相反,修复现有错误或漏洞的小改动被称为补丁。或者,一些版本控制系统松散地将软件兼容性定义为补丁和更新之间的分界线。然而,即使是较小的错误修复补丁也可能破坏与先前版本的兼容性,使得基于兼容性的区分在实践中不足。无论使用的术语是什么,补丁和更新本质上都涉及从简单到相当复杂的代码修改。
在最简单的形式中,用更新版本替换先前运行的软件实例涉及停止旧的软件实例并启动新的实例。这样运行的软件实例可以是单个进程。

Web服务、虚拟机或容器。然而,根据软件变更的程度和类型,更新过程可能会导致长时间的软件服务停机,这可能会对终端用户的体验产生负面影响。从终端用户的角度来看,更新也可能被视为现有使用会话的中断或新会话建立的延迟。这种软件服务中断可能会在高度关键的环境中(如医疗或工业控制领域)造成严重的负面后果。此外,像大型数据中心或股票交易所这样的高负载利润导向环境可能会遭受直接的财务损失,即使是短暂的停机也可能如此。Gartner最近的一份报告表明,仅一小时的停机成本可能高达100万至500万美元。因此,开发了运行时软件修补(简称为运行时修补),也称为热修补、动态修补或在线修补。为了改善传统(也称为离线)修补,运行时软件修补旨在最小化或完全避免任何软件操作中断。运行时修补通常被认为是一种快速漏洞缓解技术。但是,与简单的传统修补(即源代码修补)相比,修补正在运行的软件会带来重大挑战。即必须实现保留现有的终端用户活动(以及与未来活动的兼容性),从人类输入到复杂的基于网络的通信会话,以实现有效的运行时修补。换句话说,软件的未来行为必须受应用程序修补的影响,当前执行的活动和使用的数据也需要相应地进行调整,以保持兼容性。在某些情况下,运行时修补会将代码注入易受攻击的程序以实现临时修复。这些临时修复旨在立即禁用或替换易受攻击的代码,以防止漏洞的利用。同时,开发人员可以实现和测试实际修复错误而不仅仅是禁用当前损坏的功能的适当更新。请注意,与修补批准、测试、签名和分发相关的其他非技术供应链要求可能会在实践中引入更多的显着修补采用延迟。采用运行时修补需要深入了解软件实现细节。例如,将现有的内部函数或对象转换为与更新的软件版本兼容需要深入了解对象格式、结构和位置。

然而,由于底层硬件、编译器、第三方库和操作系统(OS)的技术多样性,没有实现综合运行时修补机制。现有的运行时修补解决方案针对特定的领域或场景,例如物联网设备或数据中心规模的虚拟机和超级虚拟机[9-13]。这些解决方案通常专注于特定平台的挑战,例如资源受限的物联网设备固有的硬件限制。例如,即使在严格的存储限制下,存储软件的修补副本可能也是不可能的。现有的修补研究和评论没有提供定量确定修补规模的正式方法,因此在与修补和更新差异相关的术语方面存在空白。此外,由于现有的特定于平台的修补解决方案数量众多且多样化,缺乏对一般运行时修补策略和所需实现能力(如方法适用性和预期潜在干扰)的全面和正式的理解。此外,运行时修补中涉及的不同方的角色以及他们在各种修补过程步骤中的责任也没有得到明确考虑。

因此,我们从部署方面回顾软件运行时修补的最新文献,以了解现有的运行时修补技术和方法。基于分析,我们提出一个分类法(在第3节中讨论),它提供了运行时修补解决方案的全面视图。

针对补丁粒度(第4节)、补丁方法(第5节)和补丁责任(第6节)的视角,我们还确定了运行时补丁中的挑战和研究空白,并在第8节中强调了进一步改进这一领域的未来研究方向。
本研究的其余部分结构如下。第2节讨论了软件修补的一般情况,接着第3节进一步深入概述了运行时修补。第4节考虑了各种运行时补丁粒度。第5节介绍了现有运行时补丁部署策略、工作流程和功能的全面视图。第6节包含了有关补丁过程中涉及的各方及其相应角色和责任的详细讨论。第8节概述了解决已确定的开放挑战的进一步研究机会,第9节总结了本研究。

2 BACKGROUND

图1展示了一个通用的补丁管理流程。无论更改的程度如何,更新软件系统都可以在线或离线进行。如图1所示,补丁准备、提供补丁、应用补丁、测试补丁以及可能删除补丁(如果适用)是关键步骤。补丁准备的技术取决于原始源代码是否可用。源代码补丁本质上是独立的、逻辑上统一的代码编辑,用于改进或修复某些功能、漏洞或漏洞。现代代码版本控制系统通常跟踪这些编辑作为代码提交。完全访问应用程序源代码使得开发全面和灵活的更改成为可能。然而,基于源代码的修补程序的主要缺点是需要重新编译整个应用程序以包括所需的更改。根据软件包的大小,重新编译步骤本身可能需要相当长的时间。

Untitled

相比之下,缺乏源代码访问权限意味着必须直接修改二进制可执行文件。因此,基于二进制的补丁基本上包含需要在原始二进制可执行文件中更改的字节。与源代码修补相比,二进制修补通常需要更短的时间,因为不需要进行漫长的重新编译步骤。然而,在准备阶段,二进制修补面临一个额外的挑战。即,必须在原始二进制文件中定位要更改的字节的地址。虽然超出了本文的主要范围,但值得注意的是,在生产系统中发生的所有更改都需要进行回滚规划,这是至关重要的。通常假定当新行为与期望的更改不匹配时进行回滚。这种假设可能意味着某些停机时间是不可避免的。因此,在进行回滚时,仍然优先考虑保持运行。因此,从总体上看,回滚操作等同于运行时补丁。相反的方向(新代码返回旧代码)。在两个方向上都必须考虑相同的自洽性问题。关键区别在于,在应用补丁之前可以进行更长时间的准备工作,而不是可能的突然失败。通常,回滚过程高度依赖于应用的补丁类型,涉及的应用程序数据,预期的影响,时间限制和代码量。本次审查旨在仅关注补丁部署步骤、方法和解决方案。

2.1 Traditional Software Patching

传统或离线修补意味着停止运行的软件实例,可能转换现有数据并启动一个新实例[9、14]。如图1所示,软件修补的第一步是修补准备。准备好的补丁针对源或二进制级别,具体取决于源代码是否可用。面向源代码的补丁需要进行可能耗时的软件重新编译以生成新的(修补后的)可执行文件,而面向二进制的补丁则修改现有的软件可执行文件。源代码级别的修补允许程序员轻松修改软件的任意方面,例如替换任何函数、指令和数据流。另一方面,二进制补丁在其本质上有些受限,并且主要用于较小规模的修改(主要在安全上下文中)。二进制修补的主要复杂性在于任何代码长度变化都可能导致重要的内存指针失效。此外,在二进制级别的修补中,找到要修改的错误部分的确切位置可能会很复杂[15]。此外,旧的二进制文件仍然可以运行,进一步复杂化了修补过程。此外,由于新修补的二进制文件已准备好执行,运行它可能会因资源共享而导致冲突,因为旧的二进制文件使用相同的资源。

传统补丁的挑战:传统软件补丁方法的主要缺陷是旧软件停止并且新软件未完全启动时会出现的服务中断[1,14]。例如,当补丁软件系统停止时,远程或本地用户会话、网络连接和数据处理会中断或暂停。在交互式用户会话中,短暂的中断可能是可以接受的(尽管不方便),例如网页浏览或文字处理。交互式软件通常被设计成自动保存和恢复用户会话,以在某种程度上减轻这种不便。试图在重新启动之间恢复状态的Web浏览器就是这种缓解的很好的例子。相比之下,对于高度关键的系统,在服务中断可能导致重大经济损失或完全不可接受的情况下,经历服务中断是不可取的。例如,对于生命支持软件和空中交通管制,禁止关闭系统或不被视为选项[14]。因此,系统管理员可能选择延迟补丁部署,以避免可能导致服务中断和应用程序状态丢失的系统重新启动。例如,更新用于高度交互活动(如在线游戏或视频流)的服务器操作系统通常需要安排服务器停机时间。在此期间,玩家必须停止游戏,等待服务器更新和重新启动,然后登录服务器,并可能从头开始游戏,这对玩家来说很麻烦。不幸的是,保持一个容易受攻击的系统未修补一段时间会增加被攻击的风险。为了克服传统修补的这些挑战,运行时修补已经出现并获得了广泛的认可。运行时修补的详细信息和相关优点将在第3节中讨论。

2.2 Existing reviews

近年来进行了几项与软件补丁和更新相关的评估[16-22]。Ahmed等人[17]对运行时软件更新解决方案进行了全面的系统映射研究。在分析大量论文时,我们遇到了一些不太结构化的方法分类。例如,最常引用的更新方法包括“Java VM”和“多版本”,它们不一定是互斥的。此外,尽管该研究提供了使用方法的统计数据和概述,但缺乏有关特定方法的好处和挑战以及技术、工具和算法采用之间的相关性的详细信息。 Seifzadeh等人[16]提出了一种更为结构化的运行时软件更新解决方案分类。该研究提供了一套全面的、高层次的运行时更新评估指标,例如范围、更新时间和类型安全。 Miedes等人[18]没有专注于现有实现,而是概述了一般运行时软件更新中使用的概念和技术。此外,还确定和讨论了一组目标和要求,例如服务连续性和普遍性。然而,该研究未提供这些方法的连贯分类或分类,导致一些分类不匹配。例如,尽管在目标方面是正交的,但Java导向的方法和可回滚性与技术挑战并列讨论。此外,一些评论关注特定的技术领域和使用场景。例如,Lopez等人[19]已调查了现有的功能和系统调用挂钩方法。虽然功能挂钩可用于多种目的(包括恶意),但应用挂钩技术可以显着提高功能级补丁的效果。此次调查的主要优点是综合考虑了主要操作系统下的功能和系统调用挂钩。然而,此次评论的范围仅限于功能粒度,未考虑其他补丁级别。 Gregersen等人[21]比较了三种现有的Java应用程序运行时修补实现。除了评估实现的性能外,还分析了低级修补能力。评论的范围仅限于Java特定功能,例如考虑了类修改。最后,Mugarza等人[22]关注了工业物联网领域的运行时软件更新。具体而言,通过核控制系统案例研究评估了安全系统的要求。与现有评论不同,Ilvonen等人[20]采取了一种有趣的视角,通过分析支持运行时或动态软件更新(DSU)在软件工程教育上下文中的情况。特别地,分析了现有的软件工程课程,以确定DSU概念的采用和覆盖范围。该研究的主要发现是,在教育中缺乏对DSU的整体方法,只有某些个体方面得到了关注。

根据审查研究,得出了几点观察结果。首先,在运行时修补领域,甚至在术语层面仍然缺乏共同理解。其次,迄今为止还没有清晰的分类法来协调现有的运行时修补方法。第三,缺乏通用性阻碍了现有的运行时软件修补方法和工具的更广泛采用。第四,评估指标因所需运行时修补领域而异,从通用时间开销到特定语言的类修改。

我们的调查范围:我们的调查重点关注运行时修补方法的多个方面,例如修补规模、通用策略和责任。我们提供了详细的分类法,并伴随着现有解决方案的相应分析。主要关注点在于修补管理生命周期中的应用修补阶段(如图1所示)。与现有调查不同的是,它们通常是狭窄的,主要强调用于修补的方法和工具,我们试图概括不同修补粒度级别固有的问题和方法。值得注意的是,我们的调查不比较不同技术或解决方案的性能,因为观察到的实验设置和执行环境差异巨大。同样,由于评估技术的高度多样性,不同实现侧重于不同的度量标准(例如停机时间、开销和长期修补连续性),因此不同的评估技术不能直接进行比较。自然而然地,旨在实现零停机时间的系统在其评估程序中甚至不会考虑这种度量标准。此外,我们指出现有先进解决方案的共同挑战,并提出一组未来发展方向。

3 RUNTIME PATCHING

运行时软件补丁旨在更新给定软件系统,同时保留运行进程和会话。如果不可避免地需要停机,运行时补丁方法侧重于最小化中断时间。周等人[3]将运行时补丁定义为“一种动态更新软件的方法,有效降低了软件升级常常伴随的停机时间和不便”。与传统的补丁方法相比,运行时补丁主要是二进制导向的,因为程序的运行二进制实例是在内存中修改的。代码的二进制表示需要在运行时用新的(即修补的)版本替换[3,23]。除了修补二进制的内存版本外,还必须相应地修补磁盘副本,以便在任何将来的重启中修补的行为保持持久。

运行时补丁必须注意需要转换为与新代码兼容的当前状态[24-26],其中包括内存中的对象、数据结构和外部操作系统资源。一组现有的方法涉及各种运行状态转换的方面,例如更新点和状态转换器[7,8,27]。在这些方法中,补丁由新代码、安全更新点和必要的状态转换器组成。更新点本质上是适用于应用运行时补丁的时间窗口。特定于数据的状态转换器可以将当前程序状态转换为新版本。具体而言,运行时补丁系统会不断监视程序执行。当程序达到适当的更新点时,系统会加载修补代码,并开始根据指定的状态转换器进行程序状态转换。一旦转换完成,程序执行将继续进行,新版本将处于活动状态[24]。

在严重情况下,通常是对于大型和复杂系统,状态转换可能需要相当长的时间,引起明显的服务中断。例如,将现有虚拟机(VM)转换为与新的超级监视器版本兼容可能涉及到转换虚拟磁盘格式。考虑到现代磁盘的多个GB大小,这个操作可能需要相当长的时间。此外,在转换过程中不停止VM可能会导致磁盘内容在新磁盘映像最终确定之前发生变化。在这种情况下,运行时补丁可以选择分步执行转换,并仅在最后一个数据部分转换期间停止VM。

Untitled

我们注意到,在考虑需要修补什么时,调查研究所涉及的问题和提出的方法是不同的。因此,我们通过将相关的运行时修补研究按照目标修补的细粒度分组,提取出七个粒度级别。需要注意的是,仅有少数论文被归类为多个子类别的情况,即某些研究提出的方法可以在多个细粒度级别上进行修补。

第二和第三类别侧重于“如何”方面的修补以及采用的一般技术(修补策略)和给定修补系统实现的技术能力(在第5节中讨论)。我们确定了两种主要策略:状态转换和共存与衰减。在某些情况下,当修补所带来的变化不大时,直接进行状态转换是可能的。例如,添加一个对象属性或方法以反映新的(修补后的)功能可能很简单。相反,删除或修改现有函数可能会导致依赖先前行为的其他代码片段发生问题。因此,共存和衰减方法旨在根据会话或事务将旧数据对象和新数据对象分开,如果可能的话。换句话说,在使用中的旧(未修补的)对象不会被修改,而新的代码修补将针对更新的对象。稍后,在预先存在的会话/事务完成时,旧对象将被处理掉。实现能力直接反映了给定解决方案的实际适用性。例如,某些系统可能能够自动安全地检测更新时间窗口,而其他缺乏这种能力的系统必须依靠开发人员的手动协助。同样,能力较弱的系统可能需要额外的外部帮助来管理内存,以将修补后的代码加载或适应到RAM中。

在第四个类别中,我们已经确定了涉及的实体及其职责,这些职责是由修补系统考虑的。具体来说,我们关注的是“谁”负责应用补丁。现有的研究有三种常见的职责目标观点。首先,供应商支持的修补系统意味着原始软件系统的开发人员负责修补过程。其次,软件系统的最终用户需要对正在运行的系统进行修补。最后,独立的第三方修补程序尝试提供设施,以将通用补丁应用于现有的通用软件(在某些限制内)。这些情况在修补程序可用的先前知识量上存在差异。最重要的是,开发人员自然会访问原始软件源代码,而最终用户则不会。同样,原始开发人员会拥有更深刻的应用内部和逻辑知识。研究可以以其他方式进行分类;然而,我们的主要关注点是确定补丁应用的级别以及应用补丁的采用或建议方法。这有助于我们确定从业者忽视的运行时修补方法的问题,并确定未来潜在的有益方向。

4 RUNTIME PATCHING GRANULARITIES

本节介绍了运行时补丁中旨在描述要应用的补丁的规模和边界的预期补丁粒度。补丁粒度定义了一个责任边界,在此边界内通常不需要或执行状态变换。我们确定的研究被分类为七个组:指令级别、函数级别、进程级别、容器级别、虚拟机级别、超级监视器级别和内核级别。图3提供了在不同粒度下涉及到的对象或元素的概述。虽然大多数简单的补丁是针对在RAM中更改单个机器指令,但是复杂的补丁则是针对替换易受攻击的VM为固定VM。例如,图3显示,补丁指令需要获取内存指针,而补丁主机操作系统则需要修改物理硬件状态。将研究按补丁粒度分类可以洞察到应用补丁时需要修改的对象和元素。它进一步

Untitled

帮助理解补丁的影响(即更改/补丁后果的范围)。例如,更改较低层中的对象,例如物理硬件状态,会影响较高层对象,例如文件系统和操作系统对象。补丁准备和部署过程高度依赖于补丁粒度。例如,替换几个CPU指令仅涉及将相应的代码编译成目标CPU架构,而更新VM可能需要重建整个VM镜像。与每个粒度相关的技术问题在下面讨论。除非对整体功能行为产生重大影响,否则修补单个指令只需要考虑指令抽象级别。同样,用修补过的变量替换函数需要关注函数抽象级别,除非整个过程行为的变化在外部可观察。相比之下,转换到新内核需要处理正在使用旧内核资源的所有进程。选择适当的补丁粒度实质上是在内部状态和外部状态之间进行权衡。如图3所示,集合边界以外的连接点(例如网络套接字、外部函数调用和硬件)可能需要调整以匹配补丁代码。

4.1 Instruction-level Patching

指令级修补需要修补解决方案将更改的指令写入适当的内存位置。它旨在替换机器级CPU命令或RAM中的单个字节[2、15、31]。我们已经确定了一组已执行指令级修补的研究[2、7-9、15、31]。根据给定软件的源代码可用性,指令级修补的修补准备略有不同。在两种情况下,修补准备输出是要写入某个内存地址的字节序列(可能是动态计算的)。指令级修补比函数级修补更轻量级和更快速[9]。表1总结了提出指令导向运行时修补方法的研究,并确定了每个研究的目标以及与修补相关的任务,例如修补生成、部署、验证和恢复。通过目标,我们考虑研究的重点——例如,解决软件更新、修复错误、漏洞或修补特定攻击(如缓冲区溢出)的解决方案。表1还显示了修补任务是否完全自动化,需要人类手动输入来触发自动化任务(即半自动化),还是完全依赖于人类。在大多数指令级修补解决方案中,修补部署后会自动进行修补生成[2、7、15、31]。我们进一步确定了修补的范围。由于技术和语言的变化,修补解决方案不是通用的;因此,特定研究的范围显示了所提出的方法是针对Linux、特定语言(如C)还是云系统。从表1中,我们可以看到大多数指令级修补方法都是自动化的,并跨越多个系统和应用领域。

Untitled

4.2 Function-level Patching

功能级补丁相对于指令级补丁更为粗略,因为它的目标是修补整个函数[4,34]。例如,它用一个修补过的函数替换有错误的函数以消除漏洞。一系列研究已经提出了进行功能级补丁[4,9,24,25,33-37]。进行功能级补丁需要考虑跨函数依赖关系。此外,长期存在的函数提出了进一步的挑战,因为必须确定合适的修补时间。与指令级补丁相比,定位修补目标地址的步骤并没有太大的区别。表2总结了与功能级补丁相关的研究。它显示了大多数功能级补丁研究,在修补部署之前执行修补生成。与指令级修补类似,这些研究的范围涵盖不同的领域、操作系统和应用程序。

一些运行时的功能级修补方法是为安全补丁而设计的[4,34,35,40]。例如,段安等人[35]提出了OSSPatcher,自动识别易受攻击的函数,生成二进制补丁并在运行时执行补丁注入。同样,李等人[34]提出了一个Appwrapper工具包,可以在每个方法(即函数)的基础上注入额外的安全代码到不安全的应用程序中,从而实现使用动态策略提高整体应用程序安全性。还可以通过将整个易受攻击的函数链接到内核中的新函数或替换代码来执行功能级修补。这种方法被认为对于被多个用户高度利用的大型服务器环境非常有用[4]。还有研究通过代码传输和函数的二进制重写来修补二进制程序的漏洞[40]。表2显示,大多数功能级修补是自动执行的,不需要用户干预。

Untitled

Untitled

Untitled

7 DISCUSSION

高性能和内存开销主要定义了运行时补丁的可行性,而不是传统的离线补丁。这些开销是由于现代复杂软件固有的执行状态的规模和确定安全更新点的困难所导致的。在医疗、军事或救援行动等关键任务领域,维持不间断的服务运营至关重要。资源丰富的高度关键的系统可以容忍运行时补丁系统常见的高性能或内存开销。然而,资源受限的系统如嵌入式物联网设备可能需要更高效的补丁解决方案,具有较低的性能或内存开销。我们对审查的研究的分析提示,供应商提供的补丁具有降低相关开销的更大潜力。对软件内部的深入了解以及软件的协作性质,简化了许多修补步骤。协作性质可以指请求控制的代码执行暂停,而不是突然的代码中断或漫长的等待静止状态。类似地,现有的旧代码可以协作提供正在使用的资源列表,而不需要复杂的手动资源跟踪。

根据我们提出的分类法,我们还得出结论,运行时补丁在开源生态系统中占主导地位,大多数补丁框架设计用于独立第三方使用,而不是软件供应商和消费者直接使用(如表6所示)。这可能解释了软件供应商没有足够关注运行时补丁功能。中间人角色似乎正在形成,专注于服务连续性。云基础设施是一个典型的例子,其中服务连续性不是软件供应商向终端用户提供的单独解决方案,而是由云服务提供商集成到云提供的服务中。访问源代码显著简化了应用程序内部的理解,使得可以开发适用于给定应用程序的高效运行时补丁解决方案。不将补丁解决方案限制在特定的供应商或最终用户也有助于补丁系统的普及度,因为它具有更广泛的适用性。

我们观察到一种常见的商业化趋势,即运行时修补解决方案通常不通过对修补系统本身进行货币化,而是将实际的修补作为付费订阅的一部分提供。例如,现有的商业解决方案,如TuxCare的KernelCare9、Oracle的Ksplice10和Red Hat的kpatch11,提供可以在运行时应用的修补程序。客户基本上支付适应和支持常规源级更改的运行时修补格式。请注意,某些类型的更改,例如语义结构修改或特定系统函数替换,可能会被某些修补解决方案明确不支持12,13。
.
我们进一步观察到,尽管这些专注于Linux内核补丁的商业解决方案取得了成功,但实际上内部使用的粒度不一定是内核级别,而可以在指令或函数级别上实现(表1-4)。值得注意的是,我们将Ksplice归类为函数级别的粒度。因此,根据补丁设计选择,可能需要实现不同的功能,如表5所示。例如,Ksplice选择不实现执行代码路径分派功能,同时支持我们的分类法所识别的其他三种补丁实施能力。

闭源商业运行时修补解决方案主要由相应的软件供应商开发、控制和管理。这是因为只有供应商才能访问软件源代码,这显着简化了补丁的开发和准备工作。因此,这种供应商策划的修补解决方案与软件紧密集成,不会单独提供给更广泛的受众。例如,微软采用了一种运行时修补解决方案,以最小化 Azure SQL 数据库引擎服务在补丁期间的中断14。根据他们的统计数据,超过80%的 SQL 错误可以使用这个修补系统得到纠正。根据我们的分类法,这种方法是一种功能级共存和衰退解决方案。尽管与特定的软件应用程序密切相关,由于补丁加载和函数重定向步骤的普及,类似的编译器辅助方法也可以用于其他基于 Visual C 的应用程序。我们发现的另一个商业解决方案,即 MuleSoft CloudHub 平台15,主要关注维护持续的云服务。由于封闭性,所提供的修补解决方案说明在实现细节上很少;然而,根据我们的分类法,它属于过程级共存和衰退解决方案。

请注意,虽然Python或JavaScript等某些动态编程语言提供了各种动态代码加载机制,但它们并不直接映射到实际的补丁内容。开发人员仍必须准备代码,通过编写将加载并应用的代码片段来进行必要的状态转换。尽管如此,动态代码处理的进一步发展是实现高效运行时修补系统的有希望的道路。

8 运行时修补的开放挑战和未来研究方向

根据进行的回顾,可以提出一些关于现有知识和努力中存在的差距的观察。简而言之,我们注意到,尽管进行了大量的研究和开发工作,但运行时补丁仍未得到充分利用。例如,最近一个有趣的Log4J漏洞被提议通过利用漏洞本身来修补。此外,尽管各个软件应用程序使用了各种运行时修补解决方案,但缺乏通用解决方案表明,在这个领域有重要的研究潜力,特别是开发编译器和操作系统内核增强方法。我们确定了许多未解决的问题和潜在的未来方向,这些问题和方向在图8中显示,并在下面的小节中讨论。首先,大多数现有解决方案的适用范围狭窄,且适用性严重受限。例如,运行时修补解决方案可能采用特定的语言或执行平台或操作系统特性,限制了这种解决方案的实用性。或者,只支持特定类型的更改(例如添加类方法),这对于非平凡的修改提出了挑战。

Untitled

其次,运行时补丁缺乏系统化方法的原因可能是当前实践中补丁开发的临时性质所导致。与已经成熟的众多开发实践相比,传统的软件开发已经被充分建立。补丁开发看起来不那么有条理。这是因为由于相关的安全风险,补丁通常是在紧迫的时间压力下开发的。高调的漏洞可能会导致巨额的经济损失,从而促使尽快部署补丁。这留给开发人员很少或没有时间系统地处理任务,导致补丁很快被补丁(有时超过一次)跟随,以修复最初的补丁被发现有漏洞的情况17,18。
.
最后,与传统的源代码级别修补相比,运行时修补能力往往被视为事后补救。
运行时补丁甚至可能不是独立开发的,而是从开发人员提供的源代码更改自动生成的。
这导致合作修补解决方案短缺,无法通过现有代码库提供各种程度的协助来简化进一步修补。
开发合作更新子系统需要在设计和开发阶段进行额外的努力。因此,在实践中并不常用。
此外,预见所有可能的修补和资源移交场景可能是不可行的,从而使这样的实现在长期内不够灵活。

8.1 Behaviour Change Compatibility

为了确保用户体验的连续性,必须维护机上活动与更新代码之间的兼容性。引入显著外部可观测变化的修补程序可能会导致与外部观察者的预期不兼容。因此,运行时修补系统的任务之一是实现代码和数据的兼容性,或者至少在补丁部署之前检测潜在的不兼容性。处理原始代码和补丁代码之间的潜在不兼容性需要确定不兼容性的范围(即补丁效果的可观测范围)和类型(即哪些方面变得不兼容)。例如,如果检测到某些不兼容性,补丁应用可能会被完全停止或推迟。检测最具破坏性的补丁可以帮助减少相关停机时间和服务中断。考虑到行为变化兼容性的上述问题,我们进一步讨论未来的研究方向,这些方向可能有助于解决原始代码和补丁代码之间不同方面的这些问题。

8.1.1 外部依赖跟踪。确定潜在的不兼容性在哪里需要跟踪代码的各个外部依赖项,这些依赖项也需要更新。例如,如果一个函数级别的补丁修改了函数接受的参数,那么调用该函数的所有外部代码片段都需要相应地进行更新。此外,需要递归分析依赖于调用修补函数的代码的进一步代码片段,以必要的方式传播更改。类似地,修补网络服务应用程序可能需要更新远程客户端软件以保留网络级服务兼容性。这个过程类似于常用于跟踪不受信任的用户输入流的递归污染跟踪,以识别和纠正不安全的输入使用[60]。一般来说,旨在安全的补丁试图最小化大幅改变代码行为。然而,可以认为,对于紧密集成的软件,即使是微小的补丁(函数或指令级别),也可能导致严重的副作用。因此,在运行时修补之前,了解补丁影响将可见的地方是关键的初步步骤。因此,研究和开发量化补丁影响评估技术将有助于检测潜在的外部不兼容性。还可以使用进一步的补丁影响评估技术来评估给定补丁的潜在破坏性。这种复杂度度量将使得可以正式验证给定补丁的影响。因此,可以立即应用不影响最终用户的简单补丁,而应用复杂和破坏性补丁则需要计划停机时间。准确地说,将估计外部可观察的软件行为变化,以确定给定补丁的影响。鉴于软件隔离(通过容器化和虚拟化)的趋势增加,需要特别注意可观察到的网络相关补丁副作用,即使在远程网络节点上也是如此。

8.1.2 代码和数据语义不一致性检测。当开发人员创建新的代码片段时,可能会无意中引入某些不一致性。简单的不一致性,如数据类型不匹配,可以通过维护类型安全性的方法自动检测。现有的解决方案通常试图通过跟踪变量数据类型的更改来实现类型安全。相反,语义差异(例如相同类型的变量的含义发生了变化)可能无法仅通过查看更改的代码片段来发现。例如,将整数变量更改为字符串变量可以被检测并相应地纠正[41]。然而,考虑一个不改变变量类型(即整数仍然是整数)但以不同的方式解释变量值的补丁(例如把米转成英尺)。通常,测量单位处理容易出现这种错误,同一距离/角度/重量变量可以用不同的方式解释(例如米与英尺,度与弧度,磅与千克等)。此外,执行从一种测量单位到另一种测量单位的转换的补丁可能会引入更多的错误。

将系统从一个转换到另一个可能不会导致类型不匹配错误(即违反类型安全),但仍可能在执行链的下游引起问题。检测这种语义变化无疑更加复杂,因为必须分析所有依赖于修补程序范围之外的变量的相关代码路径。不幸的是,迄今为止还没有提出可行的解决方案来检测语义不一致。也许类似于 𝜇DSU [61] 的元语言可以被调整以允许概述语义应用层。

8.2 Patch Deployment Overheads

除了技术实现问题之外,必须解决运行时修补的开销问题。这些开销主要集中在资源(例如RAM)和时间领域。基于共存的解决方案意味着必须同时在内存中存在多个代码或数据副本。这种共存对于较小的补丁可能不是问题;然而,在较长时间内应用多个补丁可能最终会带来挑战。同样,受资源限制的物联网设备也可能由于缺乏硬件资源而使一些解决方案不可行。例如,一些基于哈佛架构的微芯片可能不允许直接修改代码,导致需要重新刷新主存内容并重新启动执行。请注意,即使在这种受限制的设备中,某些扩展也已实施以帮助自修改,这对于修补目的是有用的19。必须预测架构、设备和资源限制的开销,以确定给定系统实时要求的修补适用性。我们在下面确定并讨论相关的定量度量研究方向。

8.3 Socio-technical Challenges

除了与运行时修补相关的纯技术问题外,几个社会技术方面也可能带来额外的复杂性。例如,由于内部政策或立法的实施而导致的延迟可能会显著减缓修补程序。组织可以考虑在设计阶段纳入运行时修补的软件开发实践。然而,根据改进的开发实践重写大型遗留软件包所需的货币和时间成本可能会使软件开发人员望而却步。此外,在某些情况下,供应链参与者(例如移动服务提供商或硬件供应商)可能会为了修补验证或安全分发而引入额外的复杂性。我们确定了一些潜在有益的研究方向,以解决阻碍运行时修补更广泛采用的社会技术挑战。

9 CONCLUSION

运行时修补技术和方法需要在安全和关键任务系统采用新兴技术后进行彻底的转型。在运行时应用补丁的关键步骤是最小化开销和停机时间。我们提出了一个分类法,突出了需要考虑的四个关键方面,以进行运行时补丁部署。我们识别和分析了七个补丁粒度级别,以及两种常规的补丁策略(状态转换和共存和衰减)和三个负责实体(供应商、消费者和第三方)。我们进一步定义了一个高层次的工作流程(图5),以支持四个关键实现能力,即(i)内存管理,(ii)资源转换,(iii)安全执行状态确定和(iv)执行路径分派。最后,本文回顾了挑战并提出了潜在的未来前景。我们认为,这个领域主要需要一种系统的方法来开发实用和方便的补丁解决方案,适用于各种基础设施、编程语言和执行环境。该领域还需要定义可量化的指标,以通过语义和依赖跟踪解决方案来确定补丁的影响。我们观察到许多需要供应商、最终用户和第三方考虑的开放机会和问题。

未来的研究方向包括通过实际的现场研究获得与修补相关的实际数据。通过分析这些数据,可以了解各个关键领域中现有修补挑战的统计数据。值得注意的研究问题应该集中在实践中常用的补丁程序、补丁粒度分布、应用的补丁复杂程度、补丁引起的系统停机时间和服务连续性,以及潜在的不兼容问题。总之,深入了解从业者和业务要求的问题和关注点对于弥合运行时修补研究与实际实现/部署之间的差距至关重要。

本文来自 eunomia-bpf 社区,我们正在探索 eBPF 和 WebAssembly 相互结合的工具链和运行时: https://github.com/eunomia-bpf/eunomia-bpf 社区关注于简化 eBPF 程序的编写、分发和动态加载流程,以及探索 eBPF 和 Wasm 相结合的工具链、运行时和运用场景等技术。

综述阅读:安全实践中的形式化方法

A Survey of Practical Formal Methods for Security (acm.org)

在今天的世界中,关键基础设施通常由计算机系统控制。这引入了新的网络攻击风险,可能会破坏这些系统的安全性并破坏其功能。因此,有必要建立具有强大的网络攻击抵御保证的系统。实现这种保证的一种方法是使用形式化验证,它提供了系统符合所需网络安全属性的证明。本文回顾了在网络安全和安全关键系统方面使用形式化方法(FM)的情况。我们将FM分为三个主要类别:定理证明、模型检查和轻量级FM。为了比较FM的不同用途,我们定义了一组共同的术语。我们进一步根据应用FM的计算机系统类型来开发类别。介绍、讨论、比较和总结了每个类别中的解决方案。我们描述了历史亮点和发展,并从FM从业者和研究人员的角度介绍了FM在网络安全领域的最新研究现状。通过考虑所有类型的FM、几种类型的安全关键系统以及相应的分类结构,实现了全面的FM概述。因此,本文为安全关键系统的系统设计人员提供了FM和可用技术的综合概述,简化了选择正确工具的过程。本文总结了检讨的讨论,重点关注最佳实践、挑战、一般未来趋势和研究方向。

1 INTRODUCTION

数字服务目前正向社会各个方面扩展[207]。这反过来导致社会对支持这些服务所需的网络基础设施的依赖性。对网络基础设施的沉重依赖带来了新的挑战,形式是网络攻击和潜在的网络恐怖主义[142],威胁行为者涵盖了从个人犯罪分子、网络犯罪分子和“黑客活动分子”到资源充沛的国家行为者[208]的整个范围。金融、工业或日常消费服务的干扰可能导致显著的财务和社会成本。随着数字化的进一步传播,潜在的攻击面只会变得更大,增加了保护数字服务的挑战[250, 260]。随着系统越来越大、越来越复杂,必须投入大量资源来保护这些系统免受已知网络攻击的攻击。通常,保护机制被纳入以关闭成功网络攻击后发现的漏洞,因此具有反应性质。这种方法将网络安全从系统内要解决的主要挑战降低到了一个事后的考虑[236]。

由于网络攻击的广泛性质,很难直接量化它们对社会的影响[101],然而,它们往往涉及显著的财务成本以及可能的生活质量受损。一个例子是潜在的针对电力基础设施的网络攻击,包括电力市场,这可能导致发电机的损坏和机密数据的泄露[191]。另一个例子是针对制造设施的攻击,导致生产延迟或质量降低[47, 204]。这些例子表明,网络威胁应被视为对社会基础设施的物理威胁一样重要。

越早发现新系统中潜在的网络安全威胁,这些威胁的缓解成本就越低[257]。形式方法(FM)提供了在系统生命周期的所有阶段发现和缓解网络威胁的机会。使用FM为网络安全保证领域带来了数学严谨性。这是可能的,因为FM是使用基于模型的方法的技术,其中的模型被严格指定[261],允许开发关于所研究的系统应该做什么的精确语句,而不会对如何做出任何约束[264]。这些模型代表所讨论系统的软件、硬件或二者的组合。使用FM的主要好处来自系统设计内部一致性的数学证明[115]。这种证明提供了强大的保证,因为它考虑了整个系统的行为,一旦证明为真,它就保持为真,而在传统的测试中,只能覆盖特定的场景。FM可以被视为提供数字社会网络安全保证的工具[262]。除了保证系统行为的正确性之外,采用全面的正式方法还可以减少实现错误的数量,这些错误是攻击的基本组成部分。

需要注意的是,存在各种各样的形式方法。我们考虑的主要类别是:

  • 定理证明,基于计算机证明分析形式描述的重要属性。
  • 模型检查,以详尽的方式检查系统的有限状态模型是否满足给定的规范。
  • 轻量级形式方法,使用形式化技术对系统进行静态或动态分析(这个概念是在参考文献[132]中提出的,但我们从其描述中提取了模型检查并将其归为了另一类)。

有关形式方法在安全领域的技术基础的详细介绍,请参阅参考文献[32]。该章节重点介绍该领域的技术基础,而我们的调查则集中于特定的应用领域。 在所有情况下,这些方法都被用来确定系统是否以正确的方式运行,许多方法都已经获得了重要的工具支持以自动化验证和验证过程[10]。在本次调查中,我们考虑所有方法及其在数字社会特定领域的应用。我们进一步将形式方法应用于系统行为的特定抽象级别,从应用级别到硬件级别。通过考虑形式验证在这些维度上的最新研究,我们提供了一个非详尽的概述,以特定学科的形式方法应用为例。本次调查的目的是允许从业者识别适用于其领域系统的经过验证的方法,从而希望增加形式方法在网络安全领域的应用。我们认为,在不同的形式方法应用领域进行类似的调查可能会成为增加应用的催化剂,因为已经确定,教育和经验以及找到形式方法的正面应用示例可能会导致专业人员将形式方法应用于他们的专业领域[104]。

1.1 Methodology

在应用形式化方法(FM)解决网络安全挑战的领域内,研究出版物的数量是相当可观的。因此,我们对于本次调查中所考虑的研究出版物做出了几项限制。首先是研究的新颖性,考虑到过去十年的情况,我们将出版日期限制在2012年之后。此外,所有的研究工作都需要在科学场合,如期刊、会议或研讨会上发表。下一个限制是专注于计算机工具支持的FM,即只有能够提供计算机分析并经常引导用户进行此分析的工具支持FM才被考虑。这一限制是为了更专注于FM的应用,将形式化安全分析的好处带给工业界。这与我们专注于应用形式化方法有关,在寻找研究出版物时,我们只考虑了使用工具支持FM来处理具体网络安全问题的研究出版物。因此,本次调查不专注于FM在安全领域的理论进展或仅简要提到FM使用的提议流程,如模型检查算法的理论方法、超属性规范等。此外,本次调查不涉及通常被称为可证明安全的安全方法。这是一种数学方法,用于分析加密机制或系统的安全性。该方法考虑攻击者模型下的系统,并将安全要求表达为一个对攻击者应该能够实现的限制。证明是通过建立攻击者需要解决一个已知的困难问题(例如二次残余问题[105])才能破坏系统安全的事实来完成的。因此,系统的安全性降至基础难题的难度。该方法通常用于密码学领域而非安全系统,因此不在我们调查的范围内。我们指向参考文献[28],提供了关于密码学中FM的报告。最后,我们将我们的搜索限制在明确考虑安全方面,而不是作为安全或正确性的副产品的研究。我们使用Google学术进行跨库搜索,重点关注研究论文,排除研究摘要或扩展摘要。

由于本调查为读者提供了研究的快速概述,我们进一步决定将不同的研究出版物按其关注的行业(领域)以及FM使用的抽象级别进行分类。通过这种方式,研究人员和潜在的工业用户可以快速找到他们感兴趣的领域。此外,我们根据发现的研究论文和现有文献的启示,将研究分类为基于网络安全问题分类。[208]。

1.2 History

本章介绍了过去40年中,在安全领域中使用形式化方法进行研究的重要案例历史。我们选择了四个案例研究,其中使用了形式化方法来保护系统的安全性:

(1) Needham-Schroeder公钥协议。Lowe使用了一个细化模型检查器来发现该协议的三角攻击。这是对先前由Burrows等人[52]证明正确的协议的新攻击。

(2) Mondex智能卡。这是第一个获得ITSEC E6级认证的商业产品。当时有很多讨论,关于是否可能实现这一点。

(3) Tokeneer ID站。人们对使用FM实现更高保证级别所需的严格性的可行性提出了类似的问题。Tokeneer解决了这个问题。

(4) seL4微内核。该系统被认为是世界上最可靠的微内核。显然,它证明了安全和使用形式化方法不会导致性能下降。

Needham-Schroeder公钥协议。Needham-Schroeder公钥协议是一种在网络设备之间进行通信的传输层协议[190],为网络中的两个方提供相互认证。该协议在图1(a)中进行了可视化。它简单而广为人知,已成为测试安全协议验证技术的流行基准。我们在这里讨论它,因为它是一个重要的安全协议,但其中包含了一个重大错误。这个错误是通过形式化建模和分析发现的。

Lowe [165]表明,与其意图相反,该协议无法确保认证。特别是,他证明了入侵者可以在协议运行期间冒充代理A。冒充者欺骗另一个代理B,使其认为他们正在与A交谈。该协议使用公钥密码学。每个代理A都拥有一个公钥,任何其他代理都可以从服务器获取该公钥。A还拥有一个密钥,它是其公钥的逆。

任何代理人都可以使用A的公钥加密消息,但只有A可以解密它,确保保密性。 该协议还使用随机数:为协议的单次运行造币的随机数字。 Lowe使用CSP [123]对协议进行编码,并通过CSP的模型检查器FDR [103]进行分析。Lowe没有直接指示FDR在协议中寻找漏洞-他只是对入侵者的能力进行了建模,但是模型检查器进行的详尽搜索发现了攻击,尽管如此。 假设我(入侵者)是一个可以参与网络会话的网络用户。我还可以拦截消息并注入新消息,但无法在没有密钥的情况下解密消息。如果我发明随机数或已经理解消息的内容,则可以产生新消息。即使不理解内容,此入侵者也可以重播完整的加密消息[213]。这种方法通常称为Dolev-Yao模型[85]。 攻击涉及协议的两次同时运行,如图1(b)所示。A与I建立有效会话。同时,我冒充A与B建立虚假会话。协议的错误运行可以解释如下:A向I发送一个带有随机数NA的消息,I使用I的秘密密钥解密该消息。 I向B中继消息,假装B正在通信。 B以A加密的方式回复NB,因此I将此加密的随机数中继给A。 A解密NB并向I确认它,I得知它。 I重新加密NB并将其返回给B,这使B相信A是另一方。攻击结束时,B错误地认为A是通信伙伴,只有A和B知道NA和NB。这表明协议是不安全的。协议分析师称这是中间人攻击。在这里,它已被自动发现。

Mondex。Mondex应用程序包括具有电子钱包(钱包)的智能卡,用于电子商务[235]。客户使用Mondex智能卡进行低价值、类似现金的交易,不需要第三方参与。英格兰银行(在这种情况下是金融监管机构)认为Mondex的要求是安全关键的:Mondex必须没有任何实现或设计漏洞,可以允许电子伪造。因此,开发人员将Mondex证明为当时最高标准的ITSEC E6级[129],相当于通用标准评估保证级别7[57]。Mondex是第一个实现ITSEC E6(EAL7)的商业产品。 参考文献[235]进一步描述了Mondex应用程序的开发,以及其抽象和具体模型。抽象模型描述了电子钱包的世界:原子交易交换价值,抽象模型表达了它们所需的安全性质。具体模型是钱包设计和价值交换的消息协议。 设计团队使用Z符号[233, 263]指定了这两个模型。他们证明了具体模型在形式上是抽象模型的细化。这意味着具体模型遵守所有抽象安全要求。抽象模型及其安全性质通常比具体模型更容易理解。开发人员编写了手动证明,认为没有有效的自动化工具可以完成这样的大任务。相反,证明步骤使用fuzz2和Formaliser工具[97]进行类型检查。证明还得到了独立的外部评估人员的检查。

有四个主要的安全属性: • 系统及其用户不得创造价值。 • 系统必须记账所有价值。 • 钱包必须具有其预期交易所需的足够价值。 • 所有转移必须在真实的钱包之间进行。 设计团队在证明发现漏洞后更改了辅助协议。 该项目的详细说明在参考文献[265]中给出。 Mondex已被证明是一个可靠的安全系统,由其正式开发保证。

Tokeneer。Tokeneer系统是由美国国家安全局(NSA)[30]设计和开发的。它提供安全访问一个工作站的区域,并控制物理进入。访问控制需要生物识别检查和安全令牌。这些令牌描述了用户在访问该区域时的允许操作。 开发人员需要确保安全性质。他们通过符合通用标准评估保障等级5 [57]来实现这一点。他们还需要展示他们可以以一种具有成本效益的方式来做到这一点。NSA邀请使用FM来开发Tokeneer系统的一个组件,并监控这个实验以测量执行开发所需的工作量和技能。 Praxis(一家英国公司)赢得了合同,并用Z [233, 263]编写了一个正式的规范,正式细化规范到一个SPARK程序。SPARK是带有附加工具集的Ada的子集[29]。他们证明了关键系统属性和运行时错误的缺失,使用传统方法开发额外的软件。这些额外的Ada程序提供了与外围设备的接口。 该项目需要260人天,三人兼职,以及九个月的经过时间。它产生了约10K行SPARK代码,约有16.5K个合同。在实施阶段平均每天写作约200行代码,整个项目大约40行代码。另外还产生了3.5K行标准Ada代码,实施阶段平均每天约200行代码,整个项目约90行代码。系统测试耗时约占项目工作量的4%,远小于通常的测试。 在Tokeneer中发现了两个缺陷。一个是通过形式分析发现的,另一个是通过代码检查发现的。测试团队发现了两个范围内的故障:用户手册中缺少的项目。 NSA设置的任务是符合通用标准评估保障等级5。Tokeneer开发实际上在几个方面超过了EAL5的要求:配置控制,故障管理和测试。尽管核心开发工作的主体是以EAL5为基础进行的,但涵盖规范,设计,实施和通信的开发领域是以EAL6和EAL7完成的。为什么?因为这样更便宜!

seL4微内核。第三代微内核seL4提供虚拟地址空间、线程和进程间通信的抽象。它提供明确的内存管理模型和授权能力。保证ARM版本的seL4微内核的二进制代码是正确的实现。seL4符合其抽象规范,不做其他事情。特别是,seL4 ARM二进制符合完整性和机密性的经典安全属性。 seL4微内核的C代码已经通过其抽象规范进行了正式证明。此证明在Isabelle/HOL中进行了机器检查。这假定启动代码、缓存管理、硬件和手写的汇编代码的正确性。 开发人员声称seL4是唯一经过验证的通用操作系统(OS)内核。系统的操作模型形成一个抽象规范。Haskell程序原型化内核。这个原型提供了自动转换到Isabelle/HOL。Isabelle代码然后是内核的可执行的、设计级别的规范。这是手工编写的C代码,形成了高性能的C实现seL4。细化证明链接规范和C代码。开发者证明攻击者不能破坏内核,即使他们使用错误的编码、虚假的调用或缓冲区溢出攻击。

1.3 Definitions/background

在本文中,我们提供了一组通用术语和定义。这非常重要,因为形式验证和安全领域在很多年内独立发展,因此一些术语被过载并且具有稍微不同的含义,取决于它们在何种上下文中使用。例如,在安全领域(特别是密码学中),证书是指用于将实体绑定到加密密钥的文档。然而,在FM中,证书被用作系统或协议正确性的证明。

在本调查中,对于安全方面,我们使用身份验证一词来指识别和验证访问系统的用户(实体或个人)是否是用户声称的人。这与授权相反,后者是基于用户身份允许用户访问系统的过程。此外,我们经常发现安全协议旨在提供特定的属性,例如隔离,这是一种设计原则,其中进程被分离并被赋予对共享资源(例如共享内存)的特权访问(通常使用容器化或虚拟化等技术)。或者不可否认性,确保无法否认语句或消息的有效性,特别是其作者。我们还想指出匿名性和保密性之间的区别,匿名性指用户或进程的身份不得透露,而保密性指编码为数据块的内容不得透露。

在形式方法方面,可以说所考虑的系统通常包括一组协调的进程,这些进程是定义一组指令的程序实例,由一个或多个线程执行。我们认为进程是系统中的主动实体,而程序是被动实体。描述进程行为的形式框架包括CSP,CCS,ACP,π演算等。进程通常实现协议,即一组数据传输规则,并且可以在共享内存上进行同步或通过通道进行通信,通道是物理通信网络的抽象。由于使用了中间处理器缓存,共享内存实现变得越来越复杂,可能实现许多不同的一致性模型[6]。同样,可以对通道进行许多不同的假设,例如消息的FIFO排序;通道是否保证完整性、可用性和保密性;通道是否无误;是否可以区分消息类型等等。

一般来说,验证是针对规范的,规范是实体(例如硬件、系统、计算机程序、数据结构等)允许行为的抽象(形式或非形式)描述。正式验证通常是针对系统模型进行的,该模型提供了实体的精确正式描述,捕捉被建模实体的关键特性。必须确保模型描述的每个特征都是实体的实际特征。可能会开发相同实体的不同模型,这取决于所关心的属性;例如,计算机程序可以通过前/后状态之间的关系、状态轨迹、输入和输出之间的函数等进行建模。模型可能描述行为功能、协议等。在形式方法中,通常在多个抽象级别上开发模型,并精确定义这些级别之间的关系。安全性的形式方法还需要攻击者的模型,例如在通信系统环境中使用的 Dolev-Yao 模型。

在硬件验证中,术语“协同验证”用于证明系统软件在底层硬件设计的表示上正确执行。这使得软件与硬件集成,而不需要任何物理设备(例如芯片或板)可用。验证的目的是确保它符合其实现(规范)的要求,即实体的物理表现形式。在某些情况下,可以将实现称为模型,该模型提供有关实体的足够细节,以便可以轻松地获得相应的物理实体。

为了最终确定FM的定义,重温我们对FM的分类很重要。我们分类的第一类是定理证明,其中提供了系统正确性的证明,采用符号逻辑。本调查重点关注自动定理证明,其中利用证明助手生成证明。该方法利用一个逻辑系统,试图确定语句ϕ是否从一组语句Γ = {ψ1,…,ψn}中得出。我们考虑的第二类是模型检查,其中利用一个系统的有限状态模型系统地搜索此模型中包含的所有可能系统状态(即模型中的所有可能系统状态)以便检测有关系统的语句是否存在反例,例如在线性时间逻辑中表示为p的属性,表示属性p在无限多个状态中成立。与定理证明一样,我们重点关注自动模型检查。最后,我们定义了一类轻量级形式方法,其中放置了不提供详尽分析的方法。一个简单的例子可能是静态代码分析,其中分析代码时不运行以确定是否违反预定义的规则。另一个例子可能是VDM [38],其中使用形式建模语言对系统进行建模,并将属性表示为合同,例如inv t == t.issueTime < t.expirationTime,表示通常必须在t到期之前发出t。为了执行检查,模型被动画化,仅设置特定场景(通常是被认为是关键的场景),但是,VDM也可以利用组合测试结合输入路径生成大量测试,从而显着增加测试覆盖率。

2 SURVEY

2.1 分类和概述

由于安全领域中的形式化方法应用于许多领域,我们根据领域和抽象级别进行分类,以提供广泛的安全领域FM的系统概述。我们选择的四个领域的标签如下:

金融(第2.2节):聚合了在金融/货币领域应用FM的工作,例如支付系统、家庭银行、金融市场、加密货币等。例如移动银行应用程序,ATM基础设施,FIX股票交易协议,智能卡/硬件钱包等。

工业(第2.3节):该标签聚集了处理应用于生产商品或服务、制造和工业控制的计算系统的工作。例如水处理管理面板,PLC控制网络,Modbus/TCP,电机控制器等。

消费者(第2.4节):这个标签分类了关注终端用户/个人个人计算设备和应用程序(如命令行shell、家庭操作系统、VoIP协议和运动智能家电)的安全性的作品。

企业(第2.5节):这是消费者类别的对应,用于分组关注提供计算服务以满足组织需求而不是个人的企业系统的安全性的作品。例如电子邮件服务,电子政务系统,sn2协议,数据服务器仓库等。

由于仅展示四个领域只会将FM在安全领域的研究按应用领域分开,我们进一步介绍了五个抽象级别,这些级别是进行形式化验证的级别:

应用:用于将FM应用于计算应用程序或计算目的的作品的级别。

系统:用于将FM应用于架构级别的作品,通常涵盖多个子系统。

协议:用于应用FM以断言属性或分析系统组件之间的通信协议的级别。

实现:这是一个横向类别,包括所有专注于直接在结果系统上应用/使用FM(例如运行时监控)而不是强调设计和规范的作品。

硬件:用于对在硬件开发过程中应用FM的作品进行分类。

这种分类使我们能够系统地审查最新的研究成果,并基于此提供概述。为了清晰地概述安全领域中的FM,我们进一步应用了第三个维度,定义所使用的FM类型,即模型检查、定理证明和轻量级FM。这提供了不同研究工作的数量概述,如图2所示。

此调查中的各个部分按照逻辑顺序组织,其中研究工作根据应用程序、系统、协议、实现或硬件类型进行分组。在此分组中,研究工作进一步按照逻辑分类组织成段落。例如,第一个段落可能会考虑与制造相关的工作,而下一个段落则会考虑与工业控制相关的工作。在这两种情况下,研究旨在工业领域,但具有不同的范围。调查中的每个部分都代表一个单独的领域,我们在不同抽象级别上提供了研究工作的系统总结。由于此调查试图对大量的研究工作进行分类,因此每个研究工作仅简要介绍,重点关注分析的问题和使用的FM技术。有关单个研究工作的更多详细信息,请参阅本调查基础的技术报告[149]。

消费者物联网系统,例如智能家居设备存在安全漏洞,并已经通过FM得到广泛验证。参考文献[153]的作者使用AVISPA中的模型检查工具和BAN逻辑来验证一个框架,确保智能家居环境中的匿名、身份验证和完整性。在参考文献[182]中,作者开发了IoTRiskAnalyzer工具,用于帮助工程师应用最适合的安全策略。这是通过使用马尔可夫决策过程[202]、将风险属性形式化为概率CTL公式,并使用PRISM模型检查器[156]进行验证实现的。汽车制造商也利用连接设备,特别是智能手机。例如,参考文献[53]的作者开发了基于智能手机的防盗装置,使用ProVerif针对Dolev-Yao攻击模型进行正式验证,以确保安全要求的强大保证。

2.5 Enterprise

企业和大型公司计算是大型国际企业的支柱。近年来,在企业计算中利用云解决方案的趋势仍然存在,同时仍然经常在本地数据中心运行。这些数据中心和云集群被用于众多企业任务,例如协作平台的虚拟化、公司管理和企业门户网站的托管。本节概述了利用FM解决企业计算安全挑战的情况,包括安全数据存储、虚拟化和软件定义网络安全、使用硬件令牌的强身份验证。由于企业是更大的实体,变化通常较慢,需要进行良好的管理。为此,FM已被用作企业云采用的助推器,因为已经提出了几种基于FM的解决方案,可以使企业从本地数据中心安全切换到联合云解决方案。 与之前的章节类似,模型检查是企业计算中形式化安全分析中最常用的工具。定理证明也不落后,尤其是在分析企业服务器中的可信平台模块芯片方面。由于轻量级FM通常作为软件开发环境的插件提供,使它们易于访问,因此在实现层面上广泛使用。企业部分中涉及的网络安全主题如图6所示。

Application.

企业应用程序通常处理和存储对组织至关重要的数据。如今,这些数据通过软件定义网络(SDN)传输。参考文献[226]的作者创建了一个验证平台,用于利用SDN的应用程序,该平台包括一个建模语言,可以自动翻译并分派到PRISM、SPIN和Alloy模型检查器。任何反例都将显示在工具中。与SDN类似,面向服务的体系结构(SOAs)经常在企业应用程序中使用,增加了这些应用程序的互连性。在参考文献[20]中,作者提出了一个安全平台

利用正式的规范语言和几个模型检查器(CL-Atse和OFMC)对SOA进行评估。作者发现了Google Apps中SAML-SSO集成的问题。企业数据通常存储在大型关系数据库中。在这种情况下,参考文献[61]的作者提出了一种安全外包数据库到不受信任的服务器的方法,建立在可验证数据库[36]的概念上,并利用定理证明来证明他们的方法是安全的。虚拟化是云中经常使用的另一种技术。参考文献[222]的作者介绍了一种针对Xen hypervisor安全的正式分析方案,包括模型检查和静态分析,成功地重新发现了已知的漏洞。最后,我们想指出参考文献[72]中的工作,描述了一家领先的云提供商如何利用FM保障其服务的安全性,指出FM的好处对于他们的客户至关重要。

System.

现今,大型企业可以自行托管基础架构,充分利用云服务,或部分地将其基础架构与云服务结合,导致联合云系统[183]的出现。参考文献[270]的作者们提出了一种利用CPN和CPN工具[137]分析联合云行为的方法,并创建了几个模型用于安全分析。同样,参考文献[256]的作者们使用Z与Z/EVES定理证明器对数据交换系统进行了机密性和完整性属性的形式化分析,同时利用域理论[39]生成了测试。在参考文献[135]中,作者们提出了一种基于移动环境[55]和不干扰的带盒环境演算[51]的防火墙规则和云拓扑分析的形式化方法。由于云计算通常是利用共享资源构建的,因此参考文献[169]的作者们建立了一个离线框架,用于形式化分析网络隔离属性,确保共享资源之间的隔离。这是通过使用一阶逻辑和约束满足求解器Sugar [240]来完成的。同样,参考文献[173]的作者们提出了一个云复杂性管理(代理)系统[113]的安全框架,利用Z/EVES定理证明器在NIST [43]云参考架构中分析了几个云安全属性。

此外,在参考文献[232]中,作者们提出了一个代理解决方案,用于自动将云服务与客户进行配对,同时管理云复杂性。代理的一个重要部分是找到一个满足客户安全要求的服务,使用基于一阶关系逻辑[130]定义的KODKOD有限模型查找器[244]。此外,还有一些研究考虑了云系统内的虚拟化。例如,参考文献[117]的作者们提出了一种在虚拟化系统中分析安全性和信任的形式化框架,将硬件和软件模型结合在一起,用CSP#[238]表达,并分派给PAT模型检查器,发现了一个现实世界云系统中微妙的错误。同样,参考文献[42]的作者们提出了一个安全子系统,用于在与安全策略相关的虚拟化基础架构中进行变更分析,利用图形和图形转换分派给GROOVE模型检查器[102]。

协议。

企业计算正向云平台转型,需要安全通信协议,并受益于FM分析。Amazon云服务[13]使用s2n[14],TLS协议的开源实现,利用FM证明其正确性。例如,在参考文献[64]中,作者们证明协议使用的基于哈希的消息认证码(HMAC)是不可区分于随机生成器的,使用Cryptol规范语言[89]描述HMAC,然后将其分派给Coq定理证明器,并通过软件分析工作台[100]将结果与实现相连接。在微软云中,参考文献[136]的作者开发了一种网络协议分析工具,利用Z3 SMT求解器来协助数据中心内的网络策略维护任务,为Azure云服务提供了重要的安全工具。由于云服务通常是远程访问的,参考文献[151]的作者利用Alloy分析器来查找SAML协议[125]中的漏洞。如今,云平台从小型IoT设备[185]收集数据,促使参考文献[141]的作者提出了一种轻量级双向认证协议,并使用OFMC和CL-AtSe验证其对多种攻击的抵抗能力。类似地,参考文献[212]的作者提出了一个移动设备认证方案,并使用ProVerif进行验证。IoT设备可以利用5G网络,参考文献[33]的作者使用Tamarin查找认证子协议问题,参考文献[8]的作者通过用CSP表达认证框架协议[269]和移动以太网协议[128]的方式进行分析,并将其分派给FDR模型检查器以评估相互认证属性。

实现。

企业计算通常由使用不同技术实现的许多应用程序组成。例如,参考文献[195]的作者创建了一个用于PHP插件的静态代码分析工具phpSafe,然后利用它发现了多个PHP插件中的580多个漏洞。类似地,参考文献[267]的作者创建了一个利用不变式分析[116]进行恶意行为检测的工具,指出在多个Web应用程序中逻辑漏洞的高效性。

虚拟化是企业计算的重要组成部分。为了实现虚拟化的安全性,参考文献[252]的作者创建了一个基于行为契约的安全验证虚拟化框架,并使用FRAMA-C[225]对行为契约进行静态分析。类似地,参考文献[251]的作者创建了一个虚拟化框架,用于在单个客户虚拟化程序中验证内存完整性,利用CBMC模型检查器[67]自动分析大部分代码库。

硬件。企业计算需要大量的云硬件基础设施和数据保密性、计算安全性等保障。客户通常将云提供商视为不受信任的实体,因为管理员本身可能构成威胁。

3 FUTURE OUTLOOK

此次调查概述了FM在多个领域中的安全应用情况。根据这些领域内的研究,可以预见在某些情况下FM的使用将加速,而在其他情况下,FM的使用将以较缓慢的速度增加。然而,一般来说,采用FM的趋势是增加的。在金融领域的情况中,FM的使用显然伴随着新的金融技术,如加密货币和智能合约。这种采用可以在专门针对智能合约领域的调查中看到[119]。金融领域中移动应用程序的使用也促使对通过使用FM可以提供高安全性保证的需求。最后,随着加密货币的兴起,金融领域内的硬件正在专门用于促进交易。预计在未来,这种硬件的安全性将继续得到形式化的审查,并且涵盖范围和复杂性将不断增加。

工业领域在网络安全领域面临着自己独特的一组挑战。随着自动化复杂性和数字技术在关键工业设施中的使用增加,预计FM将发挥至关重要的作用。这种趋势已经在2015年由[148]提出,他们调查了工业控制系统安全性和安全性的方法,包括非正式方法。到目前为止,该领域内的大多数工作都是反应性的,即现有系统和协议的分析。然而,在调查中,有几项工作显示了一种趋势,即在新的工业设施和协议的设计过程中早期利用FM。工业领域内另一个新兴趋势是将形式验证工具与软件开发流程集成,这主要表现在机器人应用和PLC代码方面。随着机器人应用和底层硬件的复杂性不断增加,预计FM在未来将发挥重要作用。

消费计算领域是一个快速发展的领域。消费者的趋势变化很快,然而,已经提出了相当多的工作来利用FM 恶意软件保护[37],甚至创造了经过正式验证的互联网浏览器。此外,从计算机到智能手机的计算转移带来了新的安全挑战。在这个领域,已经将很多关注点放在了Android操作系统权限系统的分析上。这个领域显示出研究量的增加,并且只要移动操作系统被数百万用户使用,形式验证就会加速,可能导致流行的移动操作系统的完全形式安全验证。消费类硬件,如智能手机正在使用TPM,保护移动计算。此外,在这个领域,FM的使用正在增加,特别是为了确保安全TPM区域的属性。 最后,企业计算领域揭示了几个有趣的趋势。其中之一是使用FM来针对虚拟化超级管理程序,分析现有虚拟环境的安全属性以及利用该知识构建完全形式验证的超级管理程序。另一个重要的趋势是主要云计算提供商对其产品的安全性进行形式验证的重大投资。为此,不仅应用了现有的工具,而且云提供商已经转向开发自己的FM工具。这两个趋势都预计不仅会继续,而且由于云计算和虚拟化的日益普及而加速。

安全威胁[217]。在这方面,[219]的作者创建了一个云隔离系统,将用户数据与管理员分开,并限制管理员对用户虚拟机的操作,利用硬件模块,作者命名为Trusted Cloud Module(TCM),为云管理员提供有限的接口,管理加密密钥,并为用户提供安全存储。该模块由现成的硬件组件构建,使用Scyther验证工具。同样,任何可信计算的基础是提供安全存储和计算环境的可信平台模块(TPM)协处理器。不幸的是,使用TPM的平台的安全性经常没有正式验证,导致漏洞[50]。为了缓解这一问题,[27]的作者提出了TRUSTFOUND,这是一个形式建模框架,用于利用Trusted CSP#,CSP#的扩展和LS2[77]进行模型检查,其中使用PAT模型检查器进行验证,并检测到六个隐含的假设和两个严重的逻辑缺陷。

有时,企业使用小型一次性密码(OTP)生成硬件来为用户提供强认证以访问云服务[45]。其中一种设备是Yubikey,一种USB OTP生成器。在[154]中,作者已正式分析了Yubikey OTP的安全性以及硬件安全模块(HSM)的安全性。在这个领域的另一个挑战是解决CPU侧信道攻击。其中一个攻击是时间信道攻击,在共享环境中,攻击者(可能是虚拟机)可以确定另一个虚拟机执行的算法。为了解决这个问题,[92]的作者提出了Timing Compartments,这是一种在硬件中实现的隔离方案,可隔离共享资源的各方之间的时间信息。该方案通过使用SecVerilog进行信息流分析进行了检查。

4 CONCLUSIONS

超过30年前,Burrows等人发表了他们关于安全协议分析的BAN逻辑的开创性工作[52]。他们的工作并不是完全正式的,被证明可以批准危险的协议。尽管如此,他们表明他们的逻辑在揭示各种微妙的安全缺陷和缺陷方面表现良好,特别是在认证协议中。他们着手回答五个问题: (1)这个协议是否有效? (2)它能够工作吗? (3)这个协议到底实现了什么? (4)这个协议是否比另一个协议需要更多的假设? (5)这个协议是否做了任何不必要的事情?

他们的重要论文启发了一代安全研究人员使用FM来设计和分析安全协议以及回答类似的问题。此外,在描述形式验证在软件工程中的好处时,Dijkstra曾经引用过著名的话:“测试只能证明漏洞存在,而不能证明漏洞不存在。” 50多年后,在计算机安全领域中,我们现在可能已经有足够的证据来声称“形式方法只能证明安全漏洞的存在,而不能证明不存在” [54]。也就是说,虽然FM提供了严格的工具和技术来证明安全漏洞的不存在,但这种严谨性是有条件的:只有在安全漏洞被记录和规定在一个正式的框架内时,才能进行证明。目前的局限性意味着,FM无法发现任何新的漏洞,因为我们可能并没有在寻找它们。

例如最近的Spectre和Meltdown攻击[145]。像许多漏洞一样,这两个攻击已被证明存在于使用推测执行的处理器范围内,并且缓解这些漏洞需要软件方面的干预。然而,尽管这些漏洞的普遍性,但攻击本身并没有通过形式验证发现,而是通过一系列关于微架构组件的训练和定时的实验。幸运的是,一旦发现了安全漏洞,即使是像Spectre和Meltdown这样复杂的攻击也可以得到正式的描述和隔离[59]。一旦这样做了,下一步就是一个形式的框架来推理这些问题,然后是更简化的工具来扩展验证到更大规模的系统。因此,对于在安全领域继续研究FM实际应用的重要性,同样重要的是通过研究该学科的理论方面来扩展我们在安全领域中FM的推理能力。如果没有这样做,就有可能出现一种新的安全漏洞,它超出了当今逻辑的范畴。例如,Spectre和Meltdown的变体可以通过子集闭合超属性[59]来捕获。因此,如果没有关于超属性[68]的现有作品和随后关于它们验证的作品[59、106、107],FM的规范和使用来保护Spectre和Meltdown将会更加困难。

至于不同FM技术的利用,我们发现模型检查通常是分析更广泛系统的实践者的选择。从调查的作品中可以看出,一个趋势是,由于大量的工具支持、可以选择的建模语言的丰富性、能够进行详尽分析以及专门针对安全分析的模型检查器的存在[200],模型检查通常被认为是一个好方法。我们可以确定,即使在模型检查会因问题规模而受到影响的问题上,作者也会将问题范围缩小,专门针对系统或应用程序的关键部分进行分析。在某些情况下,由于问题的规模过大或问题的适用性,FM实践者选择利用定理证明。虽然工具支持不及模型检查,学习曲线更陡峭,但定理证明提供了对会使模型检查器不堪重负的系统的详尽分析。然而,我们观察到,定理证明需要变得更加可访问,以增加其利用率。最后,轻量级FM的特殊类别提供了一个有趣的进入FM世界的入口。一方面,无法进行详尽分析可能被视为缺点,但另一方面,可以主张轻量级FM正在工程世界中变得普及。例如,静态代码分析器不仅在软件构建工具链中得到使用,而且经常直接集成到软件集成开发环境中[221],成为世界各地大量软件开发人员的指尖工具。正如在第3节中思考的那样,这可能是将FM带入系统设计和开发前台的催化剂之一。

在本文中,我们展示了FM迄今对社会产生了什么影响,以及这种影响将来会增加。过去,安全曾经是工业在开发过程中不想投资的可选附加项。但是现在情况正在改变。例如,安全已成为必须的关键要素,行业不得不在开发过程中投资安全,Amazon Web Services的一个核心卖点是弹性计算云(见第2.4节)。FM已经成功地应用于金融、工业、消费和企业领域的安全分析中(见第2节)。

规范语言和相关工具

我们的调查涵盖了超过十年的FM在安全领域的应用。它揭示了正式规范语言及其工具、定理证明器、模型检查器和验证框架的丰富多样性。我们记录了超过40种不同的规范语言和超过40种不同的验证工具。其中包括以下内容:

Specification languages. AADL (Architecture Analysis & Design Language) [18, 70], ASF (Anonymous Secure Framework) [153], ASLan++ (AVANTSSAR Specification Language) [20], BAN logic [52, 228], Boogie [31], Boxed Ambients [135], CASM (ASM-based SL for compilers)[252], CCS (Calculus of Communicating Systems)[152], COVERT (compositional analysis of Android apps) [26], CSP (Communicating Sequential Systems) [120], CSP# (shared variables CSP) [237], CTL (Computation tree temporal logic) [229], Cloud Calculus [135], Cryptol [89], Dynamic State Machine [187], ERC20 token contracts [199], Event-B [83], HLPSL (High Level Protocol Specification Language) [44], Hoare logic [108], LS2 (Logic of Secure Systems) [27], LTL (linear-time temporal logic) [266], Markov Decision Process [182], Petri nets [15], π-calculus [40], PlusCal [9], Promela [172], RTL (real-time logic) [110], SPDL (Security Protocol Description Language) [168], SysML-Sec [18], TLA+ (Temporal Logic of Actions) [72], Trusted CSP# [27], überSpark [252], VDM [98], Verilog [164], VHDL [111], VML [226], vTRUST [117], XMHF (eXtensible and Modular Hypervisor Framework) [251], Z [265].

Model checkers. AVISPA (Automated Validation of Internet Security Protocols and Applications) [21], Alloy [83], CBMC (Bounded Model Checker for C and C++) [58], CWB-NC (Concurrency Workbench of New Century) [152], Cadence IFV (RTL block-level verifier) [111], FDR [103], GROOVE [102], jKind [99], NuSMV [65], OFMC (on-the-fly model checker) [34], PAT (Process Analysis Toolkit for CSP#) [237], PRISM (probabilistic model checker) [182], SATMC (SAT-based model checker for security protocols) [44], SPIN [247], TRUSTFOUND [27], UPPAAL [177], UVHM (formal analysis scheme for hypervisors) [251].

Theorem provers. Coq [72], Isabelle/HOL [144], K-framework [199], TAMARIN [154], Why [95].

Verification tools and frameworks. AndroBugs (Framework For Android Vulnerability Scanning) [241], Cl-Atse (protocol analyser) [141], FUDGE (Fuzz driver generator) [24], Frama-C [252], Krakatau [152], Maude (rewrite engine) [193], MobSF (mobile security framework) [127], phpSAFE [195], ProVerif [40], Quark [134], SAW (Software Analysis Workbench) [72], SMACK [72], SecGuru [136], SecVeriLog [92], Sugar (SAT-based) [169], TTool (translator from SysML-Sec to π-calculus) [18], Z3 [22].

这表明自Burrows等人的开创性论文[52]以来,FM在安全方面的研究和应用已经发展。我们的调查特别关注这些技术的实际应用,尤其是在工业规模上的应用。如今,一家公司如果不将其商业安全系统提交形式化分析,似乎是不可想象的。我们还怀疑黑客使用形式化技术来破解所谓的安全系统。作为最后的声明,我们需要承认调查提供了一个发展中领域的时间快照。因此,每十年需要回顾一次调查工作,我们正在计划这样做。尽管存在这个缺点,但作者认为,调查工作是研究的重要组成部分,因为它为寻找形式化方法在安全领域下的新作品和经验丰富的从业人员提供了一个起点和方向指示。

wasm serverless edge gis

table of contents

background

Introduce to wasm

WebAssembly(缩写 Wasm)是基于堆栈虚拟机的二进制指令格式。Wasm 是为了一个可移植的目标而设计的,可作为 C/C+/RUST 等高级语言的编译目标,使客户端和服务器应用程序能够在 Web 上部署。WASM 的运行时有多种实现,包括浏览器和独立的系统,它可以用于视频和音频编解码器、图形和 3D、多媒体和游戏、密码计算或便携式语言实现等应用。

尽管 WASM 是为了提高网页中性能敏感模块表现而提出的字节码标准, 但是 WASM 却不仅能用在浏览器(broswer)中, 也可以用在其他环境中。WASM 已经发展成为一个轻量级、高性能、跨平台和多语种的软件沙盒环境,被运用于云原生软件组件。与 Linux 容器相比,WebAssembly 的启动速度可以提高 100 倍,内存和磁盘占用空间要小得多,并且具有更好定义的安全沙箱。然而,权衡是 WebAssembly 需要自己的语言 SDK 和编译器工具链,使其成为比 Linux 容器更受限制的开发环境。WebAssembly 越来越多地用于难以部署 Linux 容器或应用程序性能至关重要的边缘计算场景。

WASM 的编译和部署流程如下:

wasm-compile-deploy

通常可以将 C/C+/RUST 等高级语言编译为 WASM 字节码,在 WASM 虚拟机中进行加载运行。WASM 虚拟机会通过解释执行或 JIT 的方式,将 WASM 字节码翻译为对应平台( x86/arm 等)的机器码运行。

主要往三个方面:

  • 让 C/C+ 等现有的工具和算法可以在浏览器中运行,比如 QGIS 之类的大型 GIS 桌面应用,可以达到和 native 差不多的效果;
  • 轻量级容器:serverless,性能比现有的基于容器的 serverless 可能好很多;
  • 边缘计算,云边协同

关于毕设, 有没有可能找一个把这几个部分都结合起来的 GIS 应用场景? serverless + edge + wasm + browser? 我调研了一下, 感觉 CS 这块有一些最新的 serverless + edge + wasm 的尝试(我自己最近也在做这些事情, 不过和 GIS 没关系), 但 GIS 好像还不是很多?

Introduce to serverless

2019年,UC伯克利大学的学者们从研究角度分析并预测,Serverless计算将取代 Serverful传统的模式,成为云计算的新一代范式。

 研究者们之所以这么说,是因为IT计算模式的变化,其实就是从Serverful计算到Serverless计算的演化。从最早的物理服务器开始,我们都在不断地抽象或者虚拟化服务器。我们使用虚拟化技术、云计算IaaS来自动管理这些虚拟化的资源。随着容器技术出现,我们用容器化CaaS实现了更轻量、更易用的虚拟化和自动化。但这些都还是从管理物理服务器到管理虚拟服务器,我们在使用时仍然需要关心背后的服务器资源分配,在程序没有使用时也需要为这些资源付费。

而现在的Serverless计算理念与技术,我们又实现了一次新的飞跃:它使得我们无需管理服务器,只需专注于业务逻辑,就可以更快构建和部署应用程序。

以一个传统Serverful应用为例:它包括业务数据等四层,为了部署这个应用,首先我们需要有一个服务器包括CPU、内存等作为计算资源,基于之上是操作系统、数据库、中间件等软件,再部署相应的业务数据等模块。如果要进行横向扩容,就需要将上述关注面复制一遍。基于容器技术,上述部署工作可以自动化,但概念模型上仍然是服务器计算资源作为基础。

而在Serverless技术支撑的应用架构中,系统四层能力都可以单独部署到可直接使用的云服务中。业务数据可以放到数据库服务中,业务逻辑执行部署到函数计算服务中。我们的服务接口可以部署到 API网关服务上,来响应动态内容的请求。静态的WebApp包部署在对象存储服务上,Web浏览器首先从对象存储中获取到应用页面本身,再发起动态请求给API网关。在这个过程中,我们使用了一系列稳定存在的云服务,并且只在使用时才计费。我们实际上只需要关注在我们的业务函数上,以及如何使用这些服务完成整个开发流程。

因此,Serverless并不意味着幕后真的没有服务器,只是服务器资源由第三方以各种专门服务的形式提供和管理,它们的资源伸缩、故障恢复等工作都由第三方也即这些BaaS/FaaS云服务提供商来负责。因此对我们应用开发者来说只需要使用这些服务即可,不再需要关心幕后的服务器。

我们可以给Serverless无服务器计算一个定义:Serverless无服务器计算是一种新的云计算模型,允许开发人员在构建和运行应用程序时,无需关心或管理服务器等基础设施。

为什么可以做到这一点?是因为云计算服务商提供了两类服务来代替服务器的作用:FaaS和BaaS。其中最关键的是FaaS,函数即服务,它是一种新的算力组织和提供方式,应用的业务逻辑被拆解为一个或多个细粒度函数,这些函数按需执行、伸缩和计费。另一类服务是BaaS,后端即服务,指的是函数执行所需要的通用的后端服务,由FaaS中的函数来按需调用。

Serverless架构对开发者意味着什么?当前,我们处于互联网分布式计算时代,默认Web BS系统,除了写出算法、读写数据,还需要考虑如何分布式部署、如何应对系统高峰时期的大并发请求;我们以AI人工智能为例,它有三个基石:算法、算力、数据。在Serverful架构中,算力由IaaS提供,算法和数据都要自行部署上去并调度管理来应对峰谷流量变化。

而在Serverless架构中,算法所需要的算力由FaaS提供,算法可以解耦成多个更细粒度的函数,开发者只需要简简单单的构思算法,即功能函数即可。数据也解耦,可以按需存储,数据的存储管理由BaaS提供。

Serverless计算尤其是FaaS函数计算,简化了计算资源的供给,极大提升了面向软件开发者的生产力,可以看作是云计算编程模型从汇编语言时代进化到高级语言时代。

基于上述分析,将Serverless无服务器计算的特征进行归纳:资源的解耦和服务化、自动弹性伸缩、按使用量计费等;相应带来的优势有低运维、低成本、高弹性、高可用等。

边缘计算

无服务计算,也被称为函数即服务(FAAS), 在过去两年一直是增长最快的云服务类型,仅在2019年就增长了50%。这是由于无服务计算提供了一种新的事件驱动的模型,用户可以在不关心服务配置和资源的前提下跑一些小的,无状态的应用。自动亚马逊在2014年推出了lambda,大量云服务商推出了类似的无服务平台,包括Google Cloud Functions、Microsoft Azure Functions、IBM Cloud Functions和Alibaba Cloud Functions 。虽然它们的实现各有不同,但是大多都是用虚拟机(vms)或容器作为一个沙箱环境来托管租户,并执行他们的函数。这些框架相对较重,在小内存,低延迟的场景下表现不好,尤其是这些函数第一次实例化的时候。最近的趋势和相关的技术挑战,激发了我们对边缘资源高效型无服务器计算的兴趣。其中包括以下几个:

  • 物联网的快速发展 物联网引入了大量低成本的设备,通过物理网压倒性的网络,这些设备通过不断的感知数据,产生的庞大的数据量。充分利用物联网的潜力,需要重新梳理计算模型。
  • 依赖实时服务的新型应用程序 工业物联网和下一代技术的兴起,导致计算模型需要支持极低(10ms)的延迟处理,例如:
  • 在智慧城市中,交通灯和路灯相互通信,如果有意外发生,可以第一时间感知并响应到。
  • 实时监控和智能视频处理功能的系统,适用于多种情况(在紧急情况下的延迟)
  • 联网汽车并提供相关的数据服务,及时提醒司机路况危险。

这些新兴应用程序会实时的处理大量的数据。他们需要的数据处理系统具有下面的特性:低延迟开销和高系统吞吐量,多租户隔离。

在边缘侧处理数据的重要性 虽然在人的认知里面,云计算是一种很好的解决方案,但是在需要无人参与的情况下能做出快速、自动化决策的场景下,依赖低延迟,它就变得没有那么好用了。例如工业控制系统中,数据分析和控制逻辑可能需要10ms以内的响应时间,上传到云端的话恐怕是不好保证这个延迟的。许多机器学习的工作负载需要在边缘场景下进行(收集传感器的数据)。但是资源和电量的限制仍然是一个挑战。为了满足以上场景的业务需求,我们必须使用新的服务来增强计算的能力,用来处理更加边缘的计算。这也就是边缘计算。

wasm + serverless + edge?

  • Sledge: a Serverless-first, Light-weight Wasm Runtime for the Edge

    https://www2.seas.gwu.edu/~gparmer/publications/middleware20sledge.pdf

    现在已经拥有了很多无服务器(serverless)的商业、开源平台,但是这些利用虚拟机和容器的解决方案对于资源有限的边缘系统来说过于重了,调度容器(冷启动)、启动一个虚拟机,往往需要大量的内存占用和很高的调用时间。另外,无服务的工作负载,主要的关注在每个客户端的请求,短期运行的计算并不适合常规的计算系统。

    在本文中,我们设计和实现了一种新颖高效的基于WebAssembly的edge框架—sledge。Sledge的经过一些优化以面对独特属性的工作负载:多租户的,启动快的,突发的客户端请求以及短期的计算。

    在本文章中,我们展示Sledge的设计和实现–一种新颖的、基于WebAssembly的、高效的边缘serverless框架。Sledge主要优化并且支持了一些独特场景的工作负载:高密度的租户,短启动时间,突发的客户端请求和短期计算。Sledge通过优化调度来解决短期计算和有效的任务分配,轻量级的函数隔离模型来实现基于WebAssembly的故障隔离。这些轻量的沙箱目标是在高密度计算:为了高密度的客户端请求,快速吊起和释放函数。和其它真实世界serverless运行时对比,在工作负载多变的条件下,设计一个边缘优先的serverless框架是有效的。与 Nuclio 相比,Sledge 支持4倍的吞吐量,延迟降低了4倍,是其中最快的serverless框架之一。

  • Evaluating webassembly enabled serverless approach for edge computing:

    Evaluating webassembly enabled serverless approach for edge computing

    Abstract:

    The edge computing ecosystem has been evolving in the last few years. There have been different architectural patterns proposed to implement edge computing solutions. This paper focuses on serverless edge computing architecture and evaluates webassembly based approach for the same. The current state of serverless edge computing is explained followed by providing high level conceptual overview of webassembly. Webassembly performance is evaluated against native and container based applications using the current toolchain supported for ARM architecture. Benchmarking is done for different categories of applications like compute intensive, memory intensive, file I/O intensive and a simple image classification - machine learning application. This paper describes the experimental setup, discusses the performance results and provides the conclusion.

    Published in: 2020 IEEE Cloud Summit

  • WearMask: Fast In-browser Face Mask Detection with Serverless Edge Computing for COVID-19

    https://arxiv.org/abs/2101.00784

    COVID-19的流行一直是美国的一个重大医疗挑战。根据美国疾病控制和预防中心(CDC)的资料,COVID-19感染主要是通过人们呼吸、说话、咳嗽或打喷嚏时产生的呼吸道飞沫传播。戴口罩是阻挡80%的呼吸道感染的主要、有效和方便的方法。因此,许多口罩检测和监测系统已经被开发出来,为医院、机场、出版物运输、运动场所和零售场所提供有效的监督。然而,目前的商业口罩检测系统通常与特定的软件或硬件捆绑,阻碍了公众的使用。在本文中,我们提出了一个基于浏览器的无服务器边缘计算的人脸面具检测解决方案,称为基于网络的高效人工智能面具识别(WearMask),它可以部署在任何有互联网连接的普通设备(如手机、平板电脑、电脑)上,使用网络浏览器,而无需安装任何软件。无服务器的边缘计算设计最大限度地减少了额外的硬件成本(例如,特定的设备或云计算服务器)。所提出的方法的贡献在于提供了一个整体的边缘计算框架,它整合了(1)深度学习模型(YOLO),(2)高性能神经网络推理计算框架(NCNN),和(3)基于堆栈的虚拟机(WebAssembly)。对于终端用户来说,我们基于网络的解决方案具有以下优势:(1)无服务器边缘计算设计,设备限制和隐私风险最小;(2)免安装部署;(3)计算要求低;(4)检测速度高。我们的WearMask应用程序已经在这个http URL上推出,供公众访问。

serverless + gis: 现有进展

  • 胡中南:云原生GIS 2.0新技术解读之Serverless + GIS:http://stock.10jqka.com.cn/20220908/c641712842.shtml

  • SuperMap GIS 11i(2022) 新特性速览:https://www.supermap.com/zh-cn/a/product/11i-characteristic-2022.html

  • SuperMap GIS 11i(2022)正式发布,揭秘七大特性:https://baijiahao.baidu.com/s?id=1737120191867774649&wfr=spider&for=pc

  • Geospatial Serverless Computing: Architectures, Tools and Future Directions

    https://www.researchgate.net/publication/341245906_Geospatial_Serverless_Computing_Architectures_Tools_and_Future_Directions

    Several real-world applications involve the aggregation of physical features corresponding to different geographic and topographic phenomena. This information plays a crucial role in analyzing and predicting several events. The application areas, which often require a real-time analysis, include traffic flow, forest cover, disease monitoring and so on. Thus, most of the existing systems portray some limitations at various levels of processing and implementation. Some of the most commonly observed factors involve lack of reliability, scalability and exceeding computational costs. In this paper, we address different well-known scalable serverless frameworks i.e., Amazon Web Services (AWS) Lambda, Google Cloud Functions and Microsoft Azure Functions for the management of geospatial big data. We discuss some of the existing approaches that are popularly used in analyzing geospatial big data and indicate their limitations. We report the applicability of our proposed framework in context of Cloud Geographic Information System (GIS) platform. An account of some state-of-the-art technologies and tools relevant to our problem domain are discussed. We also visualize performance of the proposed framework in terms of reliability, scalability, speed and security parameters. Furthermore, we present the map overlay analysis,point-cluster analysis, the generated heatmap and clustering analysis. Some relevant statistical plots are also visualized. In this paper, we consider two application case-studies. The first case study was explored using the Mineral Resources Data System (MRDS) dataset, which refers to worldwide density of mineral resources in a country-wise fashion. The second case study was performed using the Fairfax Forecast Households dataset, which signifies the parcel-level household prediction for 30 consecutive years. The proposed model integrates a serverless framework to reduce timing constraints and it also improves the performance associated to geospatial data processing for high-dimensional hyperspectral data.

wasm + gis: 现有进展

  • GIS Processing on the Web

    https://www.diva-portal.org/smash/record.jsf?pid=diva2%3A1674422&dswid=6199

    Today more and more advanced and demanding applications are finding their way to the web. These are applications like video editing, games, and mathematical calculations. Up until a few years ago, JavaScript was the only language present on the web. That was until Mozilla, Google, Microsoft, and Apple decided to develop WebAssembly. WebAssembly is a low-level language, similar to assembly, but running in the browser. WebAssembly was not created to replace JavaScript, but to be used alongside it and complement JavaScript’s weaknesses. WebAssembly is still a relatively new language (2017) and is in continuous development. This work is presented as a guideline, and to give a general direction of how WebAssembly is performing (in 2022) when operating on GIS data.

    今天,越来越多的先进和高要求的应用程序正在找到它们的方式,在网络上。这些应用包括视频编辑、游戏和数学计算等。直到几年前,JavaScript是网络上唯一存在的语言。直到Mozilla、谷歌、微软和苹果决定开发WebAssembly。WebAssembly是一种低级语言,类似于汇编,但在浏览器中运行。创建WebAssembly不是为了取代JavaScript,而是为了与它一起使用,补充JavaScript的弱点。WebAssembly仍然是一种相对较新的语言(2017年),并且正在不断发展。这项工作是作为一个指南提出的,并给出了WebAssembly在GIS数据上运行时的总体表现方向(2022年)。

  • The US COVID Atlas: A dynamic cyberinfrastructure surveillance system for interactive exploration of the pandemic

    Distributed spatial infrastructures leveraging cloud computing technologies can tackle issues of disparate data sources and address the need for data-driven knowledge discovery and more sophisticated spatial analysis central to the COVID-19 pandemic. We implement a new, open source spatial middleware component (libgeoda) and system design to scale development quickly to effectively meet the need for surveilling county-level metrics in a rapidly changing pandemic landscape. We incorporate, wrangle, and analyze multiple data streams from volunteered and crowdsourced environments to leverage multiple data perspectives. We integrate explorative spatial data analysis (ESDA) and statistical hotspot standards to detect infectious disease clusters in real time, building on decades of research in GIScience and spatial statistics. We scale the computational infrastructure to provide equitable access to data and insights across the entire USA, demanding a basic but high-quality standard of ESDA techniques. Finally, we engage a research coalition and incorporate principles of user-centered design to ground the direction and design of Atlas application development.

    From an infrastructure perspective, the Atlas is the first web application (to our knowledge) that integrates WebAssembly technology to manage computationally intensive spatial analysis functions (written in C++) directly in the web browser, opening wide new possibilities of browser-based geoprocessing and GIScience.

    从基础设施的角度来看,Atlas是第一个集成了WebAssembly技术的网络应用程序(据我们所知),可以直接在网络浏览器中管理计算密集型的空间分析功能(用C++编写),为基于浏览器的地理处理和GIS科学提供了广泛的新可能性。

  • 新一代三维GIS技术体系: 超图

    https://www.supermap.com/zh-cn/a/product/11i-tec-3-2022.html

    发布自主研发的全新 WebGL 三维客户端:

    • 完善 Web 端的三维渲染引擎,支持更强的粒子系统、 更多光影特效、更多后处理特效、更具真实感的物理材质
    • 支持游戏引擎导出的标准 PBR 材质,复制游戏引擎 美化后的三维场景
    • 基于 WebAssembly 技术,支持直接加载 .x、.dae 等 更多三维模型格式的数据
    • 提供 Vue2.0/3.0 开发组件,支持低代码开发
  • WebAssembly4G: Where we are, and where we’re heading

    https://talks.osgeo.org/foss4g-2022/talk/ASDL7P/

    WebAssembly’s adoption is gaining traction and still, its potential is not yet fully utilized, especially for the processing and visualization of geo data in and outside of browsers. In this session I will give a technical introduction to WebAssembly. I will show its current state and adaptation in FOSS4G projects and will talk about the ongoing advancements of the technology and possible future scenarios.

    This will also be a hands-on session, where after showing how to get up and running, I will share my experience, tips and tricks collected while porting the latest versions of GEOS, PROJ, GDAL, SpatiaLite and osgEarth to the web platform.

    The composition of existing OSGeo/FOSS C/C++ libraries in a portable and sandboxed form also brings many advantages outside of browsers. The talk will close with some demos about how WebAssembly enables us to build for the web, as well as for any other platform.

    WebAssembly的采用越来越多,但它的潜力仍未得到充分的利用,特别是在浏览器内外的地理数据的处理和可视化方面。在这次会议上,我将对WebAssembly做一个技术介绍。我将展示它的现状和在 FOSS4G 项目中的适应性,并将谈论该技术的持续进步和未来可能的情况。这也将是一个实践会议,在展示了如何启动和运行之后,我将分享我在将GEOS、PROJ、GDAL、SpatiaLite和osgEarth的最新版本移植到网络平台时收集的经验、技巧和窍门。现有的OSGeo/FOSS C/C++库以可移植和沙盒的形式组成,在浏览器之外也带来了许多优势。讲座的最后会有一些演示,介绍WebAssembly如何使我们为网络以及其他平台进行构建。

  • Write once, run anywhere: safe and reusable analytic modules for WebAssembly, Javascript, or more!

    https://talks.osgeo.org/foss4g-2022/talk/XV87XB/

    The proliferation of client-side analytics and on-going vulnerabilities with shared code libraries have fueled the need for better safety standards for running executables from potentially unknown sources. WebAssembly (WASM), a compilation target that allows lower-level languages like Rust, C, and Go to run in the browser or server-side at near-native speeds. Much like Docker changed the way we run virtualized workflows, WASM runtimes create safe virtual environments where access to the host system is limited.
    In combination with a new free and open source full-stack geospatial platform, Matico, efforts are underway to enable portability across workflows and applications to more easily use WASM modules. WASM implementations of GDAL are in the works, and powerful open source Rust geospatial libraries are easily packaged for web usage through Wasm-Pack. Additional geo WASM libraries like jsgeoda provide spatial indices, binning, and autocorrelation functions. Shareable code can be a recipe for security vulnerabilities and attack vectors, potentially exposing personal or critical information, particularly if there is the opportunity to run code server-side. WASM implementation alleviates this by requiring access from the Virtual Machine (VM) to be limited and explicit, and for Javascript developers the lightweight AssemblyScript language is relatively familiar.
    An upcoming Javascript feature called ShadowRealms may enable even simpler and more familiar implementations to safely run Javascript code shared between module authors. These developments lay the groundwork for a hybrid front- and backend geospatial ecosystem of shareable code snippets and analytic functions, much like have emerged in the UI component Javascript ecosystem. The combination of emerging features positions web geospatial analytics and This talk explores the implementation and performance of running geospatial analytic modules through a WebAssembly virtual machine and through the upcoming Javascript ShadowRealm specification.

    客户端分析的激增和共享代码库的持续漏洞,促使人们需要更好的安全标准来运行来自潜在未知来源的可执行文件。WebAssembly(WASM)是一个编译目标,允许Rust、C和Go等低级语言以接近原生的速度在浏览器或服务器端运行。就像Docker改变了我们运行虚拟化工作流程的方式一样,WASM运行时创建了安全的虚拟环境,对主机系统的访问受到限制。

    结合新的免费和开源的全栈地理空间平台Matico,正在努力实现跨工作流程和应用程序的可移植性,以更容易地使用WASM模块。GDAL的WASM实现正在进行中,强大的开源Rust地理空间库可以通过Wasm-Pack轻松打包供网络使用。额外的地理WASM库,如jsgeoda,提供空间指数、分档和自相关功能。可共享的代码可能是安全漏洞和攻击载体的秘诀,有可能暴露个人或关键信息,特别是如果有机会在服务器端运行代码。WASM的实施通过要求来自虚拟机(VM)的访问是有限的和明确的,而对于Javascript开发者来说,轻量级的AssemblyScript语言是相对熟悉的,从而缓解了这种情况。

    一个即将到来的名为ShadowRealms的Javascript功能可能使更简单和更熟悉的实现安全地运行模块作者之间共享的Javascript代码。这些发展为可共享的代码片段和分析功能的前后端地理空间混合生态系统奠定了基础,就像UI组件Javascript生态系统中出现的那样。新兴功能的结合,使网络地理空间分析和本讲座探讨了通过WebAssembly虚拟机和即将推出的Javascript ShadowRealm规范运行地理空间分析模块的实现和性能。

  • https://github.com/stuartlynn/wasm_geo_agg

    Wasm Geo Agg is a proof of concept to explore performing complex geospatial operations in the browser using Rust and WebAssembly. As an initial test, we are focusing on point in polygon operations. Simply load in a CSV file with points and a GeoJSON file with polygons then click aggregate.

    Currently, if you want to process geospatial data you can either

    1. Spend a day or two installing a bunch of really amazing tools like GDAL, PostGIS, QGIS etc and banging your head a few times as you try to get all their versions compatible with each other ( not to mention trying to not blow up your python installation as you go)
    2. Learn Python or R and use packages like geopandas
    3. Upload your data to services like ArcGis or CARTO to be stored and processed in the cloud somewhere.

    Options 1 or 2 let you process data locally but have a steep learning curve. As someone who has been working in the geospatial world for 4+ years, I still lose half a day each time I need to install a new geospatial stack. While using something like docker makes this a little easier, that too has a bit of a learning curve.

    Option 3 means that you to some extent hand over control of your data to a third party. If the data is sensitive and needs to remain local (as is true for a lot of non-profits or research data), or if you need a service that can be guaranteed to still be around in 5-10 years, these options might not be ideal either. Another consideration is that the cloud servers that process the data on these services are often less powerful than the laptop you are using to read this article, which increasingly seems insane to me.

    So this is an experiment exploring a 4th option. To ask: what if we had a PostGIS that ran entirely in your browser? A system that uses the web to deliver sophisticated software to your computer in the form of javascript and WASM with zero installation overhead, that then processes your data locally using the powerful CPU that happens to live in your machine.

  • ArcGIS API for JavaScript and WebAssembly

    https://developers.arcgis.com/javascript/latest/faq/

    Does the ArcGIS API for JavaScript support all Content Security Policy directives?

    No. Most CSP directives are supported and certified within the ArcGIS for JavaScript API. The API’s 3D functionality, in addition to the projection engine, makes use of WebAssembly (wasm). Wasm requires unsafe-eval in the script-src directive. Adding this in CSP goes against the protection that it is supposed to provide. There is a WebAssembly GitHub proposal that discusses this in more detail. Until this is addressed, applications that make use of these two parts of the API will not be able to take advantage of using CSP.

    看起来这里也用了 wasm,不过这个是不是不开源?

  • QGIS-Developer QGIS and WebAssembly - OSGeo mailing list

边缘计算 + GIS?

边缘计算GIS技术指的是将边缘计算的各种特征,用于支撑GIS应用的各要素,包括GIS内容的发布和分发,GIS服务的代理和加速,以及在线分析和计算,以一种更加灵活的方式,高效率、低成本地使用地理信息资源。

边缘计算GIS技术是云GIS技术的重要的补充,具体包括以下技术:

  • 边缘前置代理:在GIS云中心和客户端之间,对GIS服务进行代理加速,提供更好的服务访问体验。

  • 边缘服务聚合:将不同来源,不同内容的GIS服务聚合为一个服务,实现多源、异构地理信息与服务的整合。

  • 边缘内容分发:云GIS中心自动将瓦片数据分发到边缘GIS节点,实现了边缘GIS内容的自动更新。

  • 边缘分析计算:在边缘端按需进行GIS分析和计算,有效提升GIS服务性能。

  • 为什么我们需要边缘计算GIS技术? https://baijiahao.baidu.com/s?id=1718990389366964311&wfr=spider&for=pc

  • 边缘计算GIS技术篇——边缘GIS再升级,满足云-边-端架构GIS应用多重需求: https://magazine.supermap.com/view-1000-15938.aspx

参考论文: WASM/eBPF + Serverless

  • SPRIGHT: Extracting the Server from Serverless Computing! High performance eBPF-based Event-driven, Shared-memory Processing

    https://dl.acm.org/doi/10.1145/3544216.3544259

    Serverless computing promises an efficient, low-cost compute capability in cloud environments. However, existing solutions, epitomized by open-source platforms such as Knative, include heavyweight components that undermine this goal of serverless computing. Additionally, such serverless platforms lack dataplane optimizations to achieve efficient, high-performance function chains that facilitate the popular microservices development paradigm. Their use of unnecessarily complex and duplicate capabilities for building function chains severely degrades performance. ‘Cold-start’ latency is another deterrent.

    We describe SPRIGHT, a lightweight, high-performance, responsive serverless framework. SPRIGHT exploits shared memory processing and dramatically improves the scalability of the dataplane by avoiding unnecessary protocol processing and serialization-deserialization overheads. SPRIGHT extensively leverages event-driven processing with the extended Berkeley Packet Filter (eBPF). We creatively use eBPF’s socket message mechanism to support shared memory processing, with overheads being strictly load-proportional. Compared to constantly-running, polling-based DPDK, SPRIGHT achieves the same dataplane performance with 10× less CPU usage under realistic workloads. Additionally, eBPF benefits SPRIGHT, by replacing heavyweight serverless components, allowing us to keep functions ‘warm’ with negligible penalty.

    Our preliminary experimental results show that SPRIGHT achieves an order of magnitude improvement in throughput and latency compared to Knative, while substantially reducing CPU usage, and obviates the need for ‘cold-start’.

    SPRIGHT利用共享内存处理,并且戏剧性的通过避免没必要的协议处理和序列化/反序列化开销,提升了数据面的扩展性。SPRIGHT广泛地使用eBPF进行事件驱动的处理。作者创造性的使用eBPF的socket消息机制来支持共享内存处理,其开销严格的与负载成比例。在真实的负载下,与不间断的运行、基于轮询的DPDK相比,SPRIGHT能够在使用10倍少的CPU使用量的情况下,实现相同的数据面性能。除此之外,eBPF通过代替重量级的无服务组件让SPRIGHT变好,让作者能够在开销可以忽略的情况下,保持函数“暖”(warm)。作者的初步实验结果表明,与Knative相比,SPRIGHT在吞吐和时延上有一个数量级的提升,同时实质上减少了CPU的用量,并且避免了“冷启动”的需求。

    SIGCOMM ‘22: Proceedings of the ACM SIGCOMM 2022 Conference

  • Sledge: A serverless-first, light-weight wasm runtime for the edge

    https://dl.acm.org/doi/abs/10.1145/3423211.3425680

  • Faasm: Lightweight Isolation for Efficient Stateful Serverless Computing

    Serverless computing is an excellent fit for big data processing because it can scale quickly and cheaply to thousands of parallel functions. Existing serverless platforms isolate functions in ephemeral, stateless containers, preventing them from directly sharing memory. This forces users to duplicate and serialise data repeatedly, adding unnecessary performance and resource costs. We believe that a new lightweight isolation approach is needed, which supports sharing memory directly between functions and reduces resource overheads.

    We introduce Faaslets, a new isolation abstraction for high-performance serverless computing. Faaslets isolate the memory of executed functions using \emph{software-fault isolation} (SFI), as provided by WebAssembly, while allowing memory regions to be shared between functions in the same address space. Faaslets can thus avoid expensive data movement when functions are co-located on the same machine. Our runtime for Faaslets, Faasm, isolates other resources, e.g. CPU and network, using standard Linux cgroups, and provides a low-level POSIX host interface for networking, file system access and dynamic loading. To reduce initialisation times, Faasm restores Faaslets from already-initialised snapshots. We compare Faasm to a standard container-based platform and show that, when training a machine learning model, it achieves a 2× speed-up with 10× less memory; for serving machine learning inference, Faasm doubles the throughput and reduces tail latency by 90%.

    https://arxiv.org/abs/2002.09344

    Shillaker S, Pietzuch P. Faasm: Lightweight isolation for efficient stateful serverless computing[C]//2020 {USENIX} Annual Technical Conference ({USENIX}{ATC} 20). 2020: 419-433.

毕设?

  • 希望往 wasm + eBPF + edge computing + serverless + gis 的方向走?
  • 尝试有一些相关的 gis 的 demo 应用, 证明这个思路的可行性?
  • 在浏览器里面运行的大型 GIS 应用程序和分析程序, 同时还可以通过云端加速? (云边协同?)
  • 比如同样一个 function, 又可以跑在浏览器里面, 又可以跑在边缘计算的节点, 又可以在云上以 serverless 的方式扩容, 同时性能还不错? 可以用任意语言编写, 也不需要管理对应的调度复杂度, 还可以并行?
  • 什么样的 GIS 应用场景比较好呢? 我目前没想到….总之就是做一个 showcase, 证明这个思路的可行性, 以及有一些应用场景的潜力?
    • 要有现成的开源代码, 最好是 C/C++ 写的, Java 或者 Go 可能也勉强可以, 这样我不用自己写太具体的 GIS 算法, 只要搭框架和写业务就好(这样工作量不至于过大); 而且我和华南理工那边都希望开源出去;
    • 最好是计算密集型, 但还是有一定程度上的相互通信的科学计算任务, 比较方便展示 serverless 并行效果?(图形学的事可能不如用 GPU 跑)
    • 或者原先是桌面的 C/C++ 算法代码, 现在编译成 wasm 就可以丢进浏览器跑, 同时还可以上云扩容?以及在浏览器里面跑, 还可以糊一个前端,看起来好玩一点?
    • 有一些边缘计算场景?
  • 具体的工作的话我也可以去修改一下这个底层的 wasm serverless 运行时平台, 然后把这个 GIS 的工作负载应用给跑上去.
  • 校外导师?

(不知道, 瞎说的)

Clang static analyzer Checker 初探

这里只是我刚开始学习静态分析时的一些粗浅的理解和经验之谈,以及相关资料整理,并不能确保正确,还请多多补充指正

1. 关于 Clang static analyzer

这是 naive systems 的一个静态分析工具: https://www.naivesystems.com/ 其中一些对于 MISRA C 的检查器就是基于 Clang static analyzer 构建的。

1.1. Clang static analyzer

Clang 是 LLVM 的一个“前端”,意思是底层依赖于 LLVM 架构。Xcode 使用 Clang 。

LLVM 不是一个缩写,它是一个工具集,用于构建编译器、优化器、运行时环境。Clang 只是在其基础上建立的 C语系(C/C++/Objective C)编译器,该计划最初设想提供一种基于SSA编译策略的,支持任意编程语言的静态和动态编译,现今该计划已经发展出多个模块化的子项目,成为编译器和相关工具链的合集。

Clang Static Analyzer 是 Clang 项目的一部分,在 Clang 基础上构建,静态分析引擎被实现为可重用的C++库,可以在多种环境下使用(Xcode、命令行、接口调用等)。静态分析会自动检查源代码中的隐含bug,并产生编译器警告。随着静态分析技术的发展,其已经从简单的语法检查,步进到深层的代码语义分析。要注意到,由于使用最新的技术深入分析代码,因此静态分析可能比编译慢得多(即使启用编译优化),查找错误所需的某些算法在最坏的情况下需要指数时间。静态分析还可能会存在假阳性问题(False Positives)。如果需要更多 Checker 来让静态分析引擎执行特定检查,需要在源码中实现

1.2. How it works

静态分析最初由一些基础研究论文启发。

简而言之,分析器是一个源码的模拟器,追踪其可能的执行路径。程序状态(变量和表达式的值)被封装为 ProgramState 。程序中的位置被叫做 ProgramPoint 。state 和 program point 的组合是 ExplodedGraph 中的节点。术语“exploded”来自控制流图(control-flow graph,CFG)中爆炸式增长的控制流连边。

概念上讲,分析器会沿着 ExplodedGraph 执行可达性分析(reachability analysis)。从具有 entry program point 和 initial state 的根节点开始,分析模拟每个单独表达式的转移。表达式分析会产生状态改变,使用更新后的 program point 和 state 创建新节点。当满足某些 bug 条件时(违反检测不变量,checking invariant),就认为发现了 bug 。

分析器通过推理分支(branches)追踪多条路径(paths),分叉状态:在 true 分支上认为分支条件为 true,在 false 分支上认为分支条件为 false 。这种“假设”创建了程序中的值的约束(constraints),这些约束被记录在 ProgramState 对象(通过 ConstraintManager 修改)。如果假设分支条件会导致不能满足约束,这条分支就被认为不可行,路径也不会被选取。这就是我们实现路径敏感(path-sensitivity)的方式。我们降低了缓存节点的指数爆炸。如果和已存在节点含相同 state 和 program point 的新节点将被生成,路径会“出缓存”(caches out),我们只简单重用已有节点。因此 ExplodedGraph 不是有向无环图(DAG),它可以包含圈(cycles),当路径相互循环,以及出缓存。

ProgramState 和 ExploledNodes 在创建后基本上是不可变的。当产生新状态时,需要创建一个新的 ProgramState 。这种不变性是必要的,因为 ExplodedGraph 表示了从入口点开始分析的程序的行为。为了高效表达,我们使用了函数式数据结构(比如 ImmutableMaps )在实例间共享数据。

最终,每个单独检查器(Checkers)也通过操作分析状态来工作。分析引擎通过访问者接口(visitor interface)与之沟通。比如,PreVisitCallExpr() 方法被 GRExprEngine 调用,来告诉 Checker 我们将要分析一个 CallExpr ,然后这个检查器被请求检查任意前置条件,这些条件可能不会被满足。检查器不会做除此之外的任何事情:生成一个新的 ProgramState 和包含更新后的检查器状态的 ExplodedNode 。如果它发现了一个 bug ,它会把错误告诉 BugReporter 对象,提供引发该问题的路径上的最后一个 ExplodedNode 节点。

2. 如何添加一个最简单的 Checker

以一个最简单的 checker ,禁用 malloc 为例:

2.1. Hello World

clang/lib/StaticAnalyzer/Checkers/ ,新建 MyChecker.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"

using namespace clang;
using namespace ento;

namespace {
class MyChecker : public Checker< check::PreCall > {
mutable std::unique_ptr<BuiltinBug> BT;
void reportBug(const char *Msg, const CallEvent &Call, CheckerContext &C) const;

public:
// process malloc(0)
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
};
} // end anonymous namespace

void MyChecker::reportBug(const char *Msg, const CallEvent &Call, ProgramStateRef StateZero, CheckerContext &C) const
{
if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
if (!BT)
BT.reset(new BuiltinBug(this, "call to malloc"));

auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
R->addRange(Call.getSourceRange());
C.emitReport(std::move(R));
}
}

void MyChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const
{
if (!Call.isGlobalCFunction("malloc"))
return;

reportBug("V: Allocation of zero bytes", Call, C);
return;
}

void ento::registerMyChecker(CheckerManager &mgr) {
mgr.registerChecker<MyChecker>();
}

bool ento::shouldRegisterMyChecker(const CheckerManager &mgr) {
return true;
}

2.2. 注册编译

clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 文件中,把新建的 MyChecker 放入待注册列表:

1
2
3
4
5
6
7
let ParentPackage = Core in {
// ...
def MyChecker : Checker<"MyChecker">,
HelpText<"Check for zero malloc">,
Documentation<HasDocumentation>;
// ...
} // end "core"

之后,需要把 MyChecker.cpp 添加进 clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 检查器构建列表。

1
2
3
4
add_clang_library(clangStaticAnalyzerCheckers
MyChecker.cpp
...
)

3. 教程:进行不同类型的检查

举几个例子:

(TODO: 待完善)

3.1. PointerSubChecker.cpp

检查程序某个特定的节点中,两个指针是否指向了同一个内存区域:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

using namespace clang;
using namespace ento;

namespace {
class PointerSubChecker
: public Checker< check::PreStmt<BinaryOperator> > {
mutable std::unique_ptr<BuiltinBug> BT;

public:
void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
};
}

void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
CheckerContext &C) const {
// When doing pointer subtraction, if the two pointers do not point to the
// same memory chunk, emit a warning.
if (B->getOpcode() != BO_Sub)
return;

SVal LV = C.getSVal(B->getLHS());
SVal RV = C.getSVal(B->getRHS());

const MemRegion *LR = LV.getAsRegion();
const MemRegion *RR = RV.getAsRegion();

if (!(LR && RR))
return;

const MemRegion *BaseLR = LR->getBaseRegion();
const MemRegion *BaseRR = RR->getBaseRegion();

if (BaseLR == BaseRR)
return;

// Allow arithmetic on different symbolic regions.
if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR))
return;

if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
if (!BT)
BT.reset(
new BuiltinBug(this, "Pointer subtraction",
"Subtraction of two pointers that do not point to "
"the same memory chunk may cause incorrect result."));
auto R =
std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
R->addRange(B->getSourceRange());
C.emitReport(std::move(R));
}
}

void ento::registerPointerSubChecker(CheckerManager &mgr) {
mgr.registerChecker<PointerSubChecker>();
}

bool ento::shouldRegisterPointerSubChecker(const CheckerManager &mgr) {
return true;
}

(TODO: 待完善)

3.2. SimpleStreamChecker.cpp

跟踪状态传递:[SimpleStreamChecker.cpp]https://github.com/llvm/llvm-project/blob/main/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp)

Defines a checker for proper use of fopen/fclose APIs.

  • If a file has been closed with fclose, it should not be accessed again.
    Accessing a closed file results in undefined behavior.
  • If a file was opened with fopen, it must be closed with fclose before
    the execution ends. Failing to do so results in a resource leak.

(TODO: 待完善)

3.3. Taint.cpp

Defines basic, non-domain-specific mechanisms for tracking tainted values.

https://github.com/llvm/llvm-project/blob/main/clang/lib/StaticAnalyzer/Checkers/Taint.cpp

(TODO: 待完善)

3.4. 其他一些常用的检查实现

https://b.corp.naive.systems:9443/projects/misra-c-2012/wiki/一些简单的可供参考的代码片段

4. 如何写一个更好的检查器

部分翻译和整理自 https://clang-analyzer.llvm.org/checker_dev_manual.html:Making Your Checker Better,也有一部分是经验总结。这部分值得好好阅读,我们在这上面栽过不少坑

4.0.1. 良好的编码习惯

  • 警告和注意信息应该清晰易懂,即使有点长。
    • 消息应以大写字母开头(与 Clang 警告不同!)且不应以 .. 结尾
    • 引入 BugReporterVisitor 以发出额外的注释,更好地向用户解释警告。有一些现有的访问者可能对您的检查有用,例如 trackNullOrUndefValue 。例如, SimpleStreamChecker 应该在报告文件描述符泄漏时突出显示打开文件的事件。
  • 如果 checker 跟踪程序状态中的任何内容,则需要实现 checkDeadSymbols回调来清理状态。
  • 当跟踪的未知符号被传递给 checker 时,检查应该保守地假设程序是正确的。 checkPointerEscape 回调可以帮助您处理这种情况。
  • 使用安全便捷的 API!
    • 始终使用 CheckerContext::generateErrorNodeCheckerContext::generateNonFatalErrorNode 来发出错误报告。最重要的是,永远不要针对 CheckerContext::getPredecessor 发出报告。
    • Prefer checkPreCall and checkPostCall to checkPreStmt<CallExpr> and checkPostStmt<CallExpr>.
    • 使用 CallDescription 检测程序中的硬编码 API 调用。
    • C.getState ()->getSVal(E, C.getLocationContext()) 简化为 C.getSVal(E)

4.0.2. 常见的崩溃来源

  • CallEvent::getOriginExpr 可以为空 - 例如,它为变量的自动析构函数返回 null。这同样适用于模拟调用时生成的一些值,例如, SymbolConjured::getStmt 可以为空。
  • CallEvent::getDecl 可以为空 - 例如,它为调用符号函数指针返回 null。
  • addTransition generateSink generateNonFatalErrorNode generateErrorNode 可以为空,因为您可以转换到您已经访问过的节点。
  • 当参数越界时,返回参数的 CallExpr/FunctionDecl/CallEvent 方法会崩溃。如果您检查了函数名称,这并不意味着该函数具有预期的参数数量!这就是您应该使用CallDescription的原因。
  • 不同种类的符号和区域中不同实体的可空性通常通过其构造函数中的断言来记录。
  • 如果声明的名称不是单个标记,例如对于析构函数,NamedDecl::getName 将失败。对于这些情况,您可以使用 NamedDecl::getNameAsString 。请注意,此方法要慢得多,应谨慎使用,例如仅在生成报告时而不是在分析期间使用。
  • -analyzer -checker=core 是否包含在所有测试RUN:行中?从未支持在禁用核心检查的情况下运行分析器。它可能会导致意外行为和崩溃。您应该在启用核心检查的情况下进行所有测试。

除了上述 CSA 中常见的 崩溃可能性,还应当注意 llvm 中的类型转换和空指针检查。

4.0.3. 即使在技术上没有错误,您也应该避免的模式

  • BugReporterVisitor 很可能与当前程序点的 AST 不匹配,以决定何时发出注释。通过观察 program state 的变化来确定这一点要容易得多。
  • State->getSVal(Region) 中,如果 Region 不是 TypedValueRegion 并且未指定可选类型参数,则检查器可能会意外尝试取消引用 void 指针。
  • 检查器逻辑不应依赖于某个值是 Loc 还是 NonLoc 。根据正在检查的 AST, SValLoc 还是 NonLoc 应该立即显而易见。检查一个值是 Loc 还是 Unknown/Undefined 或者该值是 NonLoc 还是 Unknown/Undefined 完全没问题。
  • 不应通过直接调用 SymbolManager 在检查器中构造新符号,除非它们属于检查器标记的 SymbolMetadata 类,或者它们代表新创建的值,例如 evalCall 中的返回值。对于模拟算术/按位/比较操作,应使用 SValBuilder
  • 不应在检查器中创建自定义 ProgramPointTag 。检查器通常没有充分的理由将多个节点链接在一起,因为检查器不是 worklists 算法。
  • 鼓励检查者通过与分析器的其余部分分享他们关于程序状态的知识来积极参与分析,但他们不应不必要地破坏分析:
  • 如果检查器拆分程序状态,这必须基于新出现的分支绝对是可能的并且值得从用户的角度探索的知识。否则,状态拆分应该延迟,直到有迹象表明采取了其中一条路径,或者需要完全删除其中一条路径。例如,只要x在每条路径上受到相应的约束,就可以在建模isalpha(x)时急切地分割路径。同时,在为调用建模时,在printf()的返回值上分割路径并不是一个好主意,因为没有人检查printf中的错误;充其量,它只会使剩余的分析时间增加一倍。
  • 使用 CheckerContext::generateNonFatalErrorNode 时建议小心, 因为它会生成一个独立的转换,很像 addTransition 。使用时很容易意外拆分路径。理想情况下,尝试对代码进行结构化,以便每个 addTransitiongenerateNonFatalErrorNode (或如果要拆分的情况下的序列)之后立即从检查器回调返回。
  • 不同检查器中 evalCall 的多个实现不应冲突。
  • 实现 evalAssume 时,检查器应始终为真假设或假假设(或两者)返回非空状态。
  • 检查器不得改变表达式的值,即使用 ProgramState::BindExpr API,除非它们完全负责计算值。在任何情况下,他们都不应更改表达式的非未知值。目前,此 API 在检查器中的唯一有效用例是在 evalCall 回调中对返回值进行建模。如果表达式值不正确,则需要修复 ExprEngine

5. 有哪些资料可以进一步参考?

5.1. llvm 官方文档和论坛

一切以官方文档为准。

5.2. 代码

这一部分是在 llvm-project 代码库中可见的一些参考资料:

  • README

    一个简要介绍 CSA 的 README 文件,包含了库结构,工作原理等。

  • documentation

    This checker lists all the checker callbacks and provides documentation for checker writers. 提供了所有的回调钩子的文档。

5.3. 论文/主要文档

5.4. 博客

5.4.1. 某个 clang static analyzer 源码分析博客:dashuniuniu

这一部分主要是关于 clang static analyzer 的工作原理分析,虽然稍微有点老旧(约2017年),不过应该大体上还是没有太多变化的;相关系列从源码入手详细分析了 clang static analyzer 的一些基本概念和工作模式,值得一看。

同一个人在知乎上也有相关文章,讨论 CSA 相关内存模型:

其他部分还可以自行访问其 csdn 和 zhihu 账号。

5.4.2. 知乎:VVKoishi

这一系列文章关注于实现一个简单的 memory.ZeroAlloc Checker,让 Analyzer 引擎提供自定义的静态检查支持;并且也涉及到了一些简单的代码分析,如果你是在 MacOS 下工作的话,这是一个很好的入门文档,写于 2021 年。

Part 1 介绍 Clang Static Analyzer ,以及源码构建 Clang

Part 2 关注引擎底层实现,包含 Checker 相关源码解读,举例 DivZeroChecker

Part 3 关注如何添加一个 Checker

5.4.3. 其他

关于 Live Variables analysis 的源码分析:

用 rust 实现可持久化 AVL 树:ImmutableMap

这几篇想简单谈谈一下自己在写代码时遇见的,或者阅读 llvm 相关代码时见到的数据结构实现。

本文源代码:https://github.com/yunwei37/immutable-map-rs

关于 ImmutableMap

ImmutableMap 是一种可持久化数据结构,在进行插入或删除操作时并不对原先的数据结构进行改动,而是创建一个新的拷贝。关于可持久化数据结构,可以参考维基百科[1]:Persistent_data_structure

这里参考的是 llvm 中的 ImmutableMap/ImmutableSet 实现,采用一个平衡因子为 2 的 AVL 树[2]:

Read more

llvm 源码中的数据结构:ImmutableList

这几篇想简单谈谈一下自己在写代码时遇见的,或者阅读 llvm 相关代码时见到的数据结构实现。

关于 ImmutableList

ImmutableList 顾名思义,即不可变链表。它是一种可持久化数据结构,在进行插入或删除操作时并不对原先的数据结构进行改动,而是创建一个新的拷贝。关于可持久化数据结构,可以参考维基百科:Persistent_data_structure

在计算中,持久数据结构或非临时数据结构是一种在修改时始终保留其先前版本的数据结构。这样的数据结构实际上是不可变的,因为它们的操作不会(明显地)就地更新结构,而是总是产生一个新的更新结构。该术语是在 Driscoll、Sarnak、Sleator 和 Tarjans 1986 年的文章中引入的。[1]这些类型的数据结构在逻辑和函数式编程中特别常见,[2]因为这些范式中的语言不鼓励(或完全禁止)使用可变数据。

Read more

c++20 协程与 io_uring 初探:一个最简单的 echo server

写这篇的初衷是想动手实践一下 io_uring 和 c++20 协程。

这个版本的代码由 github.com/frevib/io_uring-echo-server 改造而来,是希望通过在 io_uring 的基础上,尝试实现最基本的协程 IO 模式,然后进行性能对比。之前的版本使用了一个 event loop 的模式,并通过 io_uring 的 IORING_OP_PROVIDE_BUFFERS 参数和 IORING_FEAT_FAST_POLL 参数,实现了零拷贝和内核线程的 polling,不需要额外的系统调用开销。

本文在 io_uring-echo-server 的基础上增添了一个简易的协程实现,完整的 demo 代码实现在这里:github.com/yunwei37/co-uring-WebServer/blob/master/demo/io_uring_coroutine_echo_server.cpp

Read more