[patch] inode changes to 2.2.2-pre2

Andrea Arcangeli (andrea@e-mind.com)
Sat, 6 Feb 1999 15:20:01 +0100 (CET)


2.2.2-pre2 only removes the depth thing but:

a) it doesn't take care of syncing inodes if the inuse list is not
freeable.

b) It also continue to use the preshirnk thing (that avoid the
admin to increase the inode-nr if preshirnk is 1).

c) And it still allow an user to increase the number of inodes over the
inode-max limit (because it recovers with __get_free_pages() if
free_inodes() failed).

So basically I think should be still very easy for a malicious user (that
knows a and c) to eat memory via inode.c.

Here the other part of my latest patch rediffed against 2.2.2-pre2:

Index: inode.c
===================================================================
RCS file: /var/cvs/linux/fs/inode.c,v
retrieving revision 1.1.1.3
retrieving revision 1.1.2.10
diff -u -r1.1.1.3 -r1.1.2.10
--- inode.c 1999/02/06 13:20:05 1.1.1.3
+++ inode.c 1999/02/06 13:36:49 1.1.2.10
@@ -54,7 +54,7 @@
* NOTE! You also have to own the lock if you change
* the i_state of an inode while it is in use..
*/
-spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t inode_lock = SPIN_LOCK_UNLOCKED;

/*
* Statistics gathering..
@@ -62,9 +62,8 @@
struct {
int nr_inodes;
int nr_free_inodes;
- int preshrink; /* pre-shrink dcache? */
- int dummy[4];
-} inodes_stat = {0, 0, 0,};
+ int dummy[5];
+} inodes_stat = {0, 0, };

int max_inodes;

@@ -140,7 +139,7 @@
inode->i_sb->s_op->write_inode(inode);
}

-static inline void sync_one(struct inode *inode)
+static void sync_one(struct inode *inode)
{
if (inode->i_state & I_LOCK) {
spin_unlock(&inode_lock);
@@ -312,6 +311,35 @@
return busy;
}

+#define INODE(entry) (list_entry(entry, struct inode, i_list))
+
+static int produce_freeable_inodes(void)
+{
+ struct super_block * sb;
+ int freeable = 0;
+
+ for (sb = sb_entry(super_blocks.next);
+ sb != sb_entry(&super_blocks);
+ sb = sb_entry(sb->s_list.next))
+ {
+ struct list_head * tmp, * head;
+
+ if (!sb->s_dev)
+ continue;
+
+ head = &sb->s_dirty;
+ while ((tmp = head->prev) != head)
+ {
+ struct inode * inode = INODE(tmp);
+ sync_one(inode);
+ if (!inode->i_count)
+ freeable = 1;
+ }
+ }
+
+ return freeable;
+}
+
/*
* This is called with the inode lock held. It searches
* the in-use for freeable inodes, which are moved to a
@@ -325,7 +353,6 @@
*/
#define CAN_UNUSE(inode) \
(((inode)->i_count | (inode)->i_state) == 0)
-#define INODE(entry) (list_entry(entry, struct inode, i_list))

static int free_inodes(void)
{
@@ -333,6 +360,7 @@
int found = 0;

INIT_LIST_HEAD(freeable);
+ again:
entry = inode_in_use.next;
while (entry != &inode_in_use) {
struct list_head *tmp = entry;
@@ -347,9 +375,11 @@
found = 1;
}

- if (found) {
+ if (found)
dispose_list(freeable);
- found = 1; /* silly compiler */
+ else {
+ if (produce_freeable_inodes())
+ goto again;
}

return found;
@@ -404,7 +434,7 @@
/*
* Check whether to restock the unused list.
*/
- if (inodes_stat.preshrink) {
+ if (inodes_stat.nr_inodes > max_inodes) {
struct list_head *tmp;
try_to_free_inodes(8);
tmp = inode_unused.next;
@@ -414,6 +444,9 @@
inode = list_entry(tmp, struct inode, i_list);
return inode;
}
+ spin_unlock(&inode_lock);
+ printk(KERN_WARNING "grow_inodes: inode-max limit reached\n");
+ return NULL;
}

spin_unlock(&inode_lock);
@@ -437,9 +470,6 @@
*/
inodes_stat.nr_inodes += INODES_PER_PAGE;
inodes_stat.nr_free_inodes += INODES_PER_PAGE - 1;
- inodes_stat.preshrink = 0;
- if (inodes_stat.nr_inodes > max_inodes)
- inodes_stat.preshrink = 1;
return inode;
}

@@ -448,7 +478,6 @@
* the dcache and then try again to free some inodes.
*/
prune_dcache(inodes_stat.nr_inodes >> 2);
- inodes_stat.preshrink = 1;

spin_lock(&inode_lock);
free_inodes();
@@ -463,7 +492,7 @@
}
spin_unlock(&inode_lock);

- printk("grow_inodes: allocation failed\n");
+ printk(KERN_WARNING "grow_inodes: allocation failed\n");
return NULL;
}

Andrea Arcangeli

-
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.tux.org/lkml/