A Hardware Architecture for Implementing Protection Rings
Schroeder and Saltzer
Protection rings
- protection rings are a generalization of the user/supervisor bit found in
most processors
- instead of just two possible "protection modes", there can be many
(Multics uses 8)
- lower-numbered rings have more privilieges than higher-numbered rings
(ring 0 is the most privileged)
- Multics segment descriptor words contain indications of the
highest-numbered rings that are allowed to read, or to write the segment
(these are called the read/write brackets)
- the execute bracket does not usually extend all the way down to ring 0,
unlike the read and write brackets; in this way the low rings of the system
can be protected against accidentally running code written by an unpriviliged
user: the lowest ring that can execute a segment is the highest ring that can
write to it, and the highest ring that can execute a segment is the highest
ring that can read it
Changing rings
- segments can list "entry points" that are callable from higher-numbered
rings, as well as a "gate extention", which is the highest ring number that
can call entry points in this segment
- if another segment running in a ring higher than the execute bracket, but
within the gate extension, calls an entry point of a segment, control is
transferred and the process starts to run in the highest ring of the execute
bracket
- changing to a higher ring can be accomplished by executing a "change ring
and jump" instruction; the segment containing the destination of the jump must
be executable in the new ring
- some issues with procedure calls that cross rings to become more
privileged:
- the less-privileged caller must not be able to interfere with the stack
of the more-privileged callee; this is ensured by giving each process one
stack segment for each ring; the processor automatically uses the right
stack segment for the current ring
- the caller should not be allowed to pass argument pointers that point to
data that the callee can reference, but the caller can't; when the callee
reads the data, its permissions are temporarily lowered to those of the
caller when accessing the parameter data
- the callee must return to the appropriate ring; the processor assures
this by keeping track of the ring the process was in when it did the call
- calling less-privileged routines is much harder; Multics emulates
this with software instead of implementing it in hardware
Hardware help
- in addition to the segment and offset, registers contain a ring field,
indicating what ring is supposed to dereference the register; programs can not
write to this register directly, but instead, the hardware fills it in when
the register is written
- the ring field is usually filled in with the ring number of the process
that writes the register, but there are some exceptions: on an indirect
reference, the ring field of the value that is eventually loaded is filled in
with the higher of the current ring number and the ring field of the
indirect pointer; this implements the lowering of permissions for reading
arguments passed from a less-privilieged caller
- see the flowcharts in the paper for far too much detail