2001-04-05 05:16:50

by Tom Leete

[permalink] [raw]
Subject: Race in fs/proc/generic.c:make_inode_number()

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

--
The Daemons lurk and are dumb. -- Emerson


2001-04-05 14:38:08

by Tom Leete

[permalink] [raw]
Subject: [PATCH] Re: Race in fs/proc/generic.c:make_inode_number()

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
--
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)

2001-04-06 12:00:57

by Maneesh Soni

[permalink] [raw]
Subject: Re: [PATCH] Re: Race in fs/proc/generic.c:make_inode_number()

Just a couple of points:

On Thu, Apr 05, 2001 at 10:36:10AM -0400, Tom Leete wrote:
[...]
> +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.

2001-04-06 15:49:01

by Tom Leete

[permalink] [raw]
Subject: Re: [PATCH] Re: Race in fs/proc/generic.c:make_inode_number()

Maneesh Soni wrote:
>
> Just a couple of points:
>
> On Thu, Apr 05, 2001 at 10:36:10AM -0400, Tom Leete wrote:
> [...]
> > +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

--
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)