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" |
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 |