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

From: Moritz Muehlenhoff <[email-masked]>
Date: Wed, 18 Nov 2009 22:13:48 +0100

On Tue, Oct 13, 2009 at 11:13:01AM +0000, Gerrit Pape wrote:
> tags 547902 + patch
> quit

[ Adding Christian to CC: ]

Christian, did you submit your patch upstream?

Cheers,
        Moritz

> 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++;
> 
> 
>