From: Anton Blanchard <anton@samba.org>

Linas Vepstas has audited the ppc64 proc code and found a number of issues.



 25-akpm/arch/ppc64/kernel/mf_proc.c   |   26 ++++--
 25-akpm/arch/ppc64/kernel/proc_pmc.c  |   29 ++++---
 25-akpm/arch/ppc64/kernel/rtas-proc.c |  132 +++++++++++++++++++++++++---------
 25-akpm/arch/ppc64/kernel/scanlog.c   |   13 ++-
 4 files changed, 147 insertions(+), 53 deletions(-)

diff -puN arch/ppc64/kernel/mf_proc.c~ppc64-proc-fixes arch/ppc64/kernel/mf_proc.c
--- 25/arch/ppc64/kernel/mf_proc.c~ppc64-proc-fixes	Wed Jan  7 11:22:19 2004
+++ 25-akpm/arch/ppc64/kernel/mf_proc.c	Wed Jan  7 11:22:19 2004
@@ -220,19 +220,25 @@ int proc_mf_dump_side
 
 int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data)
 {
+	char stkbuf[10];
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
-	if ((*buffer != 'A') &&
-	    (*buffer != 'B') &&
-	    (*buffer != 'C') &&
-	    (*buffer != 'D'))
+	if (count > 9) count = 9;
+	if (copy_from_user (stkbuf, buffer, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+	if ((*stkbuf != 'A') &&
+	    (*stkbuf != 'B') &&
+	    (*stkbuf != 'C') &&
+	    (*stkbuf != 'D'))
 	{
 		printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n");
 		return -EINVAL;
 	}
 
-	mf_setSide(*buffer);
+	mf_setSide(*stkbuf);
 
 	return count;			
 }
@@ -256,6 +262,7 @@ int proc_mf_dump_src
 
 int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data)
 {
+	char stkbuf[10];
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
@@ -265,11 +272,16 @@ int proc_mf_change_src(struct file *file
 		return -EINVAL;
 	}
 
-	if ((count == 1) && ((*buffer) == '\0'))
+	if (count > 9) count = 9;
+	if (copy_from_user (stkbuf, buffer, count)) {
+		return -EFAULT;
+	}
+
+	if ((count == 1) && ((*stkbuf) == '\0'))
 	{
 		mf_clearSrc();
 	} else {
-		mf_displaySrc(*(u32 *)buffer);
+		mf_displaySrc(*(u32 *)stkbuf);
 	}
 
 	return count;			
diff -puN arch/ppc64/kernel/proc_pmc.c~ppc64-proc-fixes arch/ppc64/kernel/proc_pmc.c
--- 25/arch/ppc64/kernel/proc_pmc.c~ppc64-proc-fixes	Wed Jan  7 11:22:19 2004
+++ 25-akpm/arch/ppc64/kernel/proc_pmc.c	Wed Jan  7 11:22:19 2004
@@ -127,7 +127,7 @@ void proc_ppc64_init(void)
 			ent->nlink = 1;
 			ent->data = (void *)proc_ppc64_pmc_cpu_root[i];
 			ent->read_proc = (void *)proc_ppc64_pmc_stab_read;
-			ent->write_proc = (void *)proc_ppc64_pmc_stab_read;
+			ent->write_proc = NULL;
 		}
 
 		ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, 
@@ -136,7 +136,7 @@ void proc_ppc64_init(void)
 			ent->nlink = 1;
 			ent->data = (void *)proc_ppc64_pmc_cpu_root[i];
 			ent->read_proc = (void *)proc_ppc64_pmc_htab_read;
-			ent->write_proc = (void *)proc_ppc64_pmc_htab_read;
+			ent->write_proc = NULL;
 		}
 	}
 
@@ -146,7 +146,7 @@ void proc_ppc64_init(void)
 		ent->nlink = 1;
 		ent->data = (void *)proc_ppc64_pmc_system_root;
 		ent->read_proc = (void *)proc_ppc64_pmc_stab_read;
-		ent->write_proc = (void *)proc_ppc64_pmc_stab_read;
+		ent->write_proc = NULL;
 	}
 
 	ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, 
@@ -155,7 +155,7 @@ void proc_ppc64_init(void)
 		ent->nlink = 1;
 		ent->data = (void *)proc_ppc64_pmc_system_root;
 		ent->read_proc = (void *)proc_ppc64_pmc_htab_read;
-		ent->write_proc = (void *)proc_ppc64_pmc_htab_read;
+		ent->write_proc = NULL;
 	}
 
 	/* Create directories for the hardware counters. */
