src/HOL/Unix/Unix.thy
changeset 17455 151e76f0e3c7
parent 17146 67e9b86ed211
child 18372 2bffdf62fe7f
equal deleted inserted replaced
17454:a9adc5099c43 17455:151e76f0e3c7
     3     Author:     Markus Wenzel, TU Muenchen
     3     Author:     Markus Wenzel, TU Muenchen
     4 *)
     4 *)
     5 
     5 
     6 header {* Unix file-systems \label{sec:unix-file-system} *}
     6 header {* Unix file-systems \label{sec:unix-file-system} *}
     7 
     7 
     8 theory Unix imports Nested_Environment List_Prefix begin
     8 theory Unix
       
     9 imports Nested_Environment List_Prefix
       
    10 begin
     9 
    11 
    10 text {*
    12 text {*
    11   We give a simple mathematical model of the basic structures
    13   We give a simple mathematical model of the basic structures
    12   underlying the Unix file-system, together with a few fundamental
    14   underlying the Unix file-system, together with a few fundamental
    13   operations that could be imagined to be performed internally by the
    15   operations that could be imagined to be performed internally by the
   461   next
   463   next
   462     case rmdir
   464     case rmdir
   463     with root show ?thesis by cases auto
   465     with root show ?thesis by cases auto
   464   next
   466   next
   465     case readdir
   467     case readdir
   466     with root show ?thesis by cases (simp (asm_lr))+
   468     with root show ?thesis by cases fastsimp+
   467   qed
   469   qed
   468 qed
   470 qed
   469 
   471 
   470 text {*
   472 text {*
   471   \medskip Apparently, file-system transitions are \emph{type-safe} in
   473   \medskip Apparently, file-system transitions are \emph{type-safe} in
   624 
   626 
   625 
   627 
   626 section {* Executable sequences *}
   628 section {* Executable sequences *}
   627 
   629 
   628 text {*
   630 text {*
   629   An inductively defined relation such as the one of
   631   An inductively defined relation such as the one of @{text "root \<midarrow>x\<rightarrow>
   630   @{text "root \<midarrow>x\<rightarrow> root'"} (see \secref{sec:unix-syscall}) has two
   632   root'"} (see \secref{sec:unix-syscall}) has two main aspects.  First
   631   main aspects.  First of all, the resulting system admits a certain
   633   of all, the resulting system admits a certain set of transition
   632   set of transition rules (introductions) as given in the
   634   rules (introductions) as given in the specification.  Furthermore,
   633   specification.  Furthermore, there is an explicit least-fixed-point
   635   there is an explicit least-fixed-point construction involved, which
   634   construction involved, which results in induction (and
   636   results in induction (and case-analysis) rules to eliminate known
   635   case-analysis) rules to eliminate known transitions in an exhaustive
   637   transitions in an exhaustive manner.
   636   manner.
       
   637 
   638 
   638   \medskip Subsequently, we explore our transition system in an
   639   \medskip Subsequently, we explore our transition system in an
   639   experimental style, mainly using the introduction rules with basic
   640   experimental style, mainly using the introduction rules with basic
   640   algebraic properties of the underlying structures.  The technique
   641   algebraic properties of the underlying structures.  The technique
   641   closely resembles that of Prolog combined with functional evaluation
   642   closely resembles that of Prolog combined with functional evaluation
   753   apply (simp add: eval)
   754   apply (simp add: eval)
   754   txt {* peek at result: @{subgoals [display]} *}
   755   txt {* peek at result: @{subgoals [display]} *}
   755   apply (rule can_exec_nil)
   756   apply (rule can_exec_nil)
   756   done
   757   done
   757 
   758 
   758 theorem "u \<in> users \<Longrightarrow> Writable \<in> perms1 \<Longrightarrow>
   759 theorem "u \<in> users \<Longrightarrow> Writable \<in> perms\<^isub>1 \<Longrightarrow>
   759   Readable \<in> perms2 \<Longrightarrow> name3 \<noteq> name4 \<Longrightarrow>
   760   Readable \<in> perms\<^isub>2 \<Longrightarrow> name\<^isub>3 \<noteq> name\<^isub>4 \<Longrightarrow>
   760   can_exec (init users)
   761   can_exec (init users)
   761    [Mkdir u perms1 [u, name1],
   762    [Mkdir u perms\<^isub>1 [u, name\<^isub>1],
   762     Mkdir u' perms2 [u, name1, name2],
   763     Mkdir u' perms\<^isub>2 [u, name\<^isub>1, name\<^isub>2],
   763     Creat u' perms3 [u, name1, name2, name3],
   764     Creat u' perms\<^isub>3 [u, name\<^isub>1, name\<^isub>2, name\<^isub>3],
   764     Creat u' perms3 [u, name1, name2, name4],
   765     Creat u' perms\<^isub>3 [u, name\<^isub>1, name\<^isub>2, name\<^isub>4],
   765     Readdir u {name3, name4} [u, name1, name2]]"
   766     Readdir u {name\<^isub>3, name\<^isub>4} [u, name\<^isub>1, name\<^isub>2]]"
   766   apply (rule can_exec_cons, rule transition.intros,
   767   apply (rule can_exec_cons, rule transition.intros,
   767     (force simp add: eval)+, (simp add: eval)?)+
   768     (force simp add: eval)+, (simp add: eval)?)+
   768   txt {* peek at result: @{subgoals [display]} *}
   769   txt {* peek at result: @{subgoals [display]} *}
   769   apply (rule can_exec_nil)
   770   apply (rule can_exec_nil)
   770   done
   771   done
   771 
   772 
   772 theorem "u \<in> users \<Longrightarrow> Writable \<in> perms1 \<Longrightarrow> Readable \<in> perms3 \<Longrightarrow>
   773 theorem "u \<in> users \<Longrightarrow> Writable \<in> perms\<^isub>1 \<Longrightarrow> Readable \<in> perms\<^isub>3 \<Longrightarrow>
   773   can_exec (init users)
   774   can_exec (init users)
   774    [Mkdir u perms1 [u, name1],
   775    [Mkdir u perms\<^isub>1 [u, name\<^isub>1],
   775     Mkdir u' perms2 [u, name1, name2],
   776     Mkdir u' perms\<^isub>2 [u, name\<^isub>1, name\<^isub>2],
   776     Creat u' perms3 [u, name1, name2, name3],
   777     Creat u' perms\<^isub>3 [u, name\<^isub>1, name\<^isub>2, name\<^isub>3],
   777     Write u' ''foo'' [u, name1, name2, name3],
   778     Write u' ''foo'' [u, name\<^isub>1, name\<^isub>2, name\<^isub>3],
   778     Read u ''foo'' [u, name1, name2, name3]]"
   779     Read u ''foo'' [u, name\<^isub>1, name\<^isub>2, name\<^isub>3]]"
   779   apply (rule can_exec_cons, rule transition.intros,
   780   apply (rule can_exec_cons, rule transition.intros,
   780     (force simp add: eval)+, (simp add: eval)?)+
   781     (force simp add: eval)+, (simp add: eval)?)+
   781   txt {* peek at result: @{subgoals [display]} *}
   782   txt {* peek at result: @{subgoals [display]} *}
   782   apply (rule can_exec_nil)
   783   apply (rule can_exec_nil)
   783   done
   784   done
   829 
   830 
   830 text {*
   831 text {*
   831   Here @{prop "P x"} refers to the restriction on file-system
   832   Here @{prop "P x"} refers to the restriction on file-system
   832   operations that are admitted after having reached the critical
   833   operations that are admitted after having reached the critical
   833   configuration; according to the problem specification this will
   834   configuration; according to the problem specification this will
   834   become @{prop "uid_of x = user1"} later on.  Furthermore, @{term y}
   835   become @{prop "uid_of x = user\<^isub>1"} later on.  Furthermore,
   835   refers to the operations we claim to be impossible to perform
   836   @{term y} refers to the operations we claim to be impossible to
   836   afterwards, we will take @{term Rmdir} later.  Moreover @{term Q} is
   837   perform afterwards, we will take @{term Rmdir} later.  Moreover
   837   a suitable (auxiliary) invariant over the file-system; choosing
   838   @{term Q} is a suitable (auxiliary) invariant over the file-system;
   838   @{term Q} adequately is very important to make the proof work (see
   839   choosing @{term Q} adequately is very important to make the proof
   839   \secref{sec:unix-inv-lemmas}).
   840   work (see \secref{sec:unix-inv-lemmas}).
   840 *}
   841 *}
   841 
   842 
   842 
   843 
   843 subsection {* The particular situation *}
   844 subsection {* The particular situation *}
   844 
   845 
   848   of local parameters in the subsequent development.
   849   of local parameters in the subsequent development.
   849 *}
   850 *}
   850 
   851 
   851 locale situation =
   852 locale situation =
   852   fixes users :: "uid set"
   853   fixes users :: "uid set"
   853     and user1 :: uid
   854     and user\<^isub>1 :: uid
   854     and user2 :: uid
   855     and user\<^isub>2 :: uid
   855     and name1 :: name
   856     and name\<^isub>1 :: name
   856     and name2 :: name
   857     and name\<^isub>2 :: name
   857     and name3 :: name
   858     and name\<^isub>3 :: name
   858     and perms1 :: perms
   859     and perms\<^isub>1 :: perms
   859     and perms2 :: perms
   860     and perms\<^isub>2 :: perms
   860   assumes user1_known: "user1 \<in> users"
   861   assumes user\<^isub>1_known: "user\<^isub>1 \<in> users"
   861     and user1_not_root: "user1 \<noteq> 0"
   862     and user\<^isub>1_not_root: "user\<^isub>1 \<noteq> 0"
   862     and users_neq: "user1 \<noteq> user2"
   863     and users_neq: "user\<^isub>1 \<noteq> user\<^isub>2"
   863     and perms1_writable: "Writable \<in> perms1"
   864     and perms\<^isub>1_writable: "Writable \<in> perms\<^isub>1"
   864     and perms2_not_writable: "Writable \<notin> perms2"
   865     and perms\<^isub>2_not_writable: "Writable \<notin> perms\<^isub>2"
   865   notes facts = user1_known user1_not_root users_neq
   866   notes facts = user\<^isub>1_known user\<^isub>1_not_root users_neq
   866     perms1_writable perms2_not_writable
   867     perms\<^isub>1_writable perms\<^isub>2_not_writable
   867 
   868 
   868   fixes bogus :: "operation list"
   869   fixes bogus :: "operation list"
   869     and bogus_path :: path
   870     and bogus_path :: path
   870   defines "bogus \<equiv>
   871   defines "bogus \<equiv>
   871      [Mkdir user1 perms1 [user1, name1],
   872      [Mkdir user\<^isub>1 perms\<^isub>1 [user\<^isub>1, name\<^isub>1],
   872       Mkdir user2 perms2 [user1, name1, name2],
   873       Mkdir user\<^isub>2 perms\<^isub>2 [user\<^isub>1, name\<^isub>1, name\<^isub>2],
   873       Creat user2 perms2 [user1, name1, name2, name3]]"
   874       Creat user\<^isub>2 perms\<^isub>2 [user\<^isub>1, name\<^isub>1, name\<^isub>2, name\<^isub>3]]"
   874     and "bogus_path \<equiv> [user1, name1, name2]"
   875     and "bogus_path \<equiv> [user\<^isub>1, name\<^isub>1, name\<^isub>2]"
   875 
   876 
   876 text {*
   877 text {*
   877   The @{term bogus} operations are the ones that lead into the uncouth
   878   The @{term bogus} operations are the ones that lead into the uncouth
   878   situation; @{term bogus_path} is the key position within the
   879   situation; @{term bogus_path} is the key position within the
   879   file-system where things go awry.
   880   file-system where things go awry.
   884 
   885 
   885 text {*
   886 text {*
   886   The following invariant over the root file-system describes the
   887   The following invariant over the root file-system describes the
   887   bogus situation in an abstract manner: located at a certain @{term
   888   bogus situation in an abstract manner: located at a certain @{term
   888   path} within the file-system is a non-empty directory that is
   889   path} within the file-system is a non-empty directory that is
   889   neither owned and nor writable by @{term user1}.
   890   neither owned and nor writable by @{term user\<^isub>1}.
   890 *}
   891 *}
   891 
   892 
   892 locale invariant = situation +
   893 locale invariant = situation +
   893   fixes invariant :: "file \<Rightarrow> path \<Rightarrow> bool"
   894   fixes invariant :: "file \<Rightarrow> path \<Rightarrow> bool"
   894   defines "invariant root path \<equiv>
   895   defines "invariant root path \<equiv>
   895     (\<exists>att dir.
   896     (\<exists>att dir.
   896       access root path user1 {} = Some (Env att dir) \<and> dir \<noteq> empty \<and>
   897       access root path user\<^isub>1 {} = Some (Env att dir) \<and> dir \<noteq> empty \<and>
   897       user1 \<noteq> owner att \<and>
   898       user\<^isub>1 \<noteq> owner att \<and>
   898       access root path user1 {Writable} = None)"
   899       access root path user\<^isub>1 {Writable} = None)"
   899 
   900 
   900 text {*
   901 text {*
   901   Following the general procedure (see
   902   Following the general procedure (see
   902   \secref{sec:unix-inv-procedure}) we
   903   \secref{sec:unix-inv-procedure}) we will now establish the three key
   903   will now establish the three key lemmas required to yield the final
   904   lemmas required to yield the final result.
   904   result.
       
   905 
   905 
   906   \begin{enumerate}
   906   \begin{enumerate}
   907 
   907 
   908   \item The invariant is sufficiently strong to entail the
   908   \item The invariant is sufficiently strong to entail the
   909   pathological case that @{term user1} is unable to remove the (owned)
   909   pathological case that @{term user\<^isub>1} is unable to remove the
   910   directory at @{term "[user1, name1]"}.
   910   (owned) directory at @{term "[user\<^isub>1, name\<^isub>1]"}.
   911 
   911 
   912   \item The invariant does hold after having executed the @{term
   912   \item The invariant does hold after having executed the @{term
   913   bogus} list of operations (starting with an initial file-system
   913   bogus} list of operations (starting with an initial file-system
   914   configuration).
   914   configuration).
   915 
   915 
   916   \item The invariant is preserved by any file-system operation
   916   \item The invariant is preserved by any file-system operation
   917   performed by @{term user1} alone, without any help by other users.
   917   performed by @{term user\<^isub>1} alone, without any help by other
       
   918   users.
   918 
   919 
   919   \end{enumerate}
   920   \end{enumerate}
   920 
   921 
   921   As the invariant appears both as assumptions and conclusions in the
   922   As the invariant appears both as assumptions and conclusions in the
   922   course of proof, its formulation is rather critical for the whole
   923   course of proof, its formulation is rather critical for the whole
   929   we just have to inspect rather special cases.
   930   we just have to inspect rather special cases.
   930 *}
   931 *}
   931 
   932 
   932 lemma (in invariant)
   933 lemma (in invariant)
   933   cannot_rmdir: "invariant root bogus_path \<Longrightarrow>
   934   cannot_rmdir: "invariant root bogus_path \<Longrightarrow>
   934     root \<midarrow>(Rmdir user1 [user1, name1])\<rightarrow> root' \<Longrightarrow> False"
   935     root \<midarrow>(Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1])\<rightarrow> root' \<Longrightarrow> False"
   935 proof -
   936 proof -
   936   assume "invariant root bogus_path"
   937   assume "invariant root bogus_path"
   937   then obtain "file" where "access root bogus_path user1 {} = Some file"
   938   then obtain "file" where "access root bogus_path user\<^isub>1 {} = Some file"
   938     by (unfold invariant_def) blast
   939     by (unfold invariant_def) blast
   939   moreover
   940   moreover
   940   assume "root \<midarrow>(Rmdir user1 [user1, name1])\<rightarrow> root'"
   941   assume "root \<midarrow>(Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1])\<rightarrow> root'"
   941   then obtain att where
   942   then obtain att where
   942       "access root [user1, name1] user1 {} = Some (Env att empty)"
   943       "access root [user\<^isub>1, name\<^isub>1] user\<^isub>1 {} = Some (Env att empty)"
   943     by cases auto
   944     by cases auto
   944   hence "access root ([user1, name1] @ [name2]) user1 {} = empty name2"
   945   hence "access root ([user\<^isub>1, name\<^isub>1] @ [name\<^isub>2]) user\<^isub>1 {} = empty name\<^isub>2"
   945     by (simp only: access_empty_lookup lookup_append_some) simp
   946     by (simp only: access_empty_lookup lookup_append_some) simp
   946   ultimately show False by (simp add: bogus_path_def)
   947   ultimately show False by (simp add: bogus_path_def)
   947 qed
   948 qed
   948 
   949 
   949 lemma (in invariant)
   950 lemma (in invariant)
   963 qed
   964 qed
   964 
   965 
   965 text {*
   966 text {*
   966   \medskip At last we are left with the main effort to show that the
   967   \medskip At last we are left with the main effort to show that the
   967   ``bogosity'' invariant is preserved by any file-system operation
   968   ``bogosity'' invariant is preserved by any file-system operation
   968   performed by @{term user1} alone.  Note that this holds for any
   969   performed by @{term user\<^isub>1} alone.  Note that this holds for
   969   @{term path} given, the particular @{term bogus_path} is not
   970   any @{term path} given, the particular @{term bogus_path} is not
   970   required here.
   971   required here.
   971 *}
   972 *}
   972 
   973 
   973 lemma (in invariant)
   974 lemma (in invariant)
   974   preserve_invariant: "root \<midarrow>x\<rightarrow> root' \<Longrightarrow>
   975   preserve_invariant: "root \<midarrow>x\<rightarrow> root' \<Longrightarrow>
   975     invariant root path \<Longrightarrow> uid_of x = user1 \<Longrightarrow> invariant root' path"
   976     invariant root path \<Longrightarrow> uid_of x = user\<^isub>1 \<Longrightarrow> invariant root' path"
   976 proof -
   977 proof -
   977   assume tr: "root \<midarrow>x\<rightarrow> root'"
   978   assume tr: "root \<midarrow>x\<rightarrow> root'"
   978   assume inv: "invariant root path"
   979   assume inv: "invariant root path"
   979   assume uid: "uid_of x = user1"
   980   assume uid: "uid_of x = user\<^isub>1"
   980 
   981 
   981   from inv obtain att dir where
   982   from inv obtain att dir where
   982       inv1: "access root path user1 {} = Some (Env att dir)" and
   983       inv1: "access root path user\<^isub>1 {} = Some (Env att dir)" and
   983       inv2: "dir \<noteq> empty" and
   984       inv2: "dir \<noteq> empty" and
   984       inv3: "user1 \<noteq> owner att" and
   985       inv3: "user\<^isub>1 \<noteq> owner att" and
   985       inv4: "access root path user1 {Writable} = None"
   986       inv4: "access root path user\<^isub>1 {Writable} = None"
   986     by (auto simp add: invariant_def)
   987     by (auto simp add: invariant_def)
   987 
   988 
   988   from inv1 have lookup: "lookup root path = Some (Env att dir)"
   989   from inv1 have lookup: "lookup root path = Some (Env att dir)"
   989     by (simp only: access_empty_lookup)
   990     by (simp only: access_empty_lookup)
   990 
   991 
   991   from inv1 inv3 inv4 and user1_not_root
   992   from inv1 inv3 inv4 and user\<^isub>1_not_root
   992   have not_writable: "Writable \<notin> others att"
   993   have not_writable: "Writable \<notin> others att"
   993     by (auto simp add: access_def split: option.splits if_splits)
   994     by (auto simp add: access_def split: option.splits if_splits)
   994 
   995 
   995   show ?thesis
   996   show ?thesis
   996   proof cases
   997   proof cases
  1002       by cases auto
  1003       by cases auto
  1003     show ?thesis
  1004     show ?thesis
  1004     proof (rule prefix_cases)
  1005     proof (rule prefix_cases)
  1005       assume "path_of x \<parallel> path"
  1006       assume "path_of x \<parallel> path"
  1006       with inv root'
  1007       with inv root'
  1007       have "\<And>perms. access root' path user1 perms = access root path user1 perms"
  1008       have "\<And>perms. access root' path user\<^isub>1 perms = access root path user\<^isub>1 perms"
  1008         by (simp only: access_update_other)
  1009         by (simp only: access_update_other)
  1009       with inv show "invariant root' path"
  1010       with inv show "invariant root' path"
  1010         by (simp only: invariant_def)
  1011         by (simp only: invariant_def)
  1011     next
  1012     next
  1012       assume "path_of x \<le> path"
  1013       assume "path_of x \<le> path"
  1013       then obtain ys where path: "path = path_of x @ ys" ..
  1014       then obtain ys where path: "path = path_of x @ ys" ..
  1014 
  1015 
  1015       show ?thesis
  1016       show ?thesis
  1016       proof (cases ys)
  1017       proof (cases ys)
  1017         assume "ys = []"
  1018         assume "ys = []"
  1018         with tr uid inv2 inv3 lookup changed path and user1_not_root
  1019         with tr uid inv2 inv3 lookup changed path and user\<^isub>1_not_root
  1019         have False
  1020         have False
  1020           by cases (auto simp add: access_empty_lookup dest: access_some_lookup)
  1021           by cases (auto simp add: access_empty_lookup dest: access_some_lookup)
  1021         thus ?thesis ..
  1022         thus ?thesis ..
  1022       next
  1023       next
  1023         fix z zs assume ys: "ys = z # zs"
  1024         fix z zs assume ys: "ys = z # zs"
  1093         qed
  1094         qed
  1094         also have "dir(y\<mapsto>file') \<noteq> empty" by simp
  1095         also have "dir(y\<mapsto>file') \<noteq> empty" by simp
  1095         ultimately show ?thesis ..
  1096         ultimately show ?thesis ..
  1096       qed
  1097       qed
  1097 
  1098 
  1098       from lookup' have inv1': "access root' path user1 {} = Some (Env att dir')"
  1099       from lookup' have inv1': "access root' path user\<^isub>1 {} = Some (Env att dir')"
  1099         by (simp only: access_empty_lookup)
  1100         by (simp only: access_empty_lookup)
  1100 
  1101 
  1101       from inv3 lookup' and not_writable user1_not_root
  1102       from inv3 lookup' and not_writable user\<^isub>1_not_root
  1102       have "access root' path user1 {Writable} = None"
  1103       have "access root' path user\<^isub>1 {Writable} = None"
  1103         by (simp add: access_def)
  1104         by (simp add: access_def)
  1104       with inv1' inv2' inv3 show ?thesis by (unfold invariant_def) blast
  1105       with inv1' inv2' inv3 show ?thesis by (unfold invariant_def) blast
  1105     qed
  1106     qed
  1106   qed
  1107   qed
  1107 qed
  1108 qed
  1116 *}
  1117 *}
  1117 
  1118 
  1118 corollary result:
  1119 corollary result:
  1119   includes invariant
  1120   includes invariant
  1120   assumes bogus: "init users =bogus\<Rightarrow> root"
  1121   assumes bogus: "init users =bogus\<Rightarrow> root"
  1121   shows "\<not> (\<exists>xs. (\<forall>x \<in> set xs. uid_of x = user1) \<and>
  1122   shows "\<not> (\<exists>xs. (\<forall>x \<in> set xs. uid_of x = user\<^isub>1) \<and>
  1122     can_exec root (xs @ [Rmdir user1 [user1, name1]]))"
  1123     can_exec root (xs @ [Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1]]))"
  1123 proof -
  1124 proof -
  1124   from cannot_rmdir init_invariant preserve_invariant
  1125   from cannot_rmdir init_invariant preserve_invariant
  1125     and bogus show ?thesis by (rule general_procedure)
  1126     and bogus show ?thesis by (rule general_procedure)
  1126 qed
  1127 qed
  1127 
  1128 
  1128 text {*
  1129 text {*
  1129   So this is our final result:
  1130   So this is our final result:
  1130 
  1131 
  1131   @{thm [display] result [OF invariant.intro, OF situation.intro, no_vars]}
  1132   @{thm [display, show_question_marks = false] result [OF
       
  1133   invariant.intro, OF situation.intro]}
  1132 *}
  1134 *}
  1133 
  1135 
  1134 end
  1136 end