Subject: Re: Bug#547902: dash only reads the first character from pipe (bash reads whole line)

From: Gerrit Pape <[email-masked]>
Date: Tue, 13 Oct 2009 11:13:01 +0000

tags 547902 + patch
quit

Hi, please see http://bugs.debian.org/547902

Here's a patch from Christian Hohnstaedt <[email-masked]>

Thanks, Gerrit.

---
Testparameter:
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; do dd if=/proc/sys/net/ipv4/tcp_wmem bs=$i 2>/dev/null; done

for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; do dd if=/proc/sys/net/ipv4/tcp_wmem skip=$i bs=1 2>/dev/null |wc -c ; done

diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 9270125..038df14 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -2117,17 +2117,16 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
 #define TMPBUFLEN 21
 	int *i, vleft, first=1, neg, val;
 	unsigned long lval;
-	size_t left, len;
+	size_t left, len, off;
 	
 	char buf[TMPBUFLEN], *p;
 	char __user *s = buffer;
 	
-	if (!tbl_data || !table->maxlen || !*lenp ||
-	    (*ppos && !write)) {
+	if (!tbl_data || !table->maxlen || !*lenp) {
 		*lenp = 0;
 		return 0;
 	}
-	
+	off = 0;
 	i = (int *) tbl_data;
 	vleft = table->maxlen / sizeof(*i);
 	left = *lenp;
@@ -2176,25 +2175,31 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
 			if (conv(&neg, &lval, i, 1, data))
 				break;
 		} else {
+			loff_t diff;
 			p = buf;
-			if (!first)
-				*p++ = '\t';
-	
 			if (conv(&neg, &lval, i, 0, data))
 				break;
 
-			sprintf(p, "%s%lu", neg ? "-" : "", lval);
-			len = strlen(buf);
-			if (len > left)
-				len = left;
-			if(copy_to_user(s, buf, len))
-				return -EFAULT;
-			left -= len;
-			s += len;
+			len = sprintf(p, "%s%s%lu", first ? "" : "\t",
+						neg ? "-" : "", lval);
+			diff = *ppos - off;
+			off += len;
+			if (diff > 0 && diff < len) {
+				p += diff;
+				len -= diff;
+			}
+			if (off > *ppos) {
+				if (len > left)
+					len = left;
+				if(copy_to_user(s, p, len))
+					return -EFAULT;
+				left -= len;
+				s += len;
+			}
 		}
 	}
 
-	if (!write && !first && left) {
+	if (!write && !first && left && off >= *ppos) {
 		if(put_user('\n', s))
 			return -EFAULT;
 		left--, s++;