From: Matthew Flatt <mflatt@racket-lang.org>
Date: Sun, 27 Jun 2021 08:46:02 -0600
Subject: prohibit import from module with a weak code inspector

Cherry-picking commit aa2e814a02, with changes to generated files
dropped. This change alone will not fix #991327; the code in
src/bc/src/startup.inc needs to be regenerated by running "make
expander" in src/expander.
---
 .../scribblings/reference/code-inspectors.scrbl      | 12 +++++++++---
 src/expander/namespace/module.rkt                    | 20 ++++++++++++++++----
 2 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/share/pkgs/racket-doc/scribblings/reference/code-inspectors.scrbl b/share/pkgs/racket-doc/scribblings/reference/code-inspectors.scrbl
index b5037a5..2af93a8 100644
--- a/share/pkgs/racket-doc/scribblings/reference/code-inspectors.scrbl
+++ b/share/pkgs/racket-doc/scribblings/reference/code-inspectors.scrbl
@@ -22,11 +22,13 @@ of @racket[current-code-inspector] never changes, then no control is
 lost for any module invocation, since the module's invocation is
 associated with a sub-inspector of @racket[current-code-inspector].
 
-When an inspector that controls a module invocation is installed
-@racket[current-code-inspector], it enables the following
+When an inspector that controls a module invocation is installed with
+@racket[current-code-inspector], it enables using
 @racket[module->namespace] on the module, and it enables access to the
 module's protected exports (i.e., those identifiers exported from the
-module with @racket[protect-out]) via @racket[dynamic-require].
+module with @racket[protect-out]) via @racket[dynamic-require]. A
+module cannot @racket[require] a module that has a weaker
+declaration-time code inspector.
 
 When a @racket[module] form is expanded or a @tech{namespace} is
 created, the value of @racket[current-code-inspector] is associated
@@ -56,6 +58,10 @@ particular module registry can be changed via
 @racket[namespace-unprotect-module] (but changing the inspector
 requires control over the old one).
 
+@history[#:changed "8.1.0.8" @elem{Added constraint against
+                                    @racket[require] of a module with
+                                    a weaker code inspector.}]
+
 @defparam[current-code-inspector insp inspector?]{
 
 A @tech{parameter} that determines an inspector to control access to
diff --git a/src/expander/namespace/module.rkt b/src/expander/namespace/module.rkt
index b19273e..6d66c5d 100644
--- a/src/expander/namespace/module.rkt
+++ b/src/expander/namespace/module.rkt
@@ -376,7 +376,8 @@
                                        #:skip-run? [skip-run? #f]
                                        #:otherwise-available? [otherwise-available? #t]
                                        #:seen [seen #hasheq()]
-                                       #:seen-list [seen-list null])
+                                       #:seen-list [seen-list null]
+                                       #:minimum-inspector [minimum-inspector #f])
   (unless (module-path-index? mpi)
     (error "not a module path index:" mpi))
   (define name (module-path-index-resolve mpi #t))
@@ -390,7 +391,8 @@
                           #:skip-run? skip-run?
                           #:otherwise-available? otherwise-available?
                           #:seen seen
-                          #:seen-list seen-list))
+                          #:seen-list seen-list
+                          #:minimum-inspector minimum-inspector))
   ;; If the module is cross-phase persistent, make sure it's instantiated
   ;; at phase 0 and registered in `ns` as phaseless; otherwise
   (cond
@@ -417,7 +419,8 @@
                               #:skip-run? skip-run? 
                               #:otherwise-available? otherwise-available?
                               #:seen [seen #hasheq()]
-                              #:seen-list [seen-list null])
+                              #:seen-list [seen-list null]
+                              #:minimum-inspector [minimum-inspector #f])
   (performance-region
    ['eval 'requires]
    ;; Nothing to do if we've run this phase already and made the
@@ -425,6 +428,14 @@
    (define m-ns (module-instance-namespace mi))
    (define instance-phase (namespace-0-phase m-ns))
    (define run-phase-level (phase- run-phase instance-phase))
+   (define inspector (module-inspector (module-instance-module mi)))
+   (when minimum-inspector
+     (unless (or (eq? inspector minimum-inspector)
+                 (inspector-superior? inspector minimum-inspector))
+       (error 'require
+              "cannot import module with weaker code inspector\n  module: ~a"
+              (module-path-index-resolve
+               (namespace-mpi (module-instance-namespace mi))))))
    (unless (and (or skip-run?
                     (eq? 'started (small-hash-ref (module-instance-phase-level-to-state mi) run-phase-level #f)))
                 (or (not otherwise-available?)
@@ -464,7 +475,8 @@
                                         #:skip-run? skip-run?
                                         #:otherwise-available? otherwise-available?
                                         #:seen (hash-set seen mi #t)
-                                        #:seen-list (cons mi seen-list))))
+                                        #:seen-list (cons mi seen-list)
+                                        #:minimum-inspector inspector)))
      
      ;; Run or make available phases of the module body:
      (unless (label-phase? instance-phase)
