166 lines
7.8 KiB
Plaintext
166 lines
7.8 KiB
Plaintext
(*
|
|
* Copyright 2014, NICTA
|
|
*
|
|
* This software may be distributed and modified according to the terms of
|
|
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
|
|
* See "LICENSE_GPLv2.txt" for details.
|
|
*
|
|
* @TAG(NICTA_GPL)
|
|
*)
|
|
|
|
chapter "Restricted capabilities in the Separation Kernel Abstract Specification"
|
|
|
|
theory Separation
|
|
imports
|
|
"ASepSpec.Syscall_SA"
|
|
"AInvs.AInvs"
|
|
"Lib.Bisim_UL"
|
|
"Lib.LemmaBucket"
|
|
begin
|
|
|
|
text {*
|
|
The seL4 kernel, when appropriately restricted, is a separation kernel. Any
|
|
two processes in separate domains should behave the same as if they were
|
|
processes running on two physically separated machines. They should not be
|
|
aware of each other's existence and should not be able to communicate with
|
|
each other except through well-defined channels. Importantly, it must be
|
|
possible to show that there are no back channels through which one process
|
|
can determine whether another process exists or what it is doing.
|
|
|
|
In seL4 we achieve this by restricting the capabilities that a thread may
|
|
possess. The restrictions are summarised in the predicate @{text
|
|
separate_state} below (which indirectly depends on further predicates @{text
|
|
separate_cnode_cap}, @{text separate_cap}, etc).
|
|
|
|
a) A thread may only possess \emph{notification capabilities}
|
|
(@{text NotificationCap}).
|
|
|
|
b) Threads do not have caller capabilities. (A caller capability is a
|
|
capability, placed in a special slot in the TCB, to allow replies. Since the
|
|
@{text Reply} capability is disallowed so is the caller capability.)
|
|
|
|
c) Pointers to other capability tables are disallowed meaning that the
|
|
capability tree is flat. i.e. of depth 1
|
|
|
|
Initialising the kernel so that these restrictions hold is not covered in
|
|
the bisimulation proof, but can be achieved using the capDL initialiser.
|
|
|
|
Note that this proof does not preclude threads from communicating via shared
|
|
memory if the threads have been set up accordingly, which again can be done
|
|
via the capDL initialiser.
|
|
|
|
The proof does show that the kernel API after reaching a state that
|
|
satisifies @{text separate_state} is that of a static separation kernel,
|
|
that is, it only provides system calls for sending and receiving on
|
|
notification objects and otherwise exhibits no dynamic behaviour.
|
|
|
|
Systems with such a setup satisfy the preconditions of our separate
|
|
non-intereference proof, which shows that information travels only along
|
|
these authorised channels.
|
|
*}
|
|
|
|
definition
|
|
separate_cap :: "cap \<Rightarrow> bool"
|
|
where
|
|
"separate_cap cap \<equiv> case cap of
|
|
NotificationCap ptr badge rights \<Rightarrow> rights \<subseteq> {AllowRecv, AllowSend}
|
|
| NullCap \<Rightarrow> True
|
|
| _ \<Rightarrow> False"
|
|
|
|
|
|
lemma separate_capE:
|
|
"\<lbrakk> separate_cap cap; cap = NullCap \<Longrightarrow> R; \<And>ptr badge rights. \<lbrakk> cap = NotificationCap ptr badge rights \<rbrakk> \<Longrightarrow> R \<rbrakk> \<Longrightarrow> R"
|
|
unfolding separate_cap_def
|
|
by (fastforce split: cap.splits)
|
|
|
|
definition
|
|
"separate_cnode_cap cs cap \<equiv> case cap of
|
|
CNodeCap p bits guard \<Rightarrow> (bits + length guard = word_bits) \<and>
|
|
(\<forall>off. case_option True separate_cap (cs (p, off)))
|
|
| NullCap \<Rightarrow> True
|
|
| _ \<Rightarrow> False"
|
|
|
|
definition
|
|
"separate_tcb p cs \<equiv> case_option True (separate_cnode_cap cs) (cs (p, tcb_cnode_index 0))
|
|
\<and> cs (p, tcb_cnode_index 3) = Some NullCap" \<comment> \<open>ctable and caller cap\<close>
|
|
|
|
lemma separate_cnode_cap_rab:
|
|
"\<lbrakk> separate_cnode_cap cs cap; length cref = word_bits \<rbrakk> \<Longrightarrow>
|
|
resolve_address_bits (cap, cref) = (case cap of
|
|
CNodeCap p bits guard \<Rightarrow> if guard \<le> cref then
|
|
returnOk ((p, drop (length guard) cref), [])
|
|
else
|
|
(throwError (GuardMismatch (length cref) guard))
|
|
| _ \<Rightarrow> throwError InvalidRoot)"
|
|
unfolding separate_cnode_cap_def resolve_address_bits_def
|
|
by (auto simp: word_bits_def split: cap.split_asm)
|
|
|
|
definition
|
|
"separate_state s \<equiv> \<forall>p. tcb_at p s \<longrightarrow> separate_tcb p (caps_of_state s)"
|
|
|
|
|
|
lemma separate_cnode_capE:
|
|
"\<lbrakk> separate_cnode_cap cs cap;
|
|
cap = NullCap \<Longrightarrow> R;
|
|
\<And>p bits guard. \<lbrakk> cap = CNodeCap p bits guard; bits + length guard = word_bits;
|
|
(\<forall>off cap'. cs (p, off) = Some cap' \<longrightarrow> separate_cap cap') \<rbrakk> \<Longrightarrow> R \<rbrakk>
|
|
\<Longrightarrow> R"
|
|
unfolding separate_cnode_cap_def
|
|
by (auto split: cap.splits option.splits)
|
|
|
|
lemma valid_sep_cap_not_cnode:
|
|
"\<lbrakk> s \<turnstile> cap; \<forall>off cap'. caps_of_state s (p, off) = Some cap' \<longrightarrow> separate_cap cap'; cap = CNodeCap p bits guard; bits \<le> length cref - length guard \<rbrakk>
|
|
\<Longrightarrow> \<exists>cap'. caps_of_state s (p, take bits (drop (length guard) cref)) = Some cap' \<and> \<not> is_cnode_cap cap'"
|
|
apply (clarsimp simp: valid_cap_simps not_less in_monad)
|
|
apply (drule_tac offset = "take bits (drop (length guard) cref)" in cap_table_at_cte_at)
|
|
apply simp
|
|
apply (fastforce simp: cte_wp_at_caps_of_state separate_cap_def is_cap_simps)
|
|
done
|
|
|
|
lemma bisim_gen_asm_r:
|
|
assumes bs: "F \<Longrightarrow> bisim_underlying sr r P P' a b"
|
|
shows "bisim_underlying sr r P (P' and K F) a b"
|
|
using bs
|
|
by (fastforce intro!: bisim_underlyingI elim: bisim_underlyingE1 bisim_underlyingE2)
|
|
|
|
lemma bisim_separate_cap_cases:
|
|
assumes nc: "cap = NullCap \<Longrightarrow> bisim R Pn Pn' m m'"
|
|
and ac: "\<And>ptr badge rights. \<lbrakk> cap = NotificationCap ptr badge rights \<rbrakk>
|
|
\<Longrightarrow> bisim R (Pa ptr badge rights) (Pa' ptr badge rights) m m'"
|
|
shows "bisim R (\<lambda>s. (cap = NullCap \<longrightarrow> Pn s)
|
|
\<and> (\<forall>ptr badge rights. cap = NotificationCap ptr badge rights \<longrightarrow> Pa ptr badge rights s))
|
|
((\<lambda>s. (cap = NullCap \<longrightarrow> Pn' s)
|
|
\<and> (\<forall>ptr badge rights. cap = NotificationCap ptr badge rights \<longrightarrow> Pa' ptr badge rights s))
|
|
and K (separate_cap cap)) m m'"
|
|
using assms
|
|
apply -
|
|
apply (rule bisim_gen_asm_r)
|
|
apply (erule separate_capE, simp_all)
|
|
done
|
|
|
|
lemma caps_of_state_tcb:
|
|
"\<lbrakk> get_tcb p s = Some tcb; option_map fst (tcb_cap_cases idx) = Some getF \<rbrakk> \<Longrightarrow> caps_of_state s (p, idx) = Some (getF tcb)"
|
|
apply (drule get_tcb_SomeD)
|
|
apply clarsimp
|
|
apply (drule (1) cte_wp_at_tcbI [where t = "(p, idx)" and P = "(=) (getF tcb)", simplified])
|
|
apply simp
|
|
apply (clarsimp simp: cte_wp_at_caps_of_state)
|
|
done
|
|
|
|
lemma caps_of_state_tcb_cap_cases:
|
|
"\<lbrakk> get_tcb p s = Some tcb; idx \<in> dom tcb_cap_cases \<rbrakk> \<Longrightarrow> caps_of_state s (p, idx) = Some ((the (option_map fst (tcb_cap_cases idx))) tcb)"
|
|
apply (clarsimp simp: dom_def)
|
|
apply (erule caps_of_state_tcb)
|
|
apply simp
|
|
done
|
|
|
|
lemma separate_state_get_tcbD:
|
|
"\<lbrakk>separate_state s; get_tcb p s = Some tcb \<rbrakk> \<Longrightarrow>
|
|
separate_cnode_cap (caps_of_state s) (tcb_ctable tcb) \<and> tcb_caller tcb = NullCap"
|
|
unfolding separate_state_def
|
|
apply (drule spec [where x = p])
|
|
apply (simp add: tcb_at_def separate_tcb_def caps_of_state_tcb_cap_cases dom_tcb_cap_cases)
|
|
done
|
|
|
|
end
|