From: Neil Horman <nhorman@redhat.com>

Patch to add check to get_chrdev_list and get_blkdev_list to prevent reads
of /proc/devices from spilling over the provided page if more than 4096
bytes of string data are generated from all the registered character and
block devices in a system

Signed-off-by: Neil Horman <nhorman@redhat.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: <viro@parcelfarce.linux.theplanet.co.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 drivers/block/genhd.c |   12 ++++++++++--
 fs/char_dev.c         |   13 ++++++++++++-
 fs/proc/proc_misc.c   |    2 +-
 include/linux/genhd.h |    2 +-
 4 files changed, 24 insertions(+), 5 deletions(-)

diff -puN drivers/block/genhd.c~add-check-to-proc-devices-read-routines drivers/block/genhd.c
--- 25/drivers/block/genhd.c~add-check-to-proc-devices-read-routines	2005-05-09 20:09:32.000000000 -0700
+++ 25-akpm/drivers/block/genhd.c	2005-05-09 20:09:32.000000000 -0700
@@ -40,7 +40,7 @@ static inline int major_to_index(int maj
 
 #ifdef CONFIG_PROC_FS
 /* get block device names in somewhat random order */
-int get_blkdev_list(char *p)
+int get_blkdev_list(char *p, int used)
 {
 	struct blk_major_name *n;
 	int i, len;
@@ -49,10 +49,18 @@ int get_blkdev_list(char *p)
 
 	down(&block_subsys_sem);
 	for (i = 0; i < ARRAY_SIZE(major_names); i++) {
-		for (n = major_names[i]; n; n = n->next)
+		for (n = major_names[i]; n; n = n->next) {
+			/*
+			 * If the curent string plus the 5 extra characters
+			 * in the line would run us off the page, then we're done
+			 */
+			if ((len + used + strlen(n->name) + 5) >= PAGE_SIZE)
+				goto page_full;
 			len += sprintf(p+len, "%3d %s\n",
 				       n->major, n->name);
+		}
 	}
+page_full:
 	up(&block_subsys_sem);
 
 	return len;
diff -puN fs/char_dev.c~add-check-to-proc-devices-read-routines fs/char_dev.c
--- 25/fs/char_dev.c~add-check-to-proc-devices-read-routines	2005-05-09 20:09:32.000000000 -0700
+++ 25-akpm/fs/char_dev.c	2005-05-09 20:09:32.000000000 -0700
@@ -56,10 +56,21 @@ int get_chrdev_list(char *page)
 
 	down(&chrdevs_lock);
 	for (i = 0; i < ARRAY_SIZE(chrdevs) ; i++) {
-		for (cd = chrdevs[i]; cd; cd = cd->next)
+		for (cd = chrdevs[i]; cd; cd = cd->next) {
+			/*
+			 * if the current name, plus the 5 extra characters
+			 * in the device line for this entry
+			 * would run us off the page, we're done
+			 */
+			if ((len+strlen(cd->name) + 5) >= PAGE_SIZE)
+				goto page_full;
+
+
 			len += sprintf(page+len, "%3d %s\n",
 				       cd->major, cd->name);
+		}
 	}
+page_full:
 	up(&chrdevs_lock);
 
 	return len;
diff -puN fs/proc/proc_misc.c~add-check-to-proc-devices-read-routines fs/proc/proc_misc.c
--- 25/fs/proc/proc_misc.c~add-check-to-proc-devices-read-routines	2005-05-09 20:09:32.000000000 -0700
+++ 25-akpm/fs/proc/proc_misc.c	2005-05-09 20:09:32.000000000 -0700
@@ -451,7 +451,7 @@ static int devices_read_proc(char *page,
 				 int count, int *eof, void *data)
 {
 	int len = get_chrdev_list(page);
-	len += get_blkdev_list(page+len);
+	len += get_blkdev_list(page+len, len);
 	return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
diff -puN include/linux/genhd.h~add-check-to-proc-devices-read-routines include/linux/genhd.h
--- 25/include/linux/genhd.h~add-check-to-proc-devices-read-routines	2005-05-09 20:09:32.000000000 -0700
+++ 25-akpm/include/linux/genhd.h	2005-05-09 20:09:32.000000000 -0700
@@ -224,7 +224,7 @@ static inline void free_disk_stats(struc
 extern void disk_round_stats(struct gendisk *disk);
 
 /* drivers/block/genhd.c */
-extern int get_blkdev_list(char *);
+extern int get_blkdev_list(char *, int);
 extern void add_disk(struct gendisk *disk);
 extern void del_gendisk(struct gendisk *gp);
 extern void unlink_gendisk(struct gendisk *gp);
_