Page 78 - 《软件学报》2025年第8期
P. 78

何韬 等: GhostFunc: 一种针对  Rust 操作系统内核的验证方法                                         3501


                    本文对于    arch64  中的有关硬件的    unsafe 操作, 没有进行形式化建模, 所以本节的建模主要是针对标准库中的
                 unsafe 操作. 下面以涉及代码量较多       Arc 和  Mutex  为例, 对  unsafe 代码的形式化过程详细地介绍, 主要针对其具体
                 功能以及涉及的如生命周期等属性进行建模和不变式的定义.

                 4.1.1    alloc::sync::Arc
                    Arc<T> 类型提供了在堆中分配的         T  类型值的共享所有权, 在      Arc 中调用  clone 方法会生成新的实例, 实例指
                 向堆上源    Arc 所指处, 并增加引用计数. 在某内核中, Arc 的使用非常频繁, 主要是用于进线程以及权能相关的管
                 理. Arc 的核心机制是引用计数, 它通过原子计数器来跟踪对象有多少个活跃的引用. 下面为                         Arc 的  3  个基本定义.
                    定义  4. Arc 的基本属性定义.

                                    Definition [[Arc(τ)]].size := [[τ]].size+2.
                                    Definition [[Arc(τ)]].own(tid,vl) :=
                                                   ∃rc l, vl = [#l] ∗
                                             l 7→ 1(rc, [[τ]].own tid) ∗
                                                     rc 7→ {1}#1 ∗
                                          □(rc 7→ {1}#1 ⇒ [[τ]].inv tid l).
                                    Definition Arc(τ).shr(κ, tid, l) := ∃rc, rc 7→ κ#1 ∗ [[τ]].shr κ tid l.
                    这  3  个定义分别是    size、所有权  own  和共享属性, 在不考虑      DST  的情况下, 在类型系统中      [[τ]] 和  own(τ) 在
                                      [[τ]] 的                    λ Rust  中的类型如布尔类型, 整形类型, 和类型, 积类
                 语义上被归为同一类, 所以              size 属性与  own(τ) 相等, 在
                 型等的   size 属性都为  1, 大于  1  的为复合或嵌套类型, 例如     Arc 需要有引用计数, 在类型上可视为          lock 大类中的需
                 要额外属性的类型, 所以在其         size 基础上加  2.
                    接下来介绍     Arc 的包含的   3  种方法的形式化定义: 分别是新建         new(), 克隆  clone(), 删除  drop() .
                    定义  5. Arc 包含的方法形式化定义.

                                     {[[τ]].own( ¯w)} let x = Arc :: new( ¯w) := fn(τ) → Arc(τ)
                                     {[[Arc τ]].own([x])} let y = x.clone() := fn(&α Arc(τ)) → Arc(τ)
                                     {[[Arc τ]].own([¯e])} let y = x.drop(¯e) := fn(Arc(τ)) → (),
                 其中, () 表示空, & 表示获取原    Arc 指针的复制. 这    3  个方法对应的后置结果形式如下:

                                          ∃ℓ.x = [ℓ]∗ℓ 7→ [1]++ ¯w∗ shared_own(int,ℓ, ¯w,1,1)
                                          [[Arc τ]].own([x])∗[[Arc τ]].own([y])
                                          [[Arc τ]].().
                    new  对  ℓ 地址处添加了一个拥有共享所有权的变量, 对应于             Arc 的在  Rust 中的含义, clone 将共享所有权拆分
                 为了两个, 为了简化模型, 没有采用         λ Rust  中的所有权的  token  形式.
                    除了定义和操作的形式化, 我们还需要为              Arc 定义不变式, 首先我们分析       Arc 在内核中所需保持的不变性质,
                 Arc 在内核中主要是用于维护全局的线程队列, 所以第               1  个不变性质就是队列的在内存中的位置             ℓ 需要保持, 第  2
                 是引用的计数正确, 第      3  是正确的销毁. 基于此我们可以定义出           Arc 的不变式   Arc_inv.
                    定义  6. Arc 不变式定义.

                          Arc_inv (κ ,ℓ,t) := ℓ 7→ true∨ℓ 7→ false∗κ (∃¯v.(ℓ +1) 7→ ¯v∗[[τ]].own(t, ¯v))∨ℓ 7→ false∗t 7→ (),
                                                        ′
                                  ′
                                τ
                                                        full
                 其中,  κ  用于表示生命周期的快照, t 为线程集合, 第         2  个不变式状态表示若      Arc 处于空闲状态, 可以使其储存的下
                      ′
                 一个位置储存值对应于当前的线程, 使其获得该复制位置的所有权.

                 4.1.2    alloc::sync::Mutex
                    Mutex  为标准库中的互斥锁. 互斥锁阻止等待锁可用的线程. 互斥锁可以通过 new 构造函数创建. 每个互斥锁
                 都有一个类型参数, 表示它正在保护的数据. 只能通过从 lock 和 try_lock 返回的 RAII 保护来访问数据, 这保证了
                 只有在互斥锁被锁定时才可以访问数据. 在              Arc 中我们没有详细介绍       Rust 中锁的机制, 在这里将进行介绍. 锁保
                 护的内容的所有权应当拥有一个完整的借用, 若没有获取到完整的借用, 那他的外层也就是锁本身应当包含这个
   73   74   75   76   77   78   79   80   81   82   83