#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)

EFCS12345678

transform to below

EFCS12345678EFCS
after iret
in cbmgr:
EFCS12345678

before returning modify to:

CS12345678

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:

  1. void (*prcbf2)()
  2. 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