Bug-Report

Ariel Rosenblatt (arielr@cs.huji.ac.il)
Mon, 10 Aug 1998 11:25:22 +0300 (IDT)


Bug Report:
============

The following bug was found in "fs/proc/root.c",
originally in the 2.1.78 kernel, but was still not fixed by the 2.1.107 kernel.

DESCRIPTION:
=============
The problem is that the routine "proc_root_readdir" keeps a spin-lock
(tasklist_lock) where it could possibly sleep, eg. in the "copy_to_user" of
"filldir". It does not happen too often, especially when the computer has
ample memory, but if the user page(s) that are to be read need to be first
brought in from the hard disk (eg. from the swap-partition - OR in
extreme/malicious use, even as a clean demand-page), then "schedule" will be
called while holding the important "tasklist_lock" spin-lock and the whole
computer will come to a halt!

SOLUTION:
==========
First collect all process-ids into a buffer, then free the spin-lock and
proceed as before, using the process-IDs from the buffer instead of directly
from the process-list.

ORIGINAL ROUTINE OF KERNEL 2.1.107:
================================================================================
static int proc_root_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
struct task_struct *p;
char buf[NUMBUF];
unsigned int nr = filp->f_pos;

if (nr < FIRST_PROCESS_ENTRY) {
int error = proc_readdir(filp, dirent, filldir);
if (error <= 0)
return error;
filp->f_pos = FIRST_PROCESS_ENTRY;
}
nr = FIRST_PROCESS_ENTRY;

read_lock(&tasklist_lock);
for_each_task(p) {
unsigned int pid;

if(nr++ < filp->f_pos)
continue;

if((pid = p->pid) != 0) {
unsigned long j = NUMBUF, i = pid;

do {
j--;
buf[j] = '0' + (i % 10);
i /= 10;
} while (i);

if (filldir(dirent, buf+j, NUMBUF-j,
filp->f_pos, (pid << 16) + PROC_PID_INO) < 0)
break;
}
filp->f_pos++;
}
read_unlock(&tasklist_lock);
return 0;
}
================================================================================
FIXED ROUTINE PROPOSED:
================================================================================
static int proc_root_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
struct task_struct *p;
char buf[NUMBUF];
unsigned int nr = filp->f_pos;
pid_t *pids;
int i, n;

if (nr < FIRST_PROCESS_ENTRY) {
int error = proc_readdir(filp, dirent, filldir);
if (error <= 0)
return error;
filp->f_pos = FIRST_PROCESS_ENTRY;
}
if(!(pids = kmalloc(NR_TASKS * sizeof(pid_t), GFP_KERNEL)))
return -ENOMEM;
nr = FIRST_PROCESS_ENTRY;

n = 0;
read_lock(&tasklist_lock);
for_each_task(p) {
if(nr++ < filp->f_pos)
continue;
pids[n++] = p->pid;
}
read_unlock(&tasklist_lock);
for(i = 0 ; i < n ; i++) {
unsigned int pid;

if((pid = pids[i]) != 0) {
unsigned long j = NUMBUF, i = pid;

do {
j--;
buf[j] = '0' + (i % 10);
i /= 10;
} while (i);

if (filldir(dirent, buf+j, NUMBUF-j,
filp->f_pos, (pid << 16) + PROC_PID_INO) < 0)
break;
}
filp->f_pos++;
}
kfree(pids);
return 0;
}
================================================================================

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\
Ariel Rosenblatt (arielr@cs.huji.ac.il) \
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/
______ ____ ___ ___ _ __ /
MOSIX Development Group ) ) ) ) ) ( ' ) \ / \
The Hebrew University / / / / / \ / / \
of Jerusalem, Israel ( ( (___( ___) _(_ __/ \ \
\
______________ E-mail : mosix-devel@cs.huji.ac.il ________________)

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.altern.org/andrebalsa/doc/lkml-faq.html