#cs354#xinu#operating-systems#os
1 Objective
Provide kernel support for asynchronous event handling using callback functions that preserve isolation/protection when events are triggered by asynchronous events.
3 Asynchronous Event Handling using Callback Function
3.1 Overview
- In Problem 5 Lab 4, we introduced kernel support for run-time detour to user callback function specified in argument of dreamvac() when sleepms() returned.
- Instead of callback function being called directly by user code, ROP was used by kernel to modify return of sleepms().
- We will support asynchronous user callback function execution in response to asynchronous events.
3.2 Callback Function Registration
- Code systemcall in system/cbsignal.c
syscall cbsignal(uint16 etype, void(* cbf)(void), uint32 val)
- Used by processes to register a user callback function cbf with the kernel.
- If registration successful, kernel will arrange execution of callback function when the event specified by first argument etype occurs.
- To preserve isolation/protection, kernel arranges execution of cbf in user mode and in the context of the process that registered the callback function.
- User Mode is interpreted to mean when a process returns from a system call, or from an interrupt where there are clear dividing lines between kernel code and user code.
- Third argument val is meaningful only for some event types
- cbsignal() returns 0 if specified arguments are valid and request can be met.
- Returns SYSERR otherwise.
- Validity determined per-event type etype
3.3 Alarm Event
Registration
- If etype = 1, then kernel sets alarm event.
- Timer specified by argument val (msec).
- Reuse clkcountermsec from lab1 to support alarm event.
- Introduce 4 new process table fields:
-
- Init 0
-
- Init 0
-
- SYSERROR Cases:
- When cbsignal(1, func, val) called, SYSERR if val > 5000.
- cbsignal() returns SYSERR if pralarmreg != 0
- Meaning only 1 alarm allowed.
- SYSERR if function pointer falls outside text segment of XINU
- Check initialize.c which uses boundary addresses of XINU’s text alignment.
- If cbsignal() arguments are valid:
- cbsignal sets pralarmreg to 1 (indicating alarm event is registered)
- prcbf1 is set to the passed in function
- pralarmtime is set to clkcountermsec + val
ROP Detour
- Each time clkhandler() invoked from XINU interrupt, check if clkcountermsec is >= current alarm time of current process.
- If so, arrange detour. Instead of jumping to callback function, kernel will set up process stack so executing iret in clkdisp() will cause jump to system function.
void cbfmanager(void)
in system/cbfmanager.S- Kernel function that does not need to be executed in kernel mode (system function)
- Calls the user callback function prcbf1, then returns to cbfmanager() which then returns to original return address of clkdisp()*
Recall
- Interrupt pushes values in EFLAGS, CS, EIP into current process stack.
- To induce cbfmanager() to return to original return address of clkdisp(), where clkdisp() rearranges stack if detour needs to be made, so that it jumps to cbfmanager on executing iret.
- When clkhandler() returns to clkdisp(), process stack contains 8 registers saved by pushal then EIP, CS, EF.
- First step is to shuffle values near top of stack so that 8 registers are stored below EF,CS,EIP, and then to savekeep the original return address EIP before overwriting it with function pointer “cbfmanager”.
- When iret executed, values of cbfmanager, EF, CS are popped by x86 upon jumping to cbfmanager().
- When cbfmanager starts executing, the top of stack must contain the 8 register values.
CALL instruction -> Pushes EIP to stack then jumps RET -> Pops EIP IRET Instruction -> Pops EF, CS, EIP (pop EIP first, CS next, then EF)
EF | CS | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
transform to below
EF | CS | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | EF | CS | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
after iret | |||||||||||||
in cbmgr: |
EF | CS | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
before returning modify to:
CS | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|
popal, add 8 to esp, ret
3.4 Asynchronous Message Receive Event
Registration
If etype == 2
then kernel invokes cbf if message arrives from sender.
- For message to arrive, receiver must be context switched out — sender is current.
pretype
set to 2 if callback function for alarm event is not outstanding.pretype
set to 3 if both alarm and asynchronous message event are outstanding- When message receive or alarm event has been handled, pretype must be updated accordingly.
New Process Table Fields:
void (*prcbf2)()
uint16 prmsgreg
init 0- Set to 1 if outstanding async message event. Only 1 allowed at a time.
ROP Detour
-
Consider receiver calling sleepms() as case where receiver has been ctx switched out when sender runs.
-
Modify send() to check if receiver has callback function for async msg receive.
-
If so, use technique from problem 5 lab 4 to locate ret addr of sleepms to its caller in stack of receiver process.
-
Modify receiver’s stack to detour to receiver’s callback function prcbf2
-
When prcbf2 returns, it jumps to original ret addr. of sleepms()
-
Stack modification occurs in send() opposed to wakeup() or resched()
-
Update of prmsgreg must wait until receiver wakes up, becomes ready, and is selected by scheduler to become current.
-
Only then is prmsgreg updated since receiver is about to make a detour to callback function when sleepms() returns.
ctxsw stack:
Stack Ctxsw |
---|
RET |
EBP (to resched) |
EF |
8x REG |
Stack Resched |
---|
Ret |
EBP |
Stack Sleepms |
---|
arg1 |
ret |
ebp |
Stack Sleepms |
---|
gap |
ret old |
ret new |
ebp |