• No results found

Access Control with Memory Management

Chapter 8 Hardware Security

8.3 MEMORY PROTECTION

8.3.5 Access Control with Memory Management

Where the address space is divided into two segments—system and user, as in figure 8-2— the process must not be allowed to write into system space when running unprivileged programs in user mode. When running in system mode, the process is permitted to read and write all of virtual memory. A context switch from user mode to system mode is accomplished by using a special instruction that transfers control to one of a set of restricted locations in system space. Since the partition between system space and user space is static, hardware can easily enforce these access restrictions based on the privilege mode (context) of the process. For greater flexibility, however, it is desirable to allow the system software to specify exactly which pages of the process’s address space are readable and/or writable in each context.

Before machines provided transparent memory management, access decisions were based on the identity of the physical page. Each physical page was labeled with information such as a key and some access bits indicating whether the page was readable or writable. Each process was assigned a key that had been loaded by the operating system into a process status word. The hardware checked the key on each memory reference, prohibiting access unless the process status word key matched the memory key and unless the access bits matched the desired read or write access mode. A design similar to this was used on IBM 360 machines.

The approach of associating access information (keys and access bits) with physical pages becomes unmanageable when pages are not fixed in memory for the life of a process. Each time ownership of a physical page changes (as when a page is swapped), the access information has to

PROCESS A PROCESS B ALPHA ALPHA 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 PROCESS A SEGMENT DESCRIPTOR TABLE PROCESS B SEGMENT DESCRIPTOR TABLE SEGMENT ALPHA PAGE DESCRIPTOR TABLE

be modified. And, if two processes share a page—one for reading and one for writing—the access information has to be changed on each process switch.

With a descriptor-based address translation mechanism, where each process has a private set of descriptors, a process’s access modes to a page or segment of memory are specified in the descriptors. There may be two sets of access modes, one for use while the process is running in user context and the other for use while the process is running in system context:

where the fields W, R, and E are single bits that indicate whether the process has write, read, or execute access to the specified segment or page of memory. Because the descriptors are already fetched during address translation, descriptor-based access control imposes little or no additional overhead in process switching, in context switching within a process, or in swapping processes into or out of memory.

In figure 8-3, where there are two levels of descriptors, the access information should be contained in the segment descriptors, not in the page descriptors. Otherwise, it would not be possible to specify different modes of access for two processes sharing the same segment.

Use of Virtual Memory to Map Objects

When a user program reads a file, it asks the operating system to carry out an I/O operation that reads the contents of a block of the file into a buffer somewhere in the user space of the process’s virtual memory. Another way for a program to access a file is for the operating system to map the file directly onto the virtual address space of the process. The file (or a portion of it) is paged into memory and the words in the file become accessible through memory reference instructions, just as if they were words in the process’s virtual memory. The process must keep track of the starting virtual address of the file; it then computes the virtual addresses of words in the file as offsets from that starting address. In such an architecture, the entire file system is potentially part of each process’s virtual memory, although a single process will only have a small number of files mapped at any one time.

The idea of translating a virtual address into a location in a file is not unlike the concept of demand paging, by which portions of a process’s virtual memory, are kept on a paging disk and copied into physical memory when referenced. The difference is that the process specifically requests that a particular file be mapped into a particular range of virtual memory locations. The major benefit of mapping files directly into virtual memory is that no privileged I/O is required to access a file once it is mapped: the normal demand paging and memory management mechanisms are used. Random access to any location in the file is also made easier, and performance improves somewhat because a system call is not required on each access; these benefits, however, are not security concerns.

MEMORY DESCRIPTOR: W R E W R E PHYSICAL ADDRESS System User

The technique of mapping files into virtual memory, although routine in Multics, is rare elsewhere. (The feature is available in VMS and probably in other systems, but it is not routinely

used for all file accesses.) Programmers traditionally use read and write system I/O calls for accessing files; giving programmers a way to access a file as a long string of characters or as an array of words requires rethinking how applications are designed. To be practical, this technique must be well integrated into the programming language.

The simplest way to implement file-to-virtual-memory mapping is to associate a single segment with an object. Thereafter, the process need simply keep track of the segment number of each file it has mapped and need not reserve virtual memory space for objects prior to mapping. Associating hardware segments with system storage objects has advantages from a security standpoint, because the hardware’s access control mechanisms are used without additional software control.

In order to represent objects as segments efficiently, however, the range of possible segment sizes must span several orders of magnitude. It a typical object exceeds the size of a maximum- length segment, a mechanism must be available for building large objects out of several segments, and this means that direct access to a word in an object cannot consist of a simple offset from the beginning of a segment. Mapping multisegment objects onto consecutive segment numbers in virtual memory leads to an awkward software structure, although it is done in the SCOMP (with considerable difficulty) because of the small segments. The small segments in

the Intel 80286 cause similar programming difficulties. Even Multics, which supports segments of up to 218 36-bit words (over 1 million characters), has a multisegment file facility for the occasional very large file that will not fit into a segment. These multisegment files are not mapped into contiguous segments, so all applications that might potentially use them must use a special application package that provides an I/O-style interface for file access rather than direct memory reference—thereby defeating some of the performance advantages of direct memory reference to files.

Another performance problem that occurs with multisegment files managed by applications is the need for a process to initiate (map for the first time) a number of segments each time the file is opened. Segment initiation is a relatively slow process, comparable in overhead to the opening of a file in conventional file systems. Moreover, the operating system (which treats each segment independently and knows nothing about the relationship between segments in a multisegment file) must maintain separate access control information for each segment in the file, even though all segments are accessed identically. These performance and overhead problems have no adverse effect on security per se, except that the poor performance of such features might drive people to find shortcuts that bypass the security controls.

The preceding discussion indicates that, if you are to represent files as segments efficiently, a reasonably large file should fit into one segment. It is also necessary that the system support enough segments for each process to free most processes from concern about terminating (unmapping) segments no longer in use. If a process runs out of segment numbers as it brings new objects into its address space, the process must find a segment to terminate that it knows is not needed by any programs in the process. Determining which segments are no longer in use and avoiding inappropriate reuse of segment numbers for deleted objects pose a difficult problem

and an undesirable complication—complete cooperation by user applications is required. For example, when a program is finished with a file or segment, that program cannot simply unmap the segment because other programs in the process might still retain pointers to virtual memory locations in the original segment If a new file is later mapped into the same virtual memory locations thereby inheriting the same segment number, those other programs, using their stored pointers, will access the wrong file. As in the case of small segments, these problems are security issues only to the extent that they may so complicate the applications that people will seek shortcuts that bypass the controls.

In addition to mapping files into virtual memory as segments, executable programs can also be mapped. On most systems, executable programs are in fact stored in files. Usually, however, all separately compiled programs that will run in a process must be linked in advance into one large executable image that is stored in a single file or series of segments. At execution time, the image is mapped into virtual memory as one or more memory segments. From an access control standpoint, the image is one object to which the process has execute access permission, and no finer-grained control on the programs within the image is enforced.

Dynamic linking is a sophisticated capability used in Multics that allows separately compiled

programs to remain in individual segments without prior linking. The first time a program is called by a process, the segment containing the program is mapped into the virtual address space, and all linking between the process and the program takes place at execution time.1 An advantage of dynamic linking (and the ability to retain each program in its own segment) is that the protection attributes of the program (obtained from its segment) remain enforced by hardware, thereby permitting different programs in the same process to have different attributes. The importance of this will become more apparent as we discuss execution domains in the next section.