In standard Linux there is no way for an ordinary user process to access I/O ports, since only privileged processes are allowed to touch I/O ports. Moreover, even if a privileged process spawns off other processes, the latter do not inherit I/O port permissions. Thus, it is impossible using standard Linux system calls for a process to be granted I/O port access without entrusting that process with full superuser privileges, seriously compromising system security.
One solution to this problem (suggested by Prof. Hank Dietz
at Purdue University) is to add a new system call, giveioperm()
,
to allow any privileged process to control I/O port
access permission for an arbitrary Linux process.
In effect, this call implements an extension of ioperm()
in which the PID of the controlled process is inserted as the first
argument to the function; see the manual entry on ioperm()
for details about the other arguments.
The syntax for the new call is:
giveioperm(pid, PORT_NUMBER, OFFSET, COMMAND);
Where pid is the id of the process for which
I/
O port permissions are being altered, and
COMMAND is 1 or 0 depending on whether the permission needs
to be given or taken away. The call returns 0 on success or
-errno
on error.
The complete source code for the call is given below:
/* giveioperm: Enables a privileged process to give permission to * another process to use the io port. * Written by: Gayathri Krishnamurthy */ asmlinkage int sys_giveioperm(int pid, unsigned long from, unsigned long num, int turn_on) { register struct task_struct *p, **q; p = (struct task_struct *)NULL; if (!suser()) { return -EPERM; } if(pid == 0){ /* The current (privileged) task needs permission ... */ p = current; } else { /* Get the tss for the task with the given pid */ for (q = &LAST_TASK; q > &FIRST_TASK; --q) { if ((*q) && ((*q)->pid == pid)) { break; } } /* First task is the init task - ignore */ if(q == &FIRST_TASK) { /* No such process */ return -ESRCH; } p = *q; } /* Give (or take away) the io permission */ set_bitmap((unsigned long *)p->tss.io_bitmap, from, num, !turn_on); return 0; }
To install this system call, add #define
__NR_xx
to unistd.h
to assign a call
number for giveioperm()
, where xx, the system
call index, is something descriptive of the call (in our case we have
defined it as __NR_sys_giveioperm
). This will be used to
set up the vector through sys_call_table
to invoke the
code for the call. In our kernel we found that the kernel did not do
this automatically, and so we edited the table in
boot/entry.S
to put in the appropriate call.
Follow other instructions needed to install the call
found at the URL:
http://sunsite.unc.edu/mdw/LDP/khg/section2.6.4.html
The above simply creates the system call; it does not create a C
interface for giveioperm()
. Instead, the call interface
appears as:
syscall(Your_syscall_number_here, pid, portnum, offset, turnon);Gayathri Krishnamurthy ([email protected])