giveioperm() Call for Linux

H. Dietz and G. Krishnamurthy
School of Electrical and Computer Engineering, Purdue University

The effective use of PAPERS (Purdue's Adapter for Parallel Execution and Rapid Synchronization) requires that user processes be able to directly access hardware device registers (i.e., read/write I/O ports without an OS call). In standard Linux, only priviledged processes can request such I/O access rights. Moreover, using a privileged process to get rights and then spawn the user task does not work because I/O port permissions are not inherited. 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, compromising system security.

Our solution to this problem 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);

Information about giveioperm() is available online from http://garage.ecn.purdue.edu/~papers

Alternatively, contact Prof. Hank Dietz, School of Electrical and Computer Engineering, Purdue University, West Lafayette, IN, 47907-1285, phone: (317) 494 3357, fax: (317) 494 3371, email: hankd@ecn.purdue.edu