Security

MzScheme offers several mechanisms for managing security:

All security mechanisms rely on thread-specific parameters (see section 7.9).

9.1  Security Guards

A security guard provides a set of access-checking procedures to be called when a thread initiates access of a file, directory, or network connection through a primitive procedure. For example, when a thread calls open-input-file, the thread's current security guard is consulted to check whether the thread is allowed read access to the file. If access is granted, the thread receives a port that it may use indefinitely, regardless of changes to the security guard (although the port's custodian could shut down the port; see section 9.2).

A thread's current security guard is determined by the current-security-guard parameter (see section 7.9.1.8). Every security guard has a parent, and a parent's access procedures are called whenever a child's access procedures are called. Thus, a thread cannot increase its own access arbitrarily by installing a new guard. The initial security guard enforces no access restrictions other than those enforced by the host platform.

(make-security-guard parent-security-guard file-proc network-proc) creates a new security guard whose parent is parent-security-guard.

The file-proc procedure must accept three arguments:

The network-proc procedure must accept four arguments:

The return value of file-proc or network-proc is ignored. To deny access, the procedure must raise an exception or otherwise escape from the context of the primitive call. If the procedure returns, the parent's corresponding procedure is called on the same inputs, and so on up the chain of security guards.

The file-proc and network-proc procedures are invoked in the thread that called the access-checked primitive. Breaks may or may not be enabled (see section 6.6). Full continuation jumps are blocked going into or out of the file-proc or network-proc call (see section 6.3).

(security-guard? v) returns #t if v is a security guard value, #f otherwise.

9.2  Custodians

A custodian manages a collection of threads, file-stream ports, TCP ports, TCP listeners, UDP sockets, and byte converters.21 Whenever a thread, file-stream port, TCP port, TCP listener, or UDP socket is created, it is placed under the management of the current custodian (as determined by the current-custodian parameter; see section 7.9.1.8).

The main operation on a custodian is to shut down its managed values via custodian-shutdown-all. In other words, custodian-shutdown-all generalizes kill-thread to forcibly and immediately close a set of ports, TCP connections, etc., as well as terminate (or suspend) a set of threads. For example, a web server might use a custodian to manage all of the resources of a particular session so that the session can be cleanly terminated if it exceeds its allowed lifetime.

A custodian that has been shut down cannot manage new objects. If the current custodian is shut down before a procedure is called to create a managed resource (e.g., open-input-port, thread), the exn:fail:contract exception is raised.

A thread can have multiple managing custodians, and a suspended thread created with thread/suspend-to-kill can have zero custodians. Extra custodians become associated with a thread through thread-resume (see section 7.1). When a thread has multiple custodians, it is not necessarily killed by a custodian-shutdown-all, but shut-down custodians are removed from the thread's managing set, and the thread is killed when its managing set becomes empty.

The values managed by a custodian are only weakly held by the custodian. As a result, a will (see section 13.3) can be executed for a value that is managed by a custodian. In addition, a custodian only weakly references its subordinate custodians; if a subordinate custodian is unreferenced but has its own subordinates, then the custodian may be collected, at which point its subordinates become immediately subordinate to the collected custodian's superordinate custodian.

(make-custodian [custodian]) creates a new custodian that is subordinate to custodian. When custodian is directed (via custodian-shutdown-all) to shut down all of its managed values, the new subordinate custodian is automatically directed to shut down its managed values as well. The default value for custodian is the current custodian.

(custodian-shutdown-all custodian) closes all open ports and closes all active TCP listeners and UDP sockets that are managed by custodian. It also removes custodian (and its subordinates) as managers of all threads; when a thread has no managers, it is killed.22 If the current thread is to be killed, all other shut-down actions take place before killing the thread.

(custodian? v) returns #t if v is a custodian value, #f otherwise.

(custodian-managed-list custodian super-custodian) returns a list of immediately managed objects and subordinate custodians for custodian, where custodian is itself subordinate to super-custodian (directly or indirectly). If custodian is not strictly subordinate to super-custodian, the exn:fail:contract exception is raised.

(custodian-require-memory need-k custodian) registers a require check if MzScheme is compiled with support for memory accounting, otherwise the exn:fail:unsupported exception is raised. If a check is registered, and if MzScheme later reaches a state after garbage collection (see section 13.4) where need-k bytes are not available to the current custodian, custodian is shut down.

(custodian-limit-memory limit-custodian limit-k stop-custodian) registers a limit check if MzScheme is compiled with support for memory accounting (a.k.a. ``3m''), otherwise the exn:fail:unsupported exception is raised. If a check is registered, and if MzScheme later reaches a state after garbage collection (see section 13.4) where limit-custodian owns more than limit-k bytes, then stop-custodian is shut down.

9.3  Thread Groups

A thread group is a collection of threads and other thread groups that have equal claim to the CPU. By nesting thread groups and by creating certain threads within certain groups, a programmer can control the amount of CPU allocated to a set of threads. Every thread belongs to a thread group, which is determined by the current-thread-group parameter (see section 7.9.1.8) when the thread is created. Thread groups and custodians (see section 9.2) are independent.

The root thread group receives all of the CPU that the operating system gives MzScheme. Every thread or nested group in a particular thread group receives equal allocation of the CPU (a portion of the group's access), although a thread may relinquish part of its allocation by sleeping or synchronizing with other processes.

(make-thread-group [thread-group]) creates a new thread group that belongs to thread-group. The default value for thread-group is the current thread group, as determined by the current-thread-group parameter.

(thread-group? v) returns #t if v is a thread group value, #f otherwise.

9.4  Inspectors and Modules

Just as inspectors control access to structure fields (see section 4.5), inspectors control access to module bindings (see section 5). The default inspector for modules is determined by the current-code-inspector parameter, instead of the current-inspector parameter.

When a module declaration is evaluated, the value of the current-code-inspector parameter is associated with the module declaration. When the module is invoked via require or dynamic-require, a sub-inspector of the module's declaration-time inspector is created, and this sub-inspector is associated with the module invocation. Any inspector that controls the sub-inspector (i.e., the declaration-time inspector and its superior) controls the module invocation.

Control over a module invocation enables

If the value of 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 current-code-inspector.

The inspector for a module invocation is specific to a particular module registry, in case a module is attached to a new registry via namespace-attach-module. The invocation inspector in a particular registry can be changed via namespace-unprotect-module (but changing the inspector requires control over the old one).

Control over a module declaration (as opposed to a mere invocation) enables the reconstruction of syntax objects that contain references to the module's unexported identifiers. Otherwise, the compiler and macro expander prevent any reference to an unexported identifier, unless the reference appears within an expression that was generated by the module's macros (or, more precisely, a macro from a module whose declaration inspector controls the invocation of the identifier's module). See section 12.6.3 for further information.


21 In MrEd, custodians also manage eventspaces.

22 ``Killing'' a thread created with thread/suspend-to-kill merely suspends the thread.