The giveioperm() Function

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 (gayathri@ecn.purdue.edu)