skip to main content
research-article
Open Access

CN: Verifying Systems C Code with Separation-Logic Refinement Types

Published:11 January 2023Publication History
Skip Abstract Section

Abstract

Despite significant progress in the verification of hypervisors, operating systems, and compilers, and in verification tooling, there exists a wide gap between the approaches used in verification projects and conventional development of systems software. We see two main challenges in bringing these closer together: verification handling the complexity of code and semantics of conventional systems software, and verification usability.

We describe an experiment in verification tool design aimed at addressing some aspects of both: we design and implement CN, a separation-logic refinement type system for C systems software, aimed at predictable proof automation, based on a realistic semantics of ISO C. CN reduces refinement typing to decidable propositional logic reasoning, uses first-class resources to support pointer aliasing and pointer arithmetic, features resource inference for iterated separating conjunction, and uses a novel syntactic restriction of ghost variables in specifications to guarantee their successful inference. We implement CN and formalise key aspects of the type system, including a soundness proof of type checking. To demonstrate the usability of CN we use it to verify a substantial component of Google's pKVM hypervisor for Android.

References

  1. Amal Ahmed, Matthew Fluet, and Greg Morrisett. 2007. L^ 3: A Linear Language with Locations. Fundam. Informaticae, 77, 4 (2007), 397–449. Google ScholarGoogle ScholarDigital LibraryDigital Library
  2. Roberto M. Amadio, Nicholas Ayache, François Bobot, Jaap Boender, Brian Campbell, Ilias Garnier, Antoine Madet, James McKinna, Dominic P. Mulligan, Mauro Piccolo, Randy Pollack, Yann Régis-Gianas, Claudio Sacerdoti Coen, Ian Stark, and Paolo Tranquilli. 2013. Certified Complexity (CerCo). In Foundational and Practical Aspects of Resource Analysis - Third International Workshop, FOPARA 2013, Bertinoro, Italy, August 29-31, 2013, Revised Selected Papers. 1–18. https://doi.org/10.1007/978-3-319-12466-7_1 Google ScholarGoogle ScholarCross RefCross Ref
  3. Android Open Source. 2022. Android 13 Release Notes. https://source.android.com/docs/setup/about/android-13-release [Online; accessed 11-November-2022] Google ScholarGoogle Scholar
  4. Andrew W. Appel, Robert Dockins, Aquinas Hobor, Lennart Beringer, Josiah Dodds, Gordon Stewart, Sandrine Blazy, and Xavier Leroy. 2014. Program Logics for Certified Compilers. CUP. https://doi.org/10.1017/CBO9781107256552 Google ScholarGoogle ScholarCross RefCross Ref
  5. Vytautas Astrauskas, Aurel Bílý, Jonás Fiala, Zachary Grannan, Christoph Matheja, Peter Müller, Federico Poli, and Alexander J. Summers. 2022. The Prusti Project: Formal Verification for Rust. In NFM (Lecture Notes in Computer Science, Vol. 13260). Springer, 88–108. Google ScholarGoogle Scholar
  6. Michael Barnett, Bor-Yuh Evan Chang, Robert DeLine, Bart Jacobs, and K. Rustan M. Leino. 2005. Boogie: A Modular Reusable Verifier for Object-Oriented Programs. In FMCO (Lecture Notes in Computer Science, Vol. 4111). Springer, 364–387. Google ScholarGoogle Scholar
  7. Patrick Baudin, François Bobot, David Bühler, Loïc Correnson, Florent Kirchner, Nikolai Kosmatov, André Maroneze, Valentin Perrelle, Virgile Prevosto, Julien Signoles, and Nicky Williams. 2021. The dogged pursuit of bug-free C programs: the Frama-C software analysis platform. Commun. ACM, 64, 8 (2021), 56–68. Google ScholarGoogle ScholarDigital LibraryDigital Library
  8. Christoph Baumann, Mats Näslund, Christian Gehrmann, Oliver Schwarz, and Hans Thorsen. 2016. A high assurance virtualization platform for ARMv8. In European Conference on Networks and Communications, EuCNC 2016, Athens, Greece, June 27-30, 2016. 210–214. https://doi.org/10.1109/EuCNC.2016.7561034 Google ScholarGoogle ScholarCross RefCross Ref
  9. Josh Berdine, Cristiano Calcagno, and Peter W. O’Hearn. 2005. Smallfoot: Modular Automatic Assertion Checking with Separation Logic. In FMCO (Lecture Notes in Computer Science, Vol. 4111). Springer, 115–137. Google ScholarGoogle Scholar
  10. Aaron R. Bradley, Zohar Manna, and Henny B. Sipma. 2006. What’s Decidable About Arrays? In Verification, Model Checking, and Abstract Interpretation, 7th International Conference, VMCAI 2006, Charleston, SC, USA, January 8-10, 2006, Proceedings, E. Allen Emerson and Kedar S. Namjoshi (Eds.) (Lecture Notes in Computer Science, Vol. 3855). Springer, 427–442. https://doi.org/10.1007/11609773_28 Google ScholarGoogle ScholarDigital LibraryDigital Library
  11. James Brotherston, Nikos Gorogiannis, Max I. Kanovich, and Reuben Rowe. 2016. Model checking for symbolic-heap separation logic with inductive predicates. In POPL. ACM, 84–96. Google ScholarGoogle Scholar
  12. Cristiano Calcagno and Dino Distefano. 2011. Infer: An Automatic Program Verifier for Memory Safety of C Programs. In NASA Formal Methods (Lecture Notes in Computer Science, Vol. 6617). Springer, 459–465. Google ScholarGoogle ScholarCross RefCross Ref
  13. Qinxiang Cao, Lennart Beringer, Samuel Gruetter, Josiah Dodds, and Andrew W. Appel. 2018. VST-Floyd: A Separation Logic Tool to Verify Correctness of C Programs. J. Autom. Reason., 61, 1-4 (2018), 367–422. https://doi.org/10.1007/s10817-018-9457-5 Google ScholarGoogle ScholarDigital LibraryDigital Library
  14. Sa Cui, Kevin Donnelly, and Hongwei Xi. 2005. ATS: A Language That Combines Programming with Theorem Proving. In FroCoS (Lecture Notes in Computer Science, Vol. 3717). Springer, 310–320. Google ScholarGoogle ScholarDigital LibraryDigital Library
  15. Leonardo De Moura and Nikolaj Bjørner. 2008. Z3: An Efficient SMT Solver. In Proceedings of the Theory and Practice of Software, 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems (TACAS’08/ETAPS’08). Springer-Verlag, Berlin, Heidelberg. 337–340. isbn:3-540-78799-2, 978-3-540-78799-0 http://dl.acm.org/citation.cfm?id=1792734.1792766 Google ScholarGoogle ScholarDigital LibraryDigital Library
  16. Will Deacon. 2020. Virtualisation for the Masses: Exposing KVM on Android. KVM Forum slides,. https://mirrors.edge.kernel.org/pub/linux/kernel/people/will/slides/kvmforum-2020-edited.pdf Accessed 2022-07-07 Google ScholarGoogle Scholar
  17. Jake Edge. 2020. KVM for Android. Linux Weekly News,. LWN article](https://lwn.net/Articles/836693/ Accessed 2022-07-07 Google ScholarGoogle Scholar
  18. Anthony C. J. Fox, Magnus O. Myreen, Yong Kiam Tan, and Ramana Kumar. 2017. Verified compilation of CakeML to multiple machine-code targets. In Proceedings of the 6th ACM SIGPLAN Conference on Certified Programs and Proofs, CPP 2017, Paris, France, January 16-17, 2017. 125–137. https://doi.org/10.1145/3018610.3018621 Google ScholarGoogle ScholarDigital LibraryDigital Library
  19. Dan Frumin, Léon Gondelman, and Robbert Krebbers. 2019. Semi-automated Reasoning About Non-determinism in C Expressions. In Programming Languages and Systems - 28th European Symposium on Programming, ESOP 2019, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2019, Prague, Czech Republic, April 6-11, 2019, Proceedings, Luís Caires (Ed.) (Lecture Notes in Computer Science, Vol. 11423). Springer, 60–87. https://doi.org/10.1007/978-3-030-17184-1_3 Google ScholarGoogle ScholarCross RefCross Ref
  20. Jean-Yves Girard. 1987. Linear Logic. Theor. Comput. Sci., 50 (1987), 1–102. Google ScholarGoogle ScholarDigital LibraryDigital Library
  21. Ronghui Gu, Zhong Shao, Hao Chen, Xiongnan (Newman) Wu, Jieung Kim, Vilhelm Sjöberg, and David Costanzo. 2016. CertiKOS: An Extensible Architecture for Building Certified Concurrent OS Kernels. In 12th USENIX Symposium on Operating Systems Design and Implementation, OSDI 2016, Savannah, GA, USA, November 2-4, 2016.. 653–669. https://www.usenix.org/conference/osdi16/technical-sessions/presentation/gu Google ScholarGoogle ScholarDigital LibraryDigital Library
  22. Roberto Guanciale, Hamed Nemati, Mads Dam, and Christoph Baumann. 2016. Provably secure memory isolation for Linux on ARM. Journal of Computer Security, 24, 6 (2016), 793–837. https://doi.org/10.3233/JCS-160558 Google ScholarGoogle ScholarDigital LibraryDigital Library
  23. Chris Hawblitzel, Jon Howell, Jacob R. Lorch, Arjun Narayan, Bryan Parno, Danfeng Zhang, and Brian Zill. 2014. Ironclad Apps: End-to-End Security via Automated Full-System Verification. In 11th USENIX Symposium on Operating Systems Design and Implementation, OSDI ’14, Broomfield, CO, USA, October 6-8, 2014, Jason Flinn and Hank Levy (Eds.). USENIX Association, 165–181. https://www.usenix.org/conference/osdi14/technical-sessions/presentation/hawblitzel Google ScholarGoogle ScholarDigital LibraryDigital Library
  24. Gernot Heiser, Gerwin Klein, and June Andronick. 2020. seL4 in Australia: from research to real-world trustworthy systems. Commun. ACM, 63, 4 (2020), 72–75. https://doi.org/10.1145/3378426 Google ScholarGoogle ScholarDigital LibraryDigital Library
  25. Bart Jacobs, Jan Smans, Pieter Philippaerts, Frédéric Vogels, Willem Penninckx, and Frank Piessens. 2011. VeriFast: A Powerful, Sound, Predictable, Fast Verifier for C and Java. In NASA Formal Methods, Mihaela Bobaru, Klaus Havelund, Gerard J. Holzmann, and Rajeev Joshi (Eds.). Springer Berlin Heidelberg, Berlin, Heidelberg. 41–55. isbn:978-3-642-20398-5 Google ScholarGoogle Scholar
  26. Ralf Jung, Robbert Krebbers, Jacques-Henri Jourdan, Ales Bizjak, Lars Birkedal, and Derek Dreyer. 2018. Iris from the ground up: A modular foundation for higher-order concurrent separation logic. J. Funct. Program., 28 (2018), e20. Google ScholarGoogle ScholarCross RefCross Ref
  27. Gerwin Klein, June Andronick, Kevin Elphinstone, Gernot Heiser, David Cock, Philip Derrin, Dhammika Elkaduwe, Kai Engelhardt, Rafal Kolanski, Michael Norrish, Thomas Sewell, Harvey Tuch, and Simon Winwood. 2010. seL4: formal verification of an operating-system kernel. Commun. ACM, 53, 6 (2010), 107–115. Google ScholarGoogle ScholarDigital LibraryDigital Library
  28. Gerwin Klein, June Andronick, Kevin Elphinstone, Toby Murray, Thomas Sewell, Rafal Kolanski, and Gernot Heiser. 2014. Comprehensive Formal Verification of an OS Microkernel. ACM TOCS, 32, 1 (2014), Feb., 2:1–2:70. https://doi.org/10.1145/2560537 Google ScholarGoogle ScholarDigital LibraryDigital Library
  29. Robbert Krebbers. 2015. The C Standard Formalized in Coq. Ph. D. Dissertation. Radboud University Nijmegen. Google ScholarGoogle Scholar
  30. Robbert Krebbers. 2016. A Formal C Memory Model for Separation Logic. J. Autom. Reason., 57, 4 (2016), 319–387. https://doi.org/10.1007/s10817-016-9369-1 Google ScholarGoogle ScholarDigital LibraryDigital Library
  31. Ramana Kumar, Magnus O. Myreen, Michael Norrish, and Scott Owens. 2014. CakeML: A Verified Implementation of ML. In Proceedings of the 41st ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL ’14). ACM, New York, NY, USA. 179–191. isbn:978-1-4503-2544-8 https://doi.org/10.1145/2535838.2535841 Google ScholarGoogle ScholarDigital LibraryDigital Library
  32. Dirk Leinenbach and Thomas Santen. 2009. Verifying the Microsoft Hyper-V Hypervisor with VCC. In FM 2009: Formal Methods, Second World Congress, Eindhoven, The Netherlands, November 2-6, 2009. Proceedings. 806–809. https://doi.org/10.1007/978-3-642-05089-3_51 Google ScholarGoogle ScholarDigital LibraryDigital Library
  33. Rodolphe Lepigre and Michael Sammler. 2022. RefinedC formalisation of the early allocator. https://gitlab.mpi-sws.org/iris/refinedc/-/tree/master/linux/pkvm [Online; accessed 24-October-2022] Google ScholarGoogle Scholar
  34. Rodolphe Lepigre, Michael Sammler, Kayvan Memarian, Robbert Krebbers, Derek Dreyer, and Peter Sewell. 2022. VIP: verifying real-world C idioms with integer-pointer casts. Proc. ACM Program. Lang., 6, POPL (2022), 1–32. https://doi.org/10.1145/3498681 Google ScholarGoogle ScholarDigital LibraryDigital Library
  35. Xavier Leroy. 2006. Formal certification of a compiler back-end or: programming a compiler with a proof assistant. In POPL. ACM, 42–54. Google ScholarGoogle Scholar
  36. Xavier Leroy. 2009. A Formally Verified Compiler Back-end. J. Autom. Reason., 43, 4 (2009), 363–446. https://doi.org/10.1007/s10817-009-9155-4 Google ScholarGoogle ScholarDigital LibraryDigital Library
  37. Shih-Wei Li, Xupeng Li, Ronghui Gu, Jason Nieh, and John Zhuang Hui. 2021. A Secure and Formally Verified Linux KVM Hypervisor. In 42nd IEEE Symposium on Security and Privacy, SP 2021, San Francisco, CA, USA, 24-27 May 2021. IEEE, 1782–1799. https://doi.org/10.1109/SP40001.2021.00049 Google ScholarGoogle ScholarCross RefCross Ref
  38. Petar Maksimovic, José Fragoso Santos, Sacha-Élie Ayoun, and Philippa Gardner. 2021. Gillian: A Multi-Language Platform for Unified Symbolic Analysis. CoRR, abs/2105.14769 (2021). Google ScholarGoogle Scholar
  39. Gregory Malecha, Gordon Stewart, Frantisek Farka, Jasper Haag, and Yoichi Hirai. 2022. Developing With Formal Methods at BedRock Systems, Inc. IEEE Security & Privacy, 20, 3 (2022), May, 33–42. https://doi.org/10.1109/MSEC.2022.3158196 Google ScholarGoogle ScholarCross RefCross Ref
  40. Nicholas D. Matsakis and Felix S. Klock II. 2014. The Rust language. In Proceedings of the 2014 ACM SIGAda annual conference on High integrity language technology, HILT 2014, Portland, Oregon, USA, October 18-21, 2014, Michael Feldman and S. Tucker Taft (Eds.). ACM, 103–104. https://doi.org/10.1145/2663171.2663188 Google ScholarGoogle ScholarDigital LibraryDigital Library
  41. Paul-André Melliès and Noam Zeilberger. 2015. Functors are Type Refinement Systems. In Proceedings of the 42nd Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL 2015, Mumbai, India, January 15-17, 2015, Sriram K. Rajamani and David Walker (Eds.). ACM, 3–16. https://doi.org/10.1145/2676726.2676970 Google ScholarGoogle ScholarDigital LibraryDigital Library
  42. Kayvan Memarian, Victor B. F. Gomes, Brooks Davis, Stephen Kell, Alexander Richardson, Robert N. M. Watson, and Peter Sewell. 2019. Exploring C Semantics and Pointer Provenance. In Proceedings of the 46th ACM SIGPLAN Symposium on Principles of Programming Languages. https://doi.org/10.1145/3290380 Proc. ACM Program. Lang. 3, POPL, Article 67. Also available as ISO/IEC JTC1/SC22/WG14 N2311 Google ScholarGoogle ScholarDigital LibraryDigital Library
  43. Kayvan Memarian, Justus Matthiesen, James Lingard, Kyndylan Nienhuis, David Chisnall, Robert N. M. Watson, and Peter Sewell. 2016. Into the depths of C: elaborating the de facto standards. In Proceedings of the 37th ACM SIGPLAN Conference on Programming Language Design and Implementation, PLDI 2016, Santa Barbara, CA, USA, June 13-17, 2016, Chandra Krintz and Emery D. Berger (Eds.). ACM, 1–15. https://doi.org/10.1145/2908080.2908081 Google ScholarGoogle ScholarDigital LibraryDigital Library
  44. Peter Müller, Malte Schwerhoff, and Alexander J. Summers. 2016. Automatic Verification of Iterated Separating Conjunctions Using Symbolic Execution. In Computer Aided Verification - 28th International Conference, CAV 2016, Toronto, ON, Canada, July 17-23, 2016, Proceedings, Part I, Swarat Chaudhuri and Azadeh Farzan (Eds.) (Lecture Notes in Computer Science, Vol. 9779). Springer, 405–425. https://doi.org/10.1007/978-3-319-41528-4_22 Google ScholarGoogle ScholarCross RefCross Ref
  45. Peter Müller, Malte Schwerhoff, and Alexander J. Summers. 2017. Viper: A Verification Infrastructure for Permission-Based Reasoning. In Dependable Software Systems Engineering (NATO Science for Peace and Security Series - D: Information and Communication Security, Vol. 50). IOS Press, 104–125. Google ScholarGoogle Scholar
  46. George C. Necula, Scott McPeak, Shree Prakash Rahul, and Westley Weimer. 2002. CIL: Intermediate Language and Tools for Analysis and Transformation of C Programs. In Compiler Construction, 11th International Conference, CC 2002, Held as Part of the Joint European Conferences on Theory and Practice of Software, ETAPS 2002, Grenoble, France, April 8-12, 2002, Proceedings, R. Nigel Horspool (Ed.) (Lecture Notes in Computer Science, Vol. 2304). Springer, 213–228. https://doi.org/10.1007/3-540-45937-5_16 Google ScholarGoogle ScholarCross RefCross Ref
  47. Liam O’Connor, Zilin Chen, Christine Rizkallah, Vincent Jackson, Sidney Amani, Gerwin Klein, Toby Murray, Thomas Sewell, and Gabriele Keller. 2021. Cogent: uniqueness types and certifying compilation. J. Funct. Program., 31 (2021), e25. https://doi.org/10.1017/S095679682100023X Google ScholarGoogle ScholarCross RefCross Ref
  48. Christopher Pulte, Dhruv C. Makwana, Thomas Sewell, Kayvan Memarian, Peter Sewell, and Neel Krishnaswami. 2022. Data for "CN: Verifying Systems C Code with Separation-Logic Refinement Types". https://doi.org/10.5281/zenodo.7320414 Google ScholarGoogle ScholarCross RefCross Ref
  49. John C. Reynolds. 1978. Syntactic Control of Interference. In POPL. ACM Press, 39–46. Google ScholarGoogle Scholar
  50. John C. Reynolds. 2002. Separation Logic: A Logic for Shared Mutable Data Structures. In 17th IEEE Symposium on Logic in Computer Science (LICS 2002), 22-25 July 2002, Copenhagen, Denmark, Proceedings. IEEE Computer Society, 55–74. https://doi.org/10.1109/LICS.2002.1029817 Google ScholarGoogle ScholarCross RefCross Ref
  51. John C. Reynolds. 2008. An Introduction to Separation Logic (Preliminary Draft). ". https://www.cs.cmu.edu/ jcr/copenhagen08.pdf "[Online; accessed 4-July-2022]" Google ScholarGoogle Scholar
  52. Patrick Maxim Rondon, Ming Kawaguchi, and Ranjit Jhala. 2008. Liquid types. In PLDI. ACM, 159–169. Google ScholarGoogle Scholar
  53. Michael Sammler, Angus Hammond, Rodolphe Lepigre, Brian Campbell, Jean Pichon-Pharabod, Derek Dreyer, Deepak Garg, and Peter Sewell. 2022. Islaris: verification of machine code against authoritative ISA semantics. In PLDI ’22: 43rd ACM SIGPLAN International Conference on Programming Language Design and Implementation, San Diego, CA, USA, June 13 - 17, 2022, Ranjit Jhala and Isil Dillig (Eds.). ACM, 825–840. https://doi.org/10.1145/3519939.3523434 Google ScholarGoogle ScholarDigital LibraryDigital Library
  54. Michael Sammler, Rodolphe Lepigre, Robbert Krebbers, Kayvan Memarian, Derek Dreyer, and Deepak Garg. 2021. RefinedC: automating the foundational verification of C code with refined ownership types. In PLDI ’21: 42nd ACM SIGPLAN International Conference on Programming Language Design and Implementation, Virtual Event, Canada, June 20-25, 2021, Stephen N. Freund and Eran Yahav (Eds.). ACM, 158–174. https://doi.org/10.1145/3453483.3454036 Google ScholarGoogle ScholarDigital LibraryDigital Library
  55. Nikhil Swamy, Catalin Hritcu, Chantal Keller, Aseem Rastogi, Antoine Delignat-Lavaud, Simon Forest, Karthikeyan Bhargavan, Cédric Fournet, Pierre-Yves Strub, Markulf Kohlweiss, Jean-Karim Zinzindohoué, and Santiago Zanella-Béguelin. 2016. Dependent Types and Multi-Monadic Effects in F*. In 43rd ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages (POPL). ACM, 256–270. isbn:978-1-4503-3549-2 https://www.fstar-lang.org/papers/mumon/ Google ScholarGoogle ScholarDigital LibraryDigital Library
  56. Yong Kiam Tan, Magnus O. Myreen, Ramana Kumar, Anthony C. J. Fox, Scott Owens, and Michael Norrish. 2016. A new verified compiler backend for CakeML. In Proceedings of the 21st ACM SIGPLAN International Conference on Functional Programming, ICFP 2016, Nara, Japan, September 18-22, 2016. 60–73. https://doi.org/10.1145/2951913.2951924 Google ScholarGoogle ScholarDigital LibraryDigital Library
  57. Runzhou Tao, Jianan Yao, Xupeng Li, Shih-Wei Li, Jason Nieh, and Ronghui Gu. 2021. Formal Verification of a Multiprocessor Hypervisor on Arm Relaxed Memory Hardware. In SOSP ’21: ACM SIGOPS 28th Symposium on Operating Systems Principles, Virtual Event / Koblenz, Germany, October 26-29, 2021, Robbert van Renesse and Nickolai Zeldovich (Eds.). ACM, 866–881. https://doi.org/10.1145/3477132.3483560 Google ScholarGoogle ScholarDigital LibraryDigital Library
  58. Hongwei Xi. 2007. Dependent ML: An approach to practical programming with dependent types. Journal of Functional Programming, 17, 2 (2007), 215–286. https://doi.org/10.1017/S0956796806006216 Google ScholarGoogle ScholarDigital LibraryDigital Library

Index Terms

  1. CN: Verifying Systems C Code with Separation-Logic Refinement Types

        Recommendations

        Comments

        Login options

        Check if you have access through your login credentials or your institution to get full access on this article.

        Sign in

        Full Access

        • Published in

          cover image Proceedings of the ACM on Programming Languages
          Proceedings of the ACM on Programming Languages  Volume 7, Issue POPL
          January 2023
          2196 pages
          EISSN:2475-1421
          DOI:10.1145/3554308
          • Editor:
          Issue’s Table of Contents

          Copyright © 2023 Owner/Author

          Publisher

          Association for Computing Machinery

          New York, NY, United States

          Publication History

          • Published: 11 January 2023
          Published in pacmpl Volume 7, Issue POPL

          Permissions

          Request permissions about this article.

          Request Permissions

          Check for updates

          Qualifiers

          • research-article
        • Article Metrics

          • Downloads (Last 12 months)562
          • Downloads (Last 6 weeks)88

          Other Metrics

        PDF Format

        View or Download as a PDF file.

        PDF

        eReader

        View online with eReader.

        eReader
        About Cookies On This Site

        We use cookies to ensure that we give you the best experience on our website.

        Learn more

        Got it!