Kernel/userspace communication

As was said in overall design of systrace, communication of kernel and userland portion is done via systrace device /dev/systrace.
Userland systrace process gets questions from kernel portion via poll()/read() and sends replies back via ioctl().

Life cycle of userland portion looks like this:
main()
  intercept_run() /* runs traced binary */
  [ or run binary via daemon() and attach to it via intercept_attach(); ]

  while (intercept_read(trfd) != -1) {
     intercept_read()
       -> intercept.read()
         -> fbsd_read()
           read(fd, &msg, sizeof(msg)); /* read msg from /dev/systrace */
           switch(msg.msg_type){
	     case SYSTR_MSG_ASK:
	   	-> intercept_syscall()
           	   -> intercept_sccb_find(emulation, name);
                     [ intercept_translate() ] (optionaly)
                     ...
                     if (!ic_abort) 
                        action = (*sc->cb)(fd, ...);
                     else
                        action = ICPOLICY_NEVER;
                     } else if (intercept_gencb != NULL)
                        action = (*intercept_gencb)(fd, ...);

                     /* Resume execution of the process */
                     intercept.answer(fd, ...);
           
	     case SYSTR_MSG_RES:
	     ...
	     case SYSTR_MSG_CHILD:
	     ...
	   }
  }

  systrace_dumppolicy(); /* eventualy save generated policy to file */
fig.1: /bin/systrace lifecycle (pseudocode), arrows hint function call

/bin/systrace execs traced binary via intercept_run(), optionaly it will run it in background via daemon() and then it will attach to it via intercept_attach(). If you are using xsystrace, it will start it here.

The whole concept of intercept_*() functions is that they are basically wrapper for functions specialized for given system type. For each system type, there is specific set of functions, so e.g. for FreeBSD intercept_read() will call freebsd-syscalls.c:fbsd_read(). intercept.h:intercept_system structure contains pointers to functions from given system. In this case, pointers to the functions from freebsd-syscalls.c.

Then systrace enters intercept_read() cycle, in which it reads messages from kernel and responds to them via intercept.answer(). This will of course call fbsd_answer(), which creates and fills struct systrace_answer structure and passes it to kernel via ioctl(fd, STRIOCANSWER, &ans). syscall will be processed or not according to this answer.
kernel portion then updates str_process structure and wakes up the process.

More details on intercept.read():
intercept.read()
  -> fbsd_read()
      SYSTR_MSG_ASK /* kernel asks for policy for this given syscall */
        solves via intercept_syscall()
      SYSTR_MSG_RES /* kernel reports syscall result to userland */
        intercept_syscall_result()
		/* speciall execve handling */
      SYSTR_MSG_EMUL
        fbsd_set_emulation()
        fbsd_switch_emulation()
        intercept_syscall_result()
      SYSTR_MSG_UGID /* kernel reports real uid/gid */
        intercept_ugid()
        fbsd_answer()
      SYSTR_MSG_CHILD /* kernel informs about child exec/exit */
        intercept_child_info()
      SYSTR_MSG_POLICYFREE /* kernel signales to free the policy in userland */
        intercept_policy_free()
fig.2: how userland portion reacts on various messages from kernel

The reading cycle stops, when intercept_read() returns -1. If police was generated, it will save it to file in ~/.systrace/.


Vladimir Kotal <vlada--at--devnull.cz>
$Id: kernel-userspace.html,v 1.3 2003/12/17 22:03:52 techie Exp $