Log in

View Full Version : Ring 0 -> Ring 3 : Upward calls and downward returns theoretically possible?


Clandestiny
December 3rd, 2004, 10:28
Hi guys,

I was recently reading a paper on the old MULTICS ring architecture.

http://www.multicians.org/protection.pdf

It is rather interesting in the sense that it gives the history behind the development of the Pentium protection ring architecture which is, in fact, based upon MULTICS.

We all know that you can't call a ring 3 function from ring 0, but interestingly this seems to be "theoretically" possible. The described procedure for an upward call in MULTICS is basically the same as for the downward call (ring 3 to ring 0 for example) and is accomplished by installing a call gate. There are, however, a few caveats.

1. The calling procedure will need to copy the arguments into a segment accessible by the lesser privileged callee.
2. 2 call gates must be installed, one for the call, and one for the return to the greater privilege level.
3. Recursion is a problem becauses you would essentially need a "stack" for return gates.

Here is the excerpt from the paper that discusses these issues if anyone is interested:

"upward call occurs when a procedure executing in ring
n calls an entry point in another procedure segment
whose execute bracket bottom is m > n. When the call
occurs, the ring of execution will change to m. The subsequent
return is downward, resetting the ring of execution
to n. These cases exhibit two unpleasant characteristics
of a general cross-domain call and return that were
not present in the other cases.

The first is that the calling procedure may specify
arguments that cannot be referenced from the ring of the
called procedure. (For a downward call, the nested subset
property of rings guaranteed that this could not
happen.) There are at least three possible solutions to
this problem. Oneis to require that the calling procedure
specify only arguments that are accessible in the highernumbered
ring of the called procedure. This solution
compromises programming generality by forcing the
calling procedure to take special precautions in the case
of an upward call. Another possible solution is to dynamically
include in the ring of the called procedure the
capabilities to reference the arguments. Because a segment
is the smallest unit of information for which access
can be individually controlled, this forces segments
which contain arguments to contain no other information
that should be protected differently, again compromising
programming generality, unless segments are inexpensive
enough that, as a matter of course, every data
item is placed in its own segment. It may also be expensive
to dynamically include and remove the argument
referencing capabilities from the called ring. The third
possible solution is copying arguments into segments
that are accessible in the called ring, and then copying
them back to their original locations on return. This solution
restricts the possibility of sharing arguments with
parallel processes. None of the three solutions lends
itself to a straightforward hardware implementation.

The second unpleasant characteristic is that a gate
must be provided for the downward return. (For an
upward return the nested subset property of rings made
a return gate unnecessary.) The return gate must be
created at the time of the upward call and be destroyed
when the subsequent return occurs. If recursive calls
into a ring are allowed, then this gate must behave as
though it were stored in a push-down stack, so that only
the gate at the top of the stack can be used. The gates
specified in SDWts seem poorly suited to this sort of dynamic
behavior.

Processor mechanisms to provide dynamic,
stacked return gates are not obvious at this time.
Because of these two problems, the hardware described
in the next section does not implement upward
calls and downward returns without software intervention.
Although the same object code sequences that
perform all calls and returns are used in these cases as
well, the hardware responds to each attempted upward
call or downward return by generating a trap to a supervisor
procedure which performs the necessary environment
adjustments.

The manner in which the stack pointer register value
of the calling procedure is saved when a call occurs and
restored when the subsequent return occurs has not yet
been discussed. For a same-ring or downward call, it is
reasonable to trust the called procedure to savethe value
left in the stack pointer register by the calling procedure
and then restore it before the subsequent return, since in
these cases the called procedure has access capabilities
which allow it to cause the calling procedure to malfunction
in other ways anyway. For an upward call and the
subsequent downward return, the same convention can
be used without violating the protection provided by the
lower ring if the intervening software verifies the restored
stack pointer register value when performing the
downward return.

Cheers,
Clandestiny

JMI
December 3rd, 2004, 12:49
Thanks for the heads up.

Regards,

homersux
December 3rd, 2004, 17:30
Very Interesting Stuff, imagine the possibilities with this hack.

doug
December 4th, 2004, 00:07
am I the only one who doesn't understand the real benefit of this?

I understand ring3->ring0, as you obviously gain something.. but why on earth would you want to go through the pain of calling ring3 from ring0?

Woodmann
December 4th, 2004, 18:32
Hi,

I dont really see this as an upward versus downward thing. Call gates that can be opened and closed without using the stack is brilliant. Then again, I cannot understand how this could work. Perhaps this is just some way to prevent "hooking" a call ?

It seems to me that they are suggesting a way to store or move data at a level that could be considered sub-processor.

I am so full of shit

Woodmann

JMI
December 5th, 2004, 21:25
Well, who would want to disagree with the Boss??

Regards,

evlncrn8
December 6th, 2004, 03:20
i did it another way, via dioc, passing the va of the r3 api call (messageboxa in my case) to the r0 driver.. worked fine heh and then calling it via call eax etc...

begemott
December 9th, 2004, 04:16
Hi All,

>We all know that you can't call a ring 3 function from ring 0, but >interestingly this seems to be "theoretically" possible.

My 2 cents:

There is a very interesting (IMHO) paper here:
http://www.osronline.com/article.cfm?id=168
It describes how to call a R3 function from R0 practically ;-)
Even more:
"The resulting function, within the user?s address space, then executes in kernel mode."

Cheers
Begemott

Kayaker
December 9th, 2004, 19:46
Hi

Thanks. The not-directly-accessible document which was just mentioned is called
Understanding and Using Execution Context in NT Drivers
and is included with this thread

Link: Kernel Services in User Space on Win2K
http://woodmann.net/forum/showthread.php?t=6003

Kayaker

nikolatesla20
December 9th, 2004, 19:50
Yes, almost 90% of driver calls from ring3 will actually execute in the ring3 app's context, which means the driver can simply use the ring3 app's memory addresses without problem.

A driver that uses delay dispatch or queues can still accomplish this if the ring3 app passes the driver it's processID in some sort of initialization routine. Then the driver can simply KeSwitchContext() or whatever to change to the ring3 app's context, and wallah.

-nt20