theory Unix imports
Nested_Environment "HOL-Library.Sublist" begin
text\<open>
We give a simple mathematical model of the basic structures underlying the
Unix file-system, together with a few fundamental operations that could be
imagined to be performed internally by the Unix kernel. This forms the basis for the set of Unix system-calls to be introduced later (see \secref{sec:unix-trans}), which are the actual interface offered to
processes running in user-space.
\<^medskip>
Basically, any Unix fileis either a \<^emph>\<open>plain file\<close> or a \<^emph>\<open>directory\<close>,
consisting of some \<^emph>\<open>content\<close> plus \<^emph>\<open>attributes\<close>. The content of a plain fileis plain text. The content of a directory is a mapping from names to
further files.\<^footnote>\<open>In fact, this is the only way that names get associated with
files. In Unix files do \<^emph>\<open>not\<close> have a name in itself. Even more, any number
of names may be associated with the very same file due to\<^emph>\<open>hard links\<close>
(although this is excluded from our model).\<close> Attributes include information to control various ways to access the file (read, write etc.).
Our model will be quite liberal in omitting excessive detail that is easily
seen to be ``irrelevant''for the aspects of Unix file-systems to be
discussed here. First of all, we ignore character and block special files,
pipes, sockets, hard links, symbolic links, and mount points. \<close>
subsection \<open>Names\<close>
text\<open>
User ids andfile name components shall be represented by natural numbers
(without loss of generality). We do not bother about encoding of actual
names (e.g.\ strings), nor a mapping between user names and user ids as
would be present in a reality. \<close>
text\<open>
Unix file attributes mainly consist of \<^emph>\<open>owner\<close> information and a number of \<^emph>\<open>permission\<close> bits which control access for ``user'', ``group'', and
``others'' (see the Unix man pages \<open>chmod(2)\<close> and \<open>stat(2)\<close> for more
details).
\<^medskip>
Our model of file permissions only considers the ``others'' part. The
``user'' field may be omitted without loss of overall generality, since the
owner is usually able to change it anyway by performing \<open>chmod\<close>.\<^footnote>\<open>The
inclined Unix expert may try to figure out some exotic arrangements of a
real-world Unix file-system such that the owner of a fileis unable toapply
the \<open>chmod\<close> system call.\<close> We omit ``group'' permissions as a genuine
simplification as we just do not intend to discuss a model of multiple
groups and group membership, but pretend that everyone is member of a single global group.\<^footnote>\<open>A general HOL model of user group structures and related
issues is given in\<^cite>\<open>"Naraschewski:2001"\<close>.\<close> \<close>
text\<open> For plain files \<^term>\<open>Readable\<close> and \<^term>\<open>Writable\<close> specify read and write
access to the actual content, i.e.\ the string of text stored here. For
directories \<^term>\<open>Readable\<close> determines if the set of entry names may be
accessed, and\<^term>\<open>Writable\<close> controls the ability to create or delete any
entries (both plain files or sub-directories).
As another simplification, we ignore the \<^term>\<open>Executable\<close> permission
altogether. In reality it would indicate executable plain files (also known
as ``binaries''), or control actual lookup of directory entries (recall that
mere directory browsing is controlled via \<^term>\<open>Readable\<close>). Note that the
latter means that in order to perform any file-system operation whatsoever,
all directories encountered on the path would haveto grant \<^term>\<open>Executable\<close>. We ignore this detail and pretend that all directories
give \<^term>\<open>Executable\<close> permission to anybody. \<close>
subsection \<open>Files\<close>
text\<open> In order to model the general tree structure of a Unix file-system we use
the arbitrarily branching datatype\<^typ>\<open>('a, 'b, 'c) env\<close> from the
standard library of Isabelle/HOL \<^cite>\<open>"Bauer-et-al:2002:HOL-Library"\<close>.
This type provides constructors \<^term>\<open>Val\<close> and \<^term>\<open>Env\<close> as follows:
Here the parameter \<^typ>\<open>'a\<close> refers to plain values occurring at leaf
positions, parameter \<^typ>\<open>'b\<close> to information kept with inner branch
nodes, and parameter \<^typ>\<open>'c\<close> to the branching type of the tree structure. For our purpose we use the type instancewith\<^typ>\<open>att \<times>
string\<close> (representing plain files), \<^typ>\<open>att\<close> (for attributes of
directory nodes), and\<^typ>\<open>name\<close> (for the index type of directory nodes). \<close>
text\<open> \<^medskip>
The HOL library also provides \<^term>\<open>lookup\<close> and \<^term>\<open>update\<close> operations for general tree structures with the subsequent primitive recursive
characterizations.
Several further properties of these operations are proven in\<^cite>\<open>"Bauer-et-al:2002:HOL-Library"\<close>. These will be routinely used later on
without further notice.
\<^bigskip>
Apparently, the elements of type \<^typ>\<open>file\<close> contain an \<^typ>\<open>att\<close>
component in either case. We now define a few auxiliary operations to
manipulate this field uniformly, following the conventions forrecordtypes in Isabelle/HOL \<^cite>\<open>"Nipkow-et-al:2000:HOL"\<close>. \<close>
definition "attributes file =
(casefile of
Val (att, text) \<Rightarrow> att
| Env att dir \<Rightarrow> att)"
definition "map_attributes f file =
(casefile of
Val (att, text) \<Rightarrow> Val (f att, text)
| Env att dir \<Rightarrow> Env (f att) dir)"
lemma [simp]: "attributes (Env att dir) = att" by (simp add: attributes_def)
lemma [simp]: "attributes (map_attributes f file) = f (attributes file)" by (cases "file") (simp_all add: attributes_def map_attributes_def
split_tupled_all)
lemma [simp]: "map_attributes f (Val (att, text)) = Val (f att, text)" by (simp add: map_attributes_def)
lemma [simp]: "map_attributes f (Env att dir) = Env (f att) dir" by (simp add: map_attributes_def)
subsection \<open>Initial file-systems\<close>
text\<open>
Given a set of \<^emph>\<open>known users\<close> a file-system shall be initialized by
providing an empty home directory for each user, with read-only access for
everyone else. (Note that we may directly use the user id as home directory
name, since both typeshave been identified.) Certainly, the very root
directory is owned by the super user (who has user id 0). \<close>
definition "init users =
Env \<lparr>owner = 0, others = {Readable}\<rparr>
(\<lambda>u. if u \<in> users then Some (Env \<lparr>owner = u, others = {Readable}\<rparr> Map.empty)
else None)"
subsection \<open>Accessing file-systems\<close>
text\<open>
The main internal file-system operation is access of a fileby a user,
requesting a certain set of permissions. The resulting \<^typ>\<open>file option\<close>
indicates if a file had been present at the corresponding \<^typ>\<open>path\<close> and if
access was granted according to the permissions recorded within the
file-system.
Note that by the rules of Unix file-system security (e.g.\ \<^cite>\<open>"Tanenbaum:1992"\<close>) both the super-user and owner may always access a file
unconditionally (in our simplified model). \<close>
definition "access root path uid perms =
(case lookup root path of
None \<Rightarrow> None
| Some file\<Rightarrow> if uid = 0 \<or> uid = owner (attributes file) \<or> perms \<subseteq> others (attributes file) then Some file
else None)"
text\<open> \<^medskip>
Successful access to a certain fileis the main prerequisite for
system-calls to be applicable (cf.\ \secref{sec:unix-trans}). Any
modification of the file-system isthen performed using the basic \<^term>\<open>update\<close> operation.
\<^medskip>
We see that \<^term>\<open>access\<close> is just a wrapper for the basic \<^term>\<open>lookup\<close> function, with additional checking of attributes. Subsequently we establish
a few auxiliary facts that stem from the primitive \<^term>\<open>lookup\<close> used
within \<^term>\<open>access\<close>. \<close>
subsection \<open>Unix system calls \label{sec:unix-syscall}\<close>
text\<open>
According to established operating system design (cf.\ \<^cite>\<open>"Tanenbaum:1992"\<close>) user space processes may only initiate system operations by a fixed set of \<^emph>\<open>system-calls\<close>. This enables the kernel to enforce
certain security policies in the first place.\<^footnote>\<open>Incidently, this is the very
same principle employed by any ``LCF-style''theorem proving system
according to Milner's principle of ``correctness by construction'', such as
Isabelle/HOL itself.\<close>
\<^medskip> In our model of Unix we give a fixed datatype\<open>operation\<close> for the syntax of
system-calls, together with an inductivedefinition of file-system state transitions of the form \<open>root \<midarrow>x\<rightarrow> root'\<close> for the operational semantics. \<close>
text\<open>
The \<^typ>\<open>uid\<close> field of an operation corresponds to the \<^emph>\<open>effective user id\<close>
of the underlying process, although our model never mentions processes
explicitly. The other parameters are provided as arguments by the caller;
the \<^term>\<open>path\<close> one is common to all kinds of system-calls. \<close>
text\<open> \<^medskip> Note that we have omitted explicit \<open>Open\<close> and \<open>Close\<close> operations, pretending
that \<^term>\<open>Read\<close> and \<^term>\<open>Write\<close> would already take care of this behind
the scenes. Thus we have basically treated actual sequences of real
system-calls \<open>open\<close>--\<open>read\<close>/\<open>write\<close>--\<open>close\<close> as atomic.
In principle, this could make big a difference in a model with explicit
concurrent processes. On the other hand, even on a real Unix system the
exact scheduling of concurrent \<open>open\<close> and \<open>close\<close> operations does \<^emph>\<open>not\<close>
directly affect the success of corresponding \<open>read\<close> or \<open>write\<close>. Unix allows
several processes tohave files opened at the same time, even for writing!
Certainly, the result from reading the contents later may be hard to
predict, but the system-calls involved here will succeed in any case.
\<^bigskip>
The operational semantics of system calls is now specified via transitions
of the file-system configuration. This is expressed as an inductive relation
(although there is no actual recursion involved here). \<close>
text\<open> \<^medskip>
Certainly, the above specificationis central to the whole formal
development. Any of the results to be established later on are only
meaningful to the outside world if this transition system provides an
adequate model of real Unix systems. This kind of ``reality-check'' of a
formal model is the well-known problem of \<^emph>\<open>validation\<close>.
Ifin doubt, one may consider to compare our definitionwith the informal
specifications given the corresponding Unix man pages, or even peek at an
actual implementation such as \<^cite>\<open>"Torvalds-et-al:Linux"\<close>. Another common
way to gain confidence into the formal model isto run simple simulations
(see \secref{sec:unix-examples}), and check the results with that of
experiments performed on a real Unix system. \<close>
subsection \<open>Basic properties of single transitions \label{sec:unix-single-trans}\<close>
text\<open>
The transition system \<open>root \<midarrow>x\<rightarrow> root'\<close> defined above determines a unique
result \<^term>\<open>root'\<close> from given \<^term>\<open>root\<close> and \<^term>\<open>x\<close> (this is
holds rather trivially, since there is even only one clause for each
operation). This uniqueness statement will simplify our subsequent
development to some extent, since we only haveto reason about a partial function rather than a general relation. \<close>
theorem transition_uniq: assumes root': "root \x\ root'" and root'': "root \x\ root''" shows"root' = root''" using root'' proof cases case read with root' show ?thesis by cases auto next case"write" with root' show ?thesis by cases auto next case chmod with root' show ?thesis by cases auto next case creat with root' show ?thesis by cases auto next case unlink with root' show ?thesis by cases auto next case mkdir with root' show ?thesis by cases auto next case rmdir with root' show ?thesis by cases auto next case readdir with root' show ?thesis by cases blast+ qed
text\<open> \<^medskip>
Apparently, file-system transitions are \<^emph>\<open>type-safe\<close> in the sense that the
result of transforming an actual directory yields again a directory. \<close>
theorem transition_type_safe: assumes tr: "root \x\ root'" and inv: "\att dir. root = Env att dir" shows"\att dir. root' = Env att dir" proof (cases "path_of x") case Nil with tr inv show ?thesis by cases (auto simp add: access_def split: if_splits) next case Cons from tr obtain opt where "root' = root \ root' = update (path_of x) opt root" by cases auto with inv Cons show ?thesis by (auto simp add: update_eq split: list.splits) qed
text\<open>
The previous result may be seen as the most basic invariant on the
file-system state that is enforced by any proper kernel implementation. So
user processes --- being bound to the system-call interface --- may never
mess up a file-system such that the root becomes a plain file instead of a
directory, which would be a strange situation indeed. \<close>
subsection \<open>Iterated transitions\<close>
text\<open>
Iterated system transitions via finite sequences of system operations are
modeled inductively as follows. In a sense, this relation describes the
cumulative effect of the sequence of system-calls issued by a number of
running processes over a finite amount of time. \<close>
inductivetransitions :: "file \ operation list \ file \ bool"
(\<open>(\<open>open_block notation=\<open>mixfix transitions\<close>\<close>_ \<Midarrow>_\<Rightarrow> _)\<close> [90, 1000, 90] 100) where
nil: "root \[]\ root"
| cons: "root \(x # xs)\ root''" if "root \x\ root'" and "root' \xs\ root''"
text\<open> \<^medskip>
We establish a few basic facts relating iterated transitionswith single
ones, according to the recursive structure of lists. \<close>
lemma transitions_nil_eq: "root \[]\ root' \ root = root'" proof assume"root \[]\ root'" thenshow"root = root'"by cases simp_all next assume"root = root'" thenshow"root \[]\ root'" by (simp only: transitions.nil) qed
text\<open>
The next two rules show how to ``destruct'' known transition sequences. Note
that the second one actually relies on the uniqueness property of the basic
transition system (see \secref{sec:unix-single-trans}). \<close>
lemma transitions_consD: assumes list: "root \(x # xs)\ root''" and hd: "root \x\ root'" shows"root' \xs\ root''" proof - from list obtain r' where r': "root \x\ r'" and root'': "r' \xs\ root''" by cases simp_all from r' hd have "r' = root'" by (rule transition_uniq) with root''show"root' \xs\ root''" by simp qed
text\<open> \<^medskip>
The following fact shows how an invariant \<^term>\<open>Q\<close> of single transitions with property \<^term>\<open>P\<close> may be transferred to iterated transitions. The proofis rather obvious by rule induction over the definition of \<^term>\<open>root \<Midarrow>xs\<Rightarrow> root'\<close>. \<close>
lemma transitions_invariant: assumes r: "\r x r'. r \x\ r' \ Q r \ P x \ Q r'" and trans: "root \xs\ root'" shows"Q root \ \x \ set xs. P x \ Q root'" using trans proof induct case nil show ?caseby (rule nil.prems) next case (cons root x root' xs root'') note P = \<open>\<forall>x \<in> set (x # xs). P x\<close> thenhave"P x"by simp with\<open>root \<midarrow>x\<rightarrow> root'\<close> and \<open>Q root\<close> have Q': "Q root'" by (rule r) from P have"\x \ set xs. P x" by simp with Q' show "Q root''" by (rule cons.hyps) qed
text\<open>
As an example of applying the previous result, we transfer the basic
type-safety property (see \secref{sec:unix-single-trans}) from single transitionsto iterated ones, which is a rather obvious result indeed. \<close>
theorem transitions_type_safe: assumes"root \xs\ root'" and"\att dir. root = Env att dir" shows"\att dir. root' = Env att dir" using transition_type_safe and assms proof (rule transitions_invariant) show"\x \ set xs. True" by blast qed
section \<open>Executable sequences\<close>
text\<open>
An inductively defined relation such as the one of \<open>root \<midarrow>x\<rightarrow> root'\<close> (see \secref{sec:unix-syscall}) has two main aspects. First of all, the resulting
system admits a certain set of transition rules (introductions) as given in
the specification. Furthermore, there is an explicit least-fixed-point
construction involved, which results ininduction (and case-analysis) rules to eliminate known transitionsin an exhaustive manner.
\<^medskip>
Subsequently, we explore our transition system in an experimental style,
mainly using the introduction rules with basic algebraic properties of the
underlying structures. The technique closely resembles that of Prolog
combined with functional evaluation in a very simple manner.
Just as the ``closed-world assumption''is left implicit in Prolog, we do
not refer toinduction over the whole transition system here. So this is
still purely positive reasoning about possible executions; exhaustive
reasoning will be employed only later on (see \secref{sec:unix-bogosity}),
when we shall demonstrate that certain behavior is\<^emph>\<open>not\<close> possible. \<close>
subsection \<open>Possible transitions\<close>
text\<open>
Rather obviously, a list of system operations can be executed within a
certain state if there is a result state reached by an iterated transition. \<close>
text\<open> \<^medskip> Incase that we already know that a certain sequence can be executed we may
destruct it backwardly into individual transitions. \<close>
lemma can_exec_snocD: "can_exec root (xs @ [y]) \<Longrightarrow> \<exists>root' root''. root \<Midarrow>xs\<Rightarrow> root' \<and> root' \<midarrow>y\<rightarrow> root''" proof (induct xs arbitrary: root) case Nil thenshow ?case by (simp add: can_exec_def transitions_nil_eq transitions_cons_eq) next case (Cons x xs) from\<open>can_exec root ((x # xs) @ [y])\<close> obtain r root'' where
x: "root \x\ r" and
xs_y: "r \(xs @ [y])\ root''" by (auto simp add: can_exec_def transitions_nil_eq transitions_cons_eq) from xs_y Cons.hyps obtain root' r' where xs: "r \xs\ root'" and y: "root' \y\ r'" unfolding can_exec_def by blast from x xs have"root \(x # xs)\ root'" by (rule transitions.cons) with y show ?caseby blast qed
text\<open>
We are now ready to perform a few experiments within our formal model of
Unix system-calls. The common technique isto alternate introduction rules
of the transition system (see \secref{sec:unix-trans}), and steps to solve
any emerging side conditions using algebraic properties of the underlying
file-system structures (see \secref{sec:unix-file-system}). \<close>
text\<open> By inspecting the result shown just before the final step above, we may gain
confidence that our specification of Unix system-calls actually makes sense.
Further common errors are usually exhibited when preconditions of transition
rules fail unexpectedly.
\<^medskip>
Here are a few further experiments, using the same techniques as before. \<close>
text\<open>
We are now ready to give a completely formal treatment of the slightly odd
situation discussed in the introduction (see \secref{sec:unix-intro}): the
file-system can easily reach a state where a user is unable to remove his
very own directory, because it is still populated by items placed there by
another user in an uncouth manner. \<close>
subsection \<open>The general procedure \label{sec:unix-inv-procedure}\<close>
text\<open>
The following theorem expresses the general procedure we are following to
achieve the main result. \<close>
theorem general_procedure: assumes cannot_y: "\r r'. Q r \ r \y\ r' \ False" and init_inv: "\root. init users \bs\ root \ Q root" and preserve_inv: "\r x r'. r \x\ r' \ Q r \ P x \ Q r'" and init_result: "init users \bs\ root" shows"\ (\xs. (\x \ set xs. P x) \ can_exec root (xs @ [y]))" proof -
{ fix xs assume Ps: "\x \ set xs. P x" assume can_exec: "can_exec root (xs @ [y])" thenobtain root' root'' where xs: "root \xs\ root'" and y: "root' \y\ root''" by (blast dest: can_exec_snocD) from init_result have"Q root"by (rule init_inv) from preserve_inv xs this Ps have"Q root'" by (rule transitions_invariant) from this y have False by (rule cannot_y)
} thenshow ?thesis by blast qed
text\<open>
Here \<^prop>\<open>P x\<close> refers to the restriction on file-system operations that
are admitted after having reached the critical configuration; according to
the problem specification this will become \<^prop>\<open>uid_of x = user\<^sub>1\<close> later
on. Furthermore, \<^term>\<open>y\<close> refers to the operations we claim to be
impossible to perform afterwards, we will take \<^term>\<open>Rmdir\<close> later. Moreover \<^term>\<open>Q\<close> is a suitable (auxiliary) invariant over the file-system; choosing \<^term>\<open>Q\<close> adequately is very important to make the proof work (see \secref{sec:unix-inv-lemmas}). \<close>
text\<open>
We introduce a few global declarations andaxiomsto describe our particular
situation considered here. Thus we avoid excessive use of local parameters in the subsequent development. \<close>
context fixes users :: "uid set" and user\<^sub>1 :: uid and user\<^sub>2 :: uid and name\<^sub>1 :: name and name\<^sub>2 :: name and name\<^sub>3 :: name and perms\<^sub>1 :: perms and perms\<^sub>2 :: perms assumes user\<^sub>1_known: "user\<^sub>1 \<in> users" and user\<^sub>1_not_root: "user\<^sub>1 \<noteq> 0" and users_neq: "user\<^sub>1 \ user\<^sub>2" and perms\<^sub>1_writable: "Writable \<in> perms\<^sub>1" and perms\<^sub>2_not_writable: "Writable \<notin> perms\<^sub>2" notes facts = user\<^sub>1_known user\<^sub>1_not_root users_neq
perms\<^sub>1_writable perms\<^sub>2_not_writable begin
text\<open>
The \<^term>\<open>bogus\<close> operations are the ones that lead into the uncouth
situation; \<^term>\<open>bogus_path\<close> is the key position within the file-system where things go awry. \<close>
text\<open>
The following invariant over the root file-system describes the bogus
situation in an abstract manner: located at a certain \<^term>\<open>path\<close> within
the file-system is a non-empty directory that is neither owned nor writable by\<^term>\<open>user\<^sub>1\<close>. \<close>
definition "invariant root path \
(\<exists>att dir.
access root path user\<^sub>1 {} = Some (Env att dir) \<and> dir \<noteq> Map.empty \<and>
user\<^sub>1 \<noteq> owner att \<and>
access root path user\<^sub>1 {Writable} = None)"
text\<open>
Following the general procedure (see \secref{sec:unix-inv-procedure}) we
will now establish the three key lemmas required to yield the final result.
\<^enum> The invariant is sufficiently strong to entail the pathological case
that \<^term>\<open>user\<^sub>1\<close> is unable to remove the (owned) directory at \<^term>\<open>[user\<^sub>1, name\<^sub>1]\<close>.
\<^enum> The invariant does hold after having executed the \<^term>\<open>bogus\<close> list of
operations (starting with an initial file-system configuration).
\<^enum> The invariant is preserved by any file-system operation performed by \<^term>\<open>user\<^sub>1\<close> alone, without any help by other users.
As the invariant appears both as assumptions and conclusions in the course
of proof, its formulation is rather critical for the whole development to
work out properly. In particular, the third step is very sensitive to the
invariant being either too strong or too weak. Moreover the invariant has to
be sufficiently abstract, lest the proof become cluttered by confusing
detail.
\<^medskip>
The first two lemmas are technically straight forward --- we just haveto
inspect rather special cases. \<close>
lemma cannot_rmdir: assumes inv: "invariant root bogus_path" and rmdir: "root \(Rmdir user\<^sub>1 [user\<^sub>1, name\<^sub>1])\ root'" shows False proof - from inv obtain"file"where"access root bogus_path user\<^sub>1 {} = Some file" unfolding invariant_def by blast moreover from rmdir obtain att where"access root [user\<^sub>1, name\<^sub>1] user\<^sub>1 {} = Some (Env att Map.empty)" by cases auto thenhave"access root ([user\<^sub>1, name\<^sub>1] @ [name\<^sub>2]) user\<^sub>1 {} = Map.empty name\<^sub>2" by (simp only: access_empty_lookup lookup_append_some) simp ultimatelyshow False by (simp add: bogus_path_def) qed
text\<open> \<^medskip>
At last we are left with the main effort toshow that the ``bogosity''
invariant is preserved by any file-system operation performed by \<^term>\<open>user\<^sub>1\<close> alone. Note that this holds for any \<^term>\<open>path\<close> given,
the particular \<^term>\<open>bogus_path\<close> is not required here. \<close>
lemma preserve_invariant: assumes tr: "root \x\ root'" and inv: "invariant root path" and uid: "uid_of x = user\<^sub>1" shows"invariant root' path" proof - from inv obtain att dir where
inv1: "access root path user\<^sub>1 {} = Some (Env att dir)" and
inv2: "dir \ Map.empty" and
inv3: "user\<^sub>1 \ owner att" and
inv4: "access root path user\<^sub>1 {Writable} = None" by (auto simp add: invariant_def)
from inv1 have lookup: "lookup root path = Some (Env att dir)" by (simp only: access_empty_lookup)
from inv1 inv3 inv4 and user\<^sub>1_not_root have not_writable: "Writable \ others att" by (auto simp add: access_def split: option.splits)
show ?thesis proof cases assume"root' = root" with inv show"invariant root' path"by (simp only:) next assume changed: "root' \ root" with tr obtain opt where root': "root' = update (path_of x) opt root" by cases auto show ?thesis proof (rule prefix_cases) assume"path_of x \ path" with inv root' have"\perms. access root' path user\<^sub>1 perms = access root path user\<^sub>1 perms" by (simp only: access_update_other) with inv show"invariant root' path" by (simp only: invariant_def) next assume"prefix (path_of x) path" thenobtain ys where path: "path = path_of x @ ys" ..
show ?thesis proof (cases ys) assume"ys = []" with tr uid inv2 inv3 lookup changed path and user\<^sub>1_not_root have False by cases (auto simp add: access_empty_lookup dest: access_some_lookup) thenshow ?thesis .. next fix z zs assume ys: "ys = z # zs" have"lookup root' path = lookup root path" proof - from inv2 lookup path ys have look: "lookup root (path_of x @ z # zs) = Some (Env att dir)" by (simp only:) thenobtain att' dir'file' where
look': "lookup root (path_of x) = Some (Env att' dir')" and
dir': "dir' z = Some file'" and file': "lookup file' zs = Some (Env att dir)" by (blast dest: lookup_some_upper)
from tr uid changed look' dir'obtain att''where
look'': "lookup root' (path_of x) = Some (Env att'' dir')" by cases (auto simp add: access_empty_lookup lookup_update_some
dest: access_some_lookup) with dir' file'have"lookup root' (path_of x @ z # zs) =
Some (Env att dir)" by (simp add: lookup_append_some) with look path ys show ?thesis by simp qed with inv show"invariant root' path" by (simp only: invariant_def access_def) qed next assume"strict_prefix path (path_of x)" thenobtain y ys where path: "path_of x = path @ y # ys" ..
obtain dir' where
lookup': "lookup root' path = Some (Env att dir')" and
inv2': "dir'\<noteq> Map.empty" proof (cases ys) assume"ys = []" with path have parent: "path_of x = path @ [y]"by simp with tr uid inv4 changed obtain"file"where "root' = update (path_of x) (Some file) root" by cases auto with lookup parent have"lookup root' path = Some (Env att (dir(y\file)))" by (simp only: update_append_some update_cons_nil_env) moreoverhave"dir(y\file) \ Map.empty" by simp ultimatelyshow ?thesis .. next fix z zs assume ys: "ys = z # zs" with lookup root' path have"lookup root' path = Some (update (y # ys) opt (Env att dir))" by (simp only: update_append_some) alsoobtainfile' where "update (y # ys) opt (Env att dir) = Env att (dir(y\file'))" proof - have"dir y \ None" proof - have"dir y = lookup (Env att dir) [y]" by (simp split: option.splits) alsofrom lookup have"\ = lookup root (path @ [y])" by (simp only: lookup_append_some) alsohave"\ \ None" proof - from ys obtain us u where rev_ys: "ys = us @ [u]" by (cases ys rule: rev_cases) simp with tr path have"lookup root ((path @ [y]) @ (us @ [u])) \ None \
lookup root ((path @ [y]) @ us) \<noteq> None" by cases (auto dest: access_some_lookup) thenshow ?thesis by (fastforce dest!: lookup_some_append) qed finallyshow ?thesis . qed with ys show ?thesis using that by auto qed alsohave"dir(y\file') \ Map.empty" by simp ultimatelyshow ?thesis .. qed
from lookup' have inv1': "access root' path user\<^sub>1 {} = Some (Env att dir')" by (simp only: access_empty_lookup)
from inv3 lookup' and not_writable user\<^sub>1_not_root have"access root' path user\<^sub>1 {Writable} = None" by (simp add: access_def) with inv1' inv2' inv3 show ?thesis unfolding invariant_def by blast qed qed qed
subsection \<open>Putting it all together \label{sec:unix-main-result}\<close>
text\<open>
The main result is now imminent, just by composing the three invariance lemmas (see \secref{sec:unix-inv-lemmas}) according the the overall
procedure (see \secref{sec:unix-inv-procedure}). \<close>
corollary assumes bogus: "init users \bogus\ root" shows"\ (\xs. (\x \ set xs. uid_of x = user\<^sub>1) \
can_exec root (xs @ [Rmdir user\<^sub>1 [user\<^sub>1, name\<^sub>1]]))" proof - from cannot_rmdir init_invariant preserve_invariant and bogus show ?thesis by (rule general_procedure) qed
end
end
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.