253 lemma access_some_lookup: |
253 lemma access_some_lookup: |
254 "access root path uid perms = Some file \<Longrightarrow> |
254 "access root path uid perms = Some file \<Longrightarrow> |
255 lookup root path = Some file" |
255 lookup root path = Some file" |
256 by (simp add: access_def split: option.splits if_splits) |
256 by (simp add: access_def split: option.splits if_splits) |
257 |
257 |
258 lemma access_update_other: "path' \<parallel> path \<Longrightarrow> |
258 lemma access_update_other: |
259 access (update path' opt root) path uid perms = access root path uid perms" |
259 assumes parallel: "path' \<parallel> path" |
|
260 shows "access (update path' opt root) path uid perms = access root path uid perms" |
260 proof - |
261 proof - |
261 assume "path' \<parallel> path" |
262 from parallel obtain y z xs ys zs where |
262 then obtain y z xs ys zs where |
|
263 "y \<noteq> z" and "path' = xs @ y # ys" and "path = xs @ z # zs" |
263 "y \<noteq> z" and "path' = xs @ y # ys" and "path = xs @ z # zs" |
264 by (blast dest: parallel_decomp) |
264 by (blast dest: parallel_decomp) |
265 hence "lookup (update path' opt root) path = lookup root path" |
265 then have "lookup (update path' opt root) path = lookup root path" |
266 by (blast intro: lookup_update_other) |
266 by (blast intro: lookup_update_other) |
267 thus ?thesis by (simp only: access_def) |
267 then show ?thesis by (simp only: access_def) |
268 qed |
268 qed |
269 |
269 |
270 |
270 |
271 section {* File-system transitions \label{sec:unix-trans} *} |
271 section {* File-system transitions \label{sec:unix-trans} *} |
272 |
272 |
435 simplify our subsequent development to some extent, since we only |
435 simplify our subsequent development to some extent, since we only |
436 have to reason about a partial function rather than a general |
436 have to reason about a partial function rather than a general |
437 relation. |
437 relation. |
438 *} |
438 *} |
439 |
439 |
440 theorem transition_uniq: "root \<midarrow>x\<rightarrow> root' \<Longrightarrow> root \<midarrow>x\<rightarrow> root'' \<Longrightarrow> root' = root''" |
440 theorem transition_uniq: |
441 proof - |
441 assumes root': "root \<midarrow>x\<rightarrow> root'" |
442 assume root: "root \<midarrow>x\<rightarrow> root'" |
442 and root'': "root \<midarrow>x\<rightarrow> root''" |
443 assume "root \<midarrow>x\<rightarrow> root''" |
443 shows "root' = root''" |
444 thus "root' = root''" |
444 using root'' |
445 proof cases |
445 proof cases |
446 case read |
446 case read |
447 with root show ?thesis by cases auto |
447 with root' show ?thesis by cases auto |
448 next |
448 next |
449 case write |
449 case write |
450 with root show ?thesis by cases auto |
450 with root' show ?thesis by cases auto |
451 next |
451 next |
452 case chmod |
452 case chmod |
453 with root show ?thesis by cases auto |
453 with root' show ?thesis by cases auto |
454 next |
454 next |
455 case creat |
455 case creat |
456 with root show ?thesis by cases auto |
456 with root' show ?thesis by cases auto |
457 next |
457 next |
458 case unlink |
458 case unlink |
459 with root show ?thesis by cases auto |
459 with root' show ?thesis by cases auto |
460 next |
460 next |
461 case mkdir |
461 case mkdir |
462 with root show ?thesis by cases auto |
462 with root' show ?thesis by cases auto |
463 next |
463 next |
464 case rmdir |
464 case rmdir |
465 with root show ?thesis by cases auto |
465 with root' show ?thesis by cases auto |
466 next |
466 next |
467 case readdir |
467 case readdir |
468 with root show ?thesis by cases fastsimp+ |
468 with root' show ?thesis by cases fastsimp+ |
469 qed |
|
470 qed |
469 qed |
471 |
470 |
472 text {* |
471 text {* |
473 \medskip Apparently, file-system transitions are \emph{type-safe} in |
472 \medskip Apparently, file-system transitions are \emph{type-safe} in |
474 the sense that the result of transforming an actual directory yields |
473 the sense that the result of transforming an actual directory yields |
475 again a directory. |
474 again a directory. |
476 *} |
475 *} |
477 |
476 |
478 theorem transition_type_safe: |
477 theorem transition_type_safe: |
479 "root \<midarrow>x\<rightarrow> root' \<Longrightarrow> \<exists>att dir. root = Env att dir |
478 assumes tr: "root \<midarrow>x\<rightarrow> root'" |
480 \<Longrightarrow> \<exists>att dir. root' = Env att dir" |
479 and inv: "\<exists>att dir. root = Env att dir" |
481 proof - |
480 shows "\<exists>att dir. root' = Env att dir" |
482 assume tr: "root \<midarrow>x\<rightarrow> root'" |
481 proof (cases "path_of x") |
483 assume inv: "\<exists>att dir. root = Env att dir" |
482 case Nil |
484 show ?thesis |
483 with tr inv show ?thesis |
485 proof (cases "path_of x") |
484 by cases (auto simp add: access_def split: if_splits) |
486 case Nil |
485 next |
487 with tr inv show ?thesis |
486 case Cons |
488 by cases (auto simp add: access_def split: if_splits) |
487 from tr obtain opt where |
489 next |
488 "root' = root \<or> root' = update (path_of x) opt root" |
490 case Cons |
489 by cases auto |
491 from tr obtain opt where |
490 with inv Cons show ?thesis |
492 "root' = root \<or> root' = update (path_of x) opt root" |
491 by (auto simp add: update_eq split: list.splits) |
493 by cases auto |
|
494 with inv Cons show ?thesis |
|
495 by (auto simp add: update_eq split: list.splits) |
|
496 qed |
|
497 qed |
492 qed |
498 |
493 |
499 text {* |
494 text {* |
500 The previous result may be seen as the most basic invariant on the |
495 The previous result may be seen as the most basic invariant on the |
501 file-system state that is enforced by any proper kernel |
496 file-system state that is enforced by any proper kernel |
537 *} |
532 *} |
538 |
533 |
539 lemma transitions_nil_eq: "root =[]\<Rightarrow> root' = (root = root')" |
534 lemma transitions_nil_eq: "root =[]\<Rightarrow> root' = (root = root')" |
540 proof |
535 proof |
541 assume "root =[]\<Rightarrow> root'" |
536 assume "root =[]\<Rightarrow> root'" |
542 thus "root = root'" by cases simp_all |
537 then show "root = root'" by cases simp_all |
543 next |
538 next |
544 assume "root = root'" |
539 assume "root = root'" |
545 thus "root =[]\<Rightarrow> root'" by (simp only: transitions.nil) |
540 then show "root =[]\<Rightarrow> root'" by (simp only: transitions.nil) |
546 qed |
541 qed |
547 |
542 |
548 lemma transitions_cons_eq: |
543 lemma transitions_cons_eq: |
549 "root =(x # xs)\<Rightarrow> root'' = (\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root'')" |
544 "root =(x # xs)\<Rightarrow> root'' = (\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root'')" |
550 proof |
545 proof |
551 assume "root =(x # xs)\<Rightarrow> root''" |
546 assume "root =(x # xs)\<Rightarrow> root''" |
552 thus "\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root''" |
547 then show "\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root''" |
553 by cases auto |
548 by cases auto |
554 next |
549 next |
555 assume "\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root''" |
550 assume "\<exists>root'. root \<midarrow>x\<rightarrow> root' \<and> root' =xs\<Rightarrow> root''" |
556 thus "root =(x # xs)\<Rightarrow> root''" |
551 then show "root =(x # xs)\<Rightarrow> root''" |
557 by (blast intro: transitions.cons) |
552 by (blast intro: transitions.cons) |
558 qed |
553 qed |
559 |
554 |
560 text {* |
555 text {* |
561 The next two rules show how to ``destruct'' known transition |
556 The next two rules show how to ``destruct'' known transition |
566 |
561 |
567 lemma transitions_nilD: "root =[]\<Rightarrow> root' \<Longrightarrow> root' = root" |
562 lemma transitions_nilD: "root =[]\<Rightarrow> root' \<Longrightarrow> root' = root" |
568 by (simp add: transitions_nil_eq) |
563 by (simp add: transitions_nil_eq) |
569 |
564 |
570 lemma transitions_consD: |
565 lemma transitions_consD: |
571 "root =(x # xs)\<Rightarrow> root'' \<Longrightarrow> root \<midarrow>x\<rightarrow> root' \<Longrightarrow> root' =xs\<Rightarrow> root''" |
566 assumes list: "root =(x # xs)\<Rightarrow> root''" |
|
567 and hd: "root \<midarrow>x\<rightarrow> root'" |
|
568 shows "root' =xs\<Rightarrow> root''" |
572 proof - |
569 proof - |
573 assume "root =(x # xs)\<Rightarrow> root''" |
570 from list obtain r' where r': "root \<midarrow>x\<rightarrow> r'" and root'': "r' =xs\<Rightarrow> root''" |
574 then obtain r' where r': "root \<midarrow>x\<rightarrow> r'" and root'': "r' =xs\<Rightarrow> root''" |
|
575 by cases simp_all |
571 by cases simp_all |
576 assume "root \<midarrow>x\<rightarrow> root'" |
572 from r' hd have "r' = root'" by (rule transition_uniq) |
577 with r' have "r' = root'" by (rule transition_uniq) |
|
578 with root'' show "root' =xs\<Rightarrow> root''" by simp |
573 with root'' show "root' =xs\<Rightarrow> root''" by simp |
579 qed |
574 qed |
580 |
575 |
581 text {* |
576 text {* |
582 \medskip The following fact shows how an invariant @{term Q} of |
577 \medskip The following fact shows how an invariant @{term Q} of |
584 iterated transitions. The proof is rather obvious by rule induction |
579 iterated transitions. The proof is rather obvious by rule induction |
585 over the definition of @{term "root =xs\<Rightarrow> root'"}. |
580 over the definition of @{term "root =xs\<Rightarrow> root'"}. |
586 *} |
581 *} |
587 |
582 |
588 lemma transitions_invariant: |
583 lemma transitions_invariant: |
589 "(\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r') \<Longrightarrow> |
584 assumes r: "\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r'" |
590 root =xs\<Rightarrow> root' \<Longrightarrow> Q root \<Longrightarrow> \<forall>x \<in> set xs. P x \<Longrightarrow> Q root'" |
585 and trans: "root =xs\<Rightarrow> root'" |
591 proof - |
586 shows "Q root \<Longrightarrow> \<forall>x \<in> set xs. P x \<Longrightarrow> Q root'" |
592 assume r: "\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r'" |
587 using trans |
593 assume "root =xs\<Rightarrow> root'" |
588 proof induct |
594 thus "Q root \<Longrightarrow> (\<forall>x \<in> set xs. P x) \<Longrightarrow> Q root'" (is "PROP ?P root xs root'") |
589 case nil |
595 proof (induct root xs root') |
590 show ?case by assumption |
596 fix root assume "Q root" |
591 next |
597 thus "Q root" . |
592 case (cons root root' root'' x xs) |
598 next |
593 note P = `\<forall>x \<in> set (x # xs). P x` |
599 fix root root' root'' and x xs |
594 then have "P x" by simp |
600 assume root': "root \<midarrow>x\<rightarrow> root'" |
595 with `root \<midarrow>x\<rightarrow> root'` and `Q root` have Q': "Q root'" by (rule r) |
601 assume hyp: "PROP ?P root' xs root''" |
596 from P have "\<forall>x \<in> set xs. P x" by simp |
602 assume Q: "Q root" |
597 with Q' show "Q root''" by (rule cons.hyps) |
603 assume P: "\<forall>x \<in> set (x # xs). P x" |
|
604 hence "P x" by simp |
|
605 with root' Q have Q': "Q root'" by (rule r) |
|
606 from P have "\<forall>x \<in> set xs. P x" by simp |
|
607 with Q' show "Q root''" by (rule hyp) |
|
608 qed |
|
609 qed |
598 qed |
610 |
599 |
611 text {* |
600 text {* |
612 As an example of applying the previous result, we transfer the basic |
601 As an example of applying the previous result, we transfer the basic |
613 type-safety property (see \secref{sec:unix-single-trans}) from |
602 type-safety property (see \secref{sec:unix-single-trans}) from |
673 text {* |
662 text {* |
674 \medskip In case that we already know that a certain sequence can be |
663 \medskip In case that we already know that a certain sequence can be |
675 executed we may destruct it backwardly into individual transitions. |
664 executed we may destruct it backwardly into individual transitions. |
676 *} |
665 *} |
677 |
666 |
678 lemma can_exec_snocD: "\<And>root. can_exec root (xs @ [y]) |
667 lemma can_exec_snocD: "can_exec root (xs @ [y]) |
679 \<Longrightarrow> \<exists>root' root''. root =xs\<Rightarrow> root' \<and> root' \<midarrow>y\<rightarrow> root''" |
668 \<Longrightarrow> \<exists>root' root''. root =xs\<Rightarrow> root' \<and> root' \<midarrow>y\<rightarrow> root''" |
680 (is "PROP ?P xs" is "\<And>root. ?A root xs \<Longrightarrow> ?C root xs") |
669 proof (induct xs fixing: root) |
681 proof (induct xs) |
670 case Nil |
682 fix root |
671 then show ?case |
683 { |
672 by (simp add: can_exec_def transitions_nil_eq transitions_cons_eq) |
684 assume "?A root []" |
673 next |
685 thus "?C root []" |
674 case (Cons x xs) |
686 by (simp add: can_exec_def transitions_nil_eq transitions_cons_eq) |
675 from `can_exec root ((x # xs) @ [y])` obtain r root'' where |
687 next |
676 x: "root \<midarrow>x\<rightarrow> r" and |
688 fix x xs |
677 xs_y: "r =(xs @ [y])\<Rightarrow> root''" |
689 assume hyp: "PROP ?P xs" |
678 by (auto simp add: can_exec_def transitions_nil_eq transitions_cons_eq) |
690 assume asm: "?A root (x # xs)" |
679 from xs_y Cons.hyps obtain root' r' where xs: "r =xs\<Rightarrow> root'" and y: "root' \<midarrow>y\<rightarrow> r'" |
691 show "?C root (x # xs)" |
680 by (unfold can_exec_def) blast |
692 proof - |
681 from x xs have "root =(x # xs)\<Rightarrow> root'" |
693 from asm obtain r root'' where x: "root \<midarrow>x\<rightarrow> r" and |
682 by (rule transitions.cons) |
694 xs_y: "r =(xs @ [y])\<Rightarrow> root''" |
683 with y show ?case by blast |
695 by (auto simp add: can_exec_def transitions_nil_eq transitions_cons_eq) |
|
696 from xs_y hyp obtain root' r' where xs: "r =xs\<Rightarrow> root'" and y: "root' \<midarrow>y\<rightarrow> r'" |
|
697 by (unfold can_exec_def) blast |
|
698 from x xs have "root =(x # xs)\<Rightarrow> root'" |
|
699 by (rule transitions.cons) |
|
700 with y show ?thesis by blast |
|
701 qed |
|
702 } |
|
703 qed |
684 qed |
704 |
685 |
705 |
686 |
706 subsection {* Example executions \label{sec:unix-examples} *} |
687 subsection {* Example executions \label{sec:unix-examples} *} |
707 |
688 |
801 The following theorem expresses the general procedure we are |
782 The following theorem expresses the general procedure we are |
802 following to achieve the main result. |
783 following to achieve the main result. |
803 *} |
784 *} |
804 |
785 |
805 theorem general_procedure: |
786 theorem general_procedure: |
806 "(\<And>r r'. Q r \<Longrightarrow> r \<midarrow>y\<rightarrow> r' \<Longrightarrow> False) \<Longrightarrow> |
787 assumes cannot_y: "\<And>r r'. Q r \<Longrightarrow> r \<midarrow>y\<rightarrow> r' \<Longrightarrow> False" |
807 (\<And>root. init users =bs\<Rightarrow> root \<Longrightarrow> Q root) \<Longrightarrow> |
788 and init_inv: "\<And>root. init users =bs\<Rightarrow> root \<Longrightarrow> Q root" |
808 (\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r') \<Longrightarrow> |
789 and preserve_inv: "\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r'" |
809 init users =bs\<Rightarrow> root \<Longrightarrow> |
790 and init_result: "init users =bs\<Rightarrow> root" |
810 \<not> (\<exists>xs. (\<forall>x \<in> set xs. P x) \<and> can_exec root (xs @ [y]))" |
791 shows "\<not> (\<exists>xs. (\<forall>x \<in> set xs. P x) \<and> can_exec root (xs @ [y]))" |
811 proof - |
792 proof - |
812 assume cannot_y: "\<And>r r'. Q r \<Longrightarrow> r \<midarrow>y\<rightarrow> r' \<Longrightarrow> False" |
|
813 assume init_inv: "\<And>root. init users =bs\<Rightarrow> root \<Longrightarrow> Q root" |
|
814 assume preserve_inv: "\<And>r x r'. r \<midarrow>x\<rightarrow> r' \<Longrightarrow> Q r \<Longrightarrow> P x \<Longrightarrow> Q r'" |
|
815 assume init_result: "init users =bs\<Rightarrow> root" |
|
816 { |
793 { |
817 fix xs |
794 fix xs |
818 assume Ps: "\<forall>x \<in> set xs. P x" |
795 assume Ps: "\<forall>x \<in> set xs. P x" |
819 assume can_exec: "can_exec root (xs @ [y])" |
796 assume can_exec: "can_exec root (xs @ [y])" |
820 then obtain root' root'' where |
797 then obtain root' root'' where |
928 |
905 |
929 \medskip The first two lemmas are technically straight forward --- |
906 \medskip The first two lemmas are technically straight forward --- |
930 we just have to inspect rather special cases. |
907 we just have to inspect rather special cases. |
931 *} |
908 *} |
932 |
909 |
933 lemma (in invariant) |
910 lemma (in invariant) cannot_rmdir: |
934 cannot_rmdir: "invariant root bogus_path \<Longrightarrow> |
911 assumes inv: "invariant root bogus_path" |
935 root \<midarrow>(Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1])\<rightarrow> root' \<Longrightarrow> False" |
912 and rmdir: "root \<midarrow>(Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1])\<rightarrow> root'" |
|
913 shows False |
936 proof - |
914 proof - |
937 assume "invariant root bogus_path" |
915 from inv obtain "file" where "access root bogus_path user\<^isub>1 {} = Some file" |
938 then obtain "file" where "access root bogus_path user\<^isub>1 {} = Some file" |
|
939 by (unfold invariant_def) blast |
916 by (unfold invariant_def) blast |
940 moreover |
917 moreover |
941 assume "root \<midarrow>(Rmdir user\<^isub>1 [user\<^isub>1, name\<^isub>1])\<rightarrow> root'" |
918 from rmdir obtain att where |
942 then obtain att where |
|
943 "access root [user\<^isub>1, name\<^isub>1] user\<^isub>1 {} = Some (Env att empty)" |
919 "access root [user\<^isub>1, name\<^isub>1] user\<^isub>1 {} = Some (Env att empty)" |
944 by cases auto |
920 by cases auto |
945 hence "access root ([user\<^isub>1, name\<^isub>1] @ [name\<^isub>2]) user\<^isub>1 {} = empty name\<^isub>2" |
921 then have "access root ([user\<^isub>1, name\<^isub>1] @ [name\<^isub>2]) user\<^isub>1 {} = empty name\<^isub>2" |
946 by (simp only: access_empty_lookup lookup_append_some) simp |
922 by (simp only: access_empty_lookup lookup_append_some) simp |
947 ultimately show False by (simp add: bogus_path_def) |
923 ultimately show False by (simp add: bogus_path_def) |
948 qed |
924 qed |
949 |
925 |
950 lemma (in invariant) |
926 lemma (in invariant) |
951 init_invariant: "init users =bogus\<Rightarrow> root \<Longrightarrow> invariant root bogus_path" |
927 assumes init: "init users =bogus\<Rightarrow> root" |
952 proof - |
928 notes eval = facts access_def init_def |
953 note eval = facts access_def init_def |
929 shows init_invariant: "invariant root bogus_path" |
954 case rule_context thus ?thesis |
930 using init |
955 apply (unfold bogus_def bogus_path_def) |
931 apply (unfold bogus_def bogus_path_def) |
956 apply (drule transitions_consD, rule transition.intros, |
932 apply (drule transitions_consD, rule transition.intros, |
957 (force simp add: eval)+, (simp add: eval)?)+ |
933 (force simp add: eval)+, (simp add: eval)?)+ |
958 -- "evaluate all operations" |
934 -- "evaluate all operations" |
959 apply (drule transitions_nilD) |
935 apply (drule transitions_nilD) |
960 -- "reach final result" |
936 -- "reach final result" |
961 apply (simp add: invariant_def eval) |
937 apply (simp add: invariant_def eval) |
962 -- "check the invariant" |
938 -- "check the invariant" |
963 done |
939 done |
964 qed |
|
965 |
940 |
966 text {* |
941 text {* |
967 \medskip At last we are left with the main effort to show that the |
942 \medskip At last we are left with the main effort to show that the |
968 ``bogosity'' invariant is preserved by any file-system operation |
943 ``bogosity'' invariant is preserved by any file-system operation |
969 performed by @{term user\<^isub>1} alone. Note that this holds for |
944 performed by @{term user\<^isub>1} alone. Note that this holds for |
970 any @{term path} given, the particular @{term bogus_path} is not |
945 any @{term path} given, the particular @{term bogus_path} is not |
971 required here. |
946 required here. |
972 *} |
947 *} |
973 |
948 |
974 lemma (in invariant) |
949 lemma (in invariant) preserve_invariant: |
975 preserve_invariant: "root \<midarrow>x\<rightarrow> root' \<Longrightarrow> |
950 assumes tr: "root \<midarrow>x\<rightarrow> root'" |
976 invariant root path \<Longrightarrow> uid_of x = user\<^isub>1 \<Longrightarrow> invariant root' path" |
951 and inv: "invariant root path" |
|
952 and uid: "uid_of x = user\<^isub>1" |
|
953 shows "invariant root' path" |
977 proof - |
954 proof - |
978 assume tr: "root \<midarrow>x\<rightarrow> root'" |
|
979 assume inv: "invariant root path" |
|
980 assume uid: "uid_of x = user\<^isub>1" |
|
981 |
|
982 from inv obtain att dir where |
955 from inv obtain att dir where |
983 inv1: "access root path user\<^isub>1 {} = Some (Env att dir)" and |
956 inv1: "access root path user\<^isub>1 {} = Some (Env att dir)" and |
984 inv2: "dir \<noteq> empty" and |
957 inv2: "dir \<noteq> empty" and |
985 inv3: "user\<^isub>1 \<noteq> owner att" and |
958 inv3: "user\<^isub>1 \<noteq> owner att" and |
986 inv4: "access root path user\<^isub>1 {Writable} = None" |
959 inv4: "access root path user\<^isub>1 {Writable} = None" |