Skip to contentSkip to conversation

Race in fs/proc/generic.c:make_inode_number()

DISCUSSION Tom Leete 2 participants 4 messages Apr 5, 2001 – Apr 6, 2001

lore
Share & tools
lore

Tom Leete

author
#0 lore raw

Hello,

The proc_alloc_map bitfield is unprotected by any lock, and find_first_zero_bit() is not atomic. Concurrent module loading can race here.

static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8];

static int make_inode_number(void)

{
	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
	if (i<0 || i>=PROC_NDYNAMIC) 
		return -1;
	set_bit(i, (void *) proc_alloc_map);
	return PROC_DYNAMIC_FIRST + i;
}

Cheers,
Tom

Signature · 1 line
The Daemons lurk and are dumb. -- Emerson
author
#1 lore raw
Quoted 4 lines · I wrote:

The proc_alloc_map bitfield is unprotected by any lock, and find_first_zero_bit() is not atomic. Concurrent module loading can race here.

Hello,

Here is a patch for this. It looks like callers are always in user context (kmalloc flag GFP_KERNEL), so I used a light spinlock.

Cheers,
Tom

Signature · 31 lines
The Daemons lurk and are dumb. -- Emerson

--- linux-2.4.3/fs/proc/generic.c.orig	Thu Apr  5 10:03:02 2001
+++ linux-2.4.3/fs/proc/generic.c	Thu Apr  5 10:22:48 2001
@@ -192,13 +192,22 @@
 
 static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8];
 
+spinlock_t proc_alloc_map_lock = RW_LOCK_UNLOCKED;
+
 static int make_inode_number(void)
 {
-	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
-	if (i<0 || i>=PROC_NDYNAMIC) 
-		return -1;
+	int i;
+	spin_lock(&proc_alloc_map_lock);
+	i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+	if (i<0 || i>=PROC_NDYNAMIC) {
+		i = -1;
+		goto out;
+	}
 	set_bit(i, (void *) proc_alloc_map);
-	return PROC_DYNAMIC_FIRST + i;
+	i += PROC_DYNAMIC_FIRST;
+out:
+	spin_unlock(&proc_alloc_map_lock);
+	return i;
 }
 
 static int proc_readlink(struct dentry *dentry, char *buffer, int buflen)
#2 lore raw
In reply to Tom Leete

Just a couple of points:

On Thu, Apr 05, 2001 at 10:36:10AM -0400, Tom Leete wrote:
[...]

Review

Quoted 2 lines
(quoted diff) +2 -0
+spinlock_t proc_alloc_map_lock = RW_LOCK_UNLOCKED;
+

Why not make this static?
Initializer should be SPIN_LOCK_UNLOCKED.

Maneesh Soni
Linux Technology Center,
IBM Bangalore.

author
#3 lore raw
In reply to Tom Leete
Quoted 10 lines · Maneesh Soni

Just a couple of points:

On Thu, Apr 05, 2001 at 10:36:10AM -0400, Tom Leete wrote:
[...]

Review

Quoted 2 lines
(quoted diff) +2 -0
+spinlock_t proc_alloc_map_lock = RW_LOCK_UNLOCKED;
+

Why not make this static?
Initializer should be SPIN_LOCK_UNLOCKED.

Thanks, you're right on both counts.

Linus, Alan, this version is more correct. I also checked for other uses of proc_alloc_map[], The only case is in deallocation, and it looks ok to me.

Tom

Signature · 32 lines
The Daemons lurk and are dumb. -- Emerson

diff -u linux-2.4.3/fs/proc/generic.c.orig linux-2.4.3/fs/proc/generic.c
--- linux-2.4.3/fs/proc/generic.c.orig	Thu Apr  5 10:03:02 2001
+++ linux-2.4.3/fs/proc/generic.c	Thu Apr  5 10:22:48 2001
@@ -192,13 +192,22 @@
 
 static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8];
 
+spinlock_t proc_alloc_map_lock = RW_LOCK_UNLOCKED;
+
 static int make_inode_number(void)
 {
-	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
-	if (i<0 || i>=PROC_NDYNAMIC) 
-		return -1;
+	int i;
+	spin_lock(&proc_alloc_map_lock);
+	i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+	if (i<0 || i>=PROC_NDYNAMIC) {
+		i = -1;
+		goto out;
+	}
 	set_bit(i, (void *) proc_alloc_map);
-	return PROC_DYNAMIC_FIRST + i;
+	i += PROC_DYNAMIC_FIRST;
+out:
+	spin_unlock(&proc_alloc_map_lock);
+	return i;
 }
 
 static int proc_readlink(struct dentry *dentry, char *buffer, int buflen)