@@ -168,7 +168,7 @@ void proc_ppc64_init(void)
 			ent->nlink = 1;
 			ent->data = (void *)proc_ppc64_pmc_cpu_root[i];
 			ent->read_proc = (void *)proc_ppc64_pmc_hw_read;
-			ent->write_proc = (void *)proc_ppc64_pmc_hw_read;
+			ent->write_proc = NULL;
 		}
 	}
 
@@ -178,7 +178,7 @@ void proc_ppc64_init(void)
 		ent->nlink = 1;
 		ent->data = (void *)proc_ppc64_pmc_system_root;
 		ent->read_proc = (void *)proc_ppc64_pmc_hw_read;
-		ent->write_proc = (void *)proc_ppc64_pmc_hw_read;
+		ent->write_proc = NULL;
 	}
 }
 
@@ -676,15 +676,22 @@ static inline void proc_pmc_tlb(void)
 
 int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data )
 {
-	if      ( ! strncmp( buffer, "stop", 4 ) )
+	char stkbuf[10];
+	if (count > 9) count = 9;
+	if (copy_from_user (stkbuf, buffer, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+
+	if      ( ! strncmp( stkbuf, "stop", 4 ) )
 		proc_pmc_stop();
-	else if ( ! strncmp( buffer, "start", 5 ) )
+	else if ( ! strncmp( stkbuf, "start", 5 ) )
 		proc_pmc_start();
-	else if ( ! strncmp( buffer, "reset", 5 ) )
+	else if ( ! strncmp( stkbuf, "reset", 5 ) )
 		proc_pmc_reset();
-	else if ( ! strncmp( buffer, "cpi", 3 ) )
+	else if ( ! strncmp( stkbuf, "cpi", 3 ) )
 		proc_pmc_cpi();
-	else if ( ! strncmp( buffer, "tlb", 3 ) )
+	else if ( ! strncmp( stkbuf, "tlb", 3 ) )
 		proc_pmc_tlb();
 	
 	/* IMPLEMENT ME */
diff -puN arch/ppc64/kernel/rtas-proc.c~ppc64-proc-fixes arch/ppc64/kernel/rtas-proc.c
--- 25/arch/ppc64/kernel/rtas-proc.c~ppc64-proc-fixes	Wed Jan  7 11:22:19 2004
+++ 25-akpm/arch/ppc64/kernel/rtas-proc.c	Wed Jan  7 11:22:19 2004
@@ -241,12 +241,18 @@ void proc_rtas_init(void)
 static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf,
 		size_t count, loff_t *ppos)
 {
+	char stkbuf[40];  /* its small, its on stack */
 	struct rtc_time tm;
 	unsigned long nowtime;
 	char *dest;
 	int error;
 
-	nowtime = simple_strtoul(buf, &dest, 10);
+	if (39 < count) count = 39;
+	if (copy_from_user (stkbuf, buf, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+	nowtime = simple_strtoul(stkbuf, &dest, 10);
 	if (*dest != '\0' && *dest != '\n') {
 		printk("ppc_rtas_poweron_write: Invalid time\n");
 		return count;
@@ -267,18 +273,23 @@ static ssize_t ppc_rtas_poweron_write(st
 static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf,
 		size_t count, loff_t *ppos)
 {
+	char stkbuf[40];  /* its small, its on stack */
 	int n;
 	if (power_on_time == 0)
-		n = sprintf(buf, "Power on time not set\n");
+		n = snprintf(stkbuf, 40, "Power on time not set\n");
 	else
-		n = sprintf(buf, "%lu\n", power_on_time);
+		n = snprintf(stkbuf, 40, "%lu\n", power_on_time);
 
-	if (*ppos >= strlen(buf))
+	int sn = strlen (stkbuf) +1;
+	if (*ppos >= sn)
 		return 0;
-	if (n > strlen(buf) - *ppos)
-		n = strlen(buf) - *ppos;
+	if (n > sn - *ppos)
+		n = sn - *ppos;
 	if (n > count)
 		n = count;
+	if (copy_to_user (buf, stkbuf + (*ppos), n)) {
+		return -EFAULT;
+	}
 	*ppos += n;
 	return n;
 }
@@ -291,11 +302,16 @@ static ssize_t ppc_rtas_progress_write(s
 {
 	unsigned long hex;
 
-	strcpy(progress_led, buf); /* save the string */
+	if (count >= MAX_LINELENGTH) count = MAX_LINELENGTH -1;
+	if (copy_from_user (progress_led, buf, count)) { /* save the string */
+		return -EFAULT;
+	}
+	progress_led[count] = 0;
+
 	/* Lets see if the user passed hexdigits */
-	hex = simple_strtoul(buf, NULL, 10);
-	
-	ppc_md.progress ((char *)buf, hex);
+	hex = simple_strtoul(progress_led, NULL, 10);
+
+	ppc_md.progress ((char *)progress_led, hex);
 	return count;
 
 	/* clear the line */ /* ppc_md.progress("                   ", 0xffff);*/
@@ -305,14 +321,30 @@ static ssize_t ppc_rtas_progress_read(st
 		size_t count, loff_t *ppos)
 {
 	int n = 0;
-	if (progress_led != NULL)
-		n = sprintf (buf, "%s\n", progress_led);
-	if (*ppos >= strlen(buf))
+
+	if (progress_led == NULL) return 0;
+
+	char * tmpbuf = kmalloc (MAX_LINELENGTH, GFP_KERNEL);
+	if (!tmpbuf) {
+		printk(KERN_ERR "error: kmalloc failed\n");
+		return -ENOMEM;
+	}
+	n = sprintf (tmpbuf, "%s\n", progress_led);
+
+	int sn = strlen (tmpbuf) +1;
+	if (*ppos >= sn) {
+		kfree (tmpbuf);
 		return 0;
-	if (n > strlen(buf) - *ppos)
-		n = strlen(buf) - *ppos;
+	}
+	if (n > sn - *ppos)
+		n = sn - *ppos;
 	if (n > count)
 		n = count;
+	if (copy_to_user (buf, tmpbuf + (*ppos), n)) {
+		kfree (tmpbuf);
+		return -EFAULT;
+	}
+	kfree (tmpbuf);
 	*ppos += n;
 	return n;
 }
@@ -323,12 +355,18 @@ static ssize_t ppc_rtas_progress_read(st
 static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, 
 		size_t count, loff_t *ppos)
 {
+	char stkbuf[40];  /* its small, its on stack */
 	struct rtc_time tm;
 	unsigned long nowtime;
 	char *dest;
 	int error;
 
-	nowtime = simple_strtoul(buf, &dest, 10);
+	if (39 < count) count = 39;
+	if (copy_from_user (stkbuf, buf, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+	nowtime = simple_strtoul(stkbuf, &dest, 10);
 	if (*dest != '\0' && *dest != '\n') {
 		printk("ppc_rtas_clock_write: Invalid time\n");
 		return count;
@@ -356,21 +394,27 @@ static ssize_t ppc_rtas_clock_read(struc
 	year = ret[0]; mon  = ret[1]; day  = ret[2];
 	hour = ret[3]; min  = ret[4]; sec  = ret[5];
 
+	char stkbuf[40];  /* its small, its on stack */
+
 	if (error != 0){
 		printk(KERN_WARNING "error: reading the clock returned: %s\n", 
 				ppc_rtas_process_error(error));
-		n = sprintf (buf, "0");
+		n = snprintf (stkbuf, 40, "0");
 	} else { 
-		n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec));
+		n = snprintf (stkbuf, 40, "%lu\n", mktime(year, mon, day, hour, min, sec));
 	}
 	kfree(ret);
 
-	if (*ppos >= strlen(buf))
+	int sn = strlen (stkbuf) +1;
+	if (*ppos >= sn)
 		return 0;
-	if (n > strlen(buf) - *ppos)
-		n = strlen(buf) - *ppos;
+	if (n > sn - *ppos)
+		n = sn - *ppos;
 	if (n > count)
 		n = count;
+	if (copy_to_user (buf, stkbuf + (*ppos), n)) {
+		return -EFAULT;
+	}
 	*ppos += n;
 	return n;
 }
@@ -764,7 +808,7 @@ int get_location_code(struct individual_
 		n += check_location_string(ret, buffer + n);
 		n += sprintf ( buffer+n, " ");
 		/* see how many characters we have printed */
-		sprintf ( t, "%s ", ret);
+		snprintf ( t, 50, "%s ", ret);
 
 		pos += strlen(t);
 		if (pos >= llen) pos=0;
@@ -777,10 +821,17 @@ int get_location_code(struct individual_
 static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf,
 		size_t count, loff_t *ppos)
 {
+	char stkbuf[40];  /* its small, its on stack */
 	unsigned long freq;
 	char *dest;
 	int error;
-	freq = simple_strtoul(buf, &dest, 10);
+
+	if (39 < count) count = 39;
+	if (copy_from_user (stkbuf, buf, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+	freq = simple_strtoul(stkbuf, &dest, 10);
 	if (*dest != '\0' && *dest != '\n') {
 		printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n");
 		return count;
@@ -799,14 +850,19 @@ static ssize_t ppc_rtas_tone_freq_read(s
 		size_t count, loff_t *ppos)
 {
 	int n;
-	n = sprintf(buf, "%lu\n", rtas_tone_frequency);
+	char stkbuf[40];  /* its small, its on stack */
+	n = snprintf(stkbuf, 40, "%lu\n", rtas_tone_frequency);
 
-	if (*ppos >= strlen(buf))
+	int sn = strlen (stkbuf) +1;
+	if (*ppos >= sn)
 		return 0;
-	if (n > strlen(buf) - *ppos)
-		n = strlen(buf) - *ppos;
+	if (n > sn - *ppos)
+		n = sn - *ppos;
 	if (n > count)
 		n = count;
+	if (copy_to_user (buf, stkbuf + (*ppos), n)) {
+		return -EFAULT;
+	}
 	*ppos += n;
 	return n;
 }
@@ -816,10 +872,17 @@ static ssize_t ppc_rtas_tone_freq_read(s
 static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf,
 		size_t count, loff_t *ppos)
 {
+	char stkbuf[40];  /* its small, its on stack */
 	unsigned long volume;
 	char *dest;
 	int error;
-	volume = simple_strtoul(buf, &dest, 10);
+
+	if (39 < count) count = 39;
+	if (copy_from_user (stkbuf, buf, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+	volume = simple_strtoul(stkbuf, &dest, 10);
 	if (*dest != '\0' && *dest != '\n') {
 		printk("ppc_rtas_tone_volume_write: Invalid tone volume\n");
 		return count;
@@ -840,14 +903,19 @@ static ssize_t ppc_rtas_tone_volume_read
 		size_t count, loff_t *ppos)
 {
 	int n;
-	n = sprintf(buf, "%lu\n", rtas_tone_volume);
+	char stkbuf[40];  /* its small, its on stack */
+	n = snprintf(stkbuf, 40, "%lu\n", rtas_tone_volume);
 
-	if (*ppos >= strlen(buf))
+	int sn = strlen (stkbuf) +1;
+	if (*ppos >= sn)
 		return 0;
-	if (n > strlen(buf) - *ppos)
-		n = strlen(buf) - *ppos;
+	if (n > sn - *ppos)
+		n = sn - *ppos;
 	if (n > count)
 		n = count;
+	if (copy_to_user (buf, stkbuf + (*ppos), n)) {
+		return -EFAULT;
+	}
 	*ppos += n;
 	return n;
 }
diff -puN arch/ppc64/kernel/scanlog.c~ppc64-proc-fixes arch/ppc64/kernel/scanlog.c
--- 25/arch/ppc64/kernel/scanlog.c~ppc64-proc-fixes	Wed Jan  7 11:22:19 2004
+++ 25-akpm/arch/ppc64/kernel/scanlog.c	Wed Jan  7 11:22:19 2004
@@ -135,17 +135,24 @@ static ssize_t scanlog_read(struct file 
 static ssize_t scanlog_write(struct file * file, const char * buf,
 			     size_t count, loff_t *ppos)
 {
+	char stkbuf[20];
 	unsigned long status;
 
+	if (count > 19) count = 19;
+	if (copy_from_user (stkbuf, buf, count)) {
+		return -EFAULT;
+	}
+	stkbuf[count] = 0;
+
 	if (buf) {
-		if (strncmp(buf, "reset", 5) == 0) {
+		if (strncmp(stkbuf, "reset", 5) == 0) {
 			DEBUG("reset scanlog\n");
 			status = rtas_call(ibm_scan_log_dump, 2, 1, NULL, NULL, 0);
 			DEBUG("rtas returns %ld\n", status);
-		} else if (strncmp(buf, "debugon", 7) == 0) {
+		} else if (strncmp(stkbuf, "debugon", 7) == 0) {
 			printk(KERN_ERR "scanlog: debug on\n");
 			scanlog_debug = 1;
-		} else if (strncmp(buf, "debugoff", 8) == 0) {
+		} else if (strncmp(stkbuf, "debugoff", 8) == 0) {
 			printk(KERN_ERR "scanlog: debug off\n");
 			scanlog_debug = 0;
 		}

_