All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jean Delvare <khali@linux-fr.org>
To: Guenter Roeck <linux@roeck-us.net>
Cc: linux-kernel@vger.kernel.org,
	Andrew Morton <akpm@linux-foundation.org>,
	lm-sensors@lm-sensors.org, Juergen Beisert <jbe@pengutronix.de>
Subject: Re: [lm-sensors] [PATCH v2] linux/kernel.h: Fix DIV_ROUND_CLOSEST with unsigned divisors
Date: Wed, 19 Dec 2012 22:21:15 +0000	[thread overview]
Message-ID: <20121219232115.727a2e91@endymion.delvare> (raw)
In-Reply-To: <1355928015-26212-1-git-send-email-linux@roeck-us.net>

Hi Guenter,

On Wed, 19 Dec 2012 06:40:15 -0800, Guenter Roeck wrote:
> Commit 263a523 fixes a warning seen with W=1 due to change in
> DIV_ROUND_CLOSEST. Unfortunately, the C compiler converts divide operations
> with unsigned divisors to unsigned, even if the dividend is signed and
> negative (for example, -10 / 5U = 858993457). The C standard says "If one
> operand has unsigned int type, the other operand is converted to unsigned
> int", so the compiler is not to blame.

This is surprising to say the least. But if the C standard says so...

I wouldn't be surprised if there are bugs because of this in the kernel
and in other projects.

> As a result, DIV_ROUND_CLOSEST(0, 2U) and similar operations now return
> bad values, since the automatic conversion of expressions such as "0 - 2U/2"
> to unsigned was not taken into account.
> 
> Fix by checking for the divisor variable type when deciding which operation
> to perform. This fixes DIV_ROUND_CLOSEST(0, 2U), but still returns bad values
> for negative dividends divided by unsigned divisors. Mark the latter case as
> unsupported.

True but this last issue isn't specific to the DIV_ROUND_CLOSEST
implementation, it would also happen with a simple division.

> Reported-by: Juergen Beisert <jbe@pengutronix.de>
> Tested-by: Juergen Beisert <jbe@pengutronix.de>
> Cc: Jean Delvare <khali@linux-fr.org>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> ---
> v2: Description update (v1 wasn't supposed to make it to lkml)
> 
>  include/linux/kernel.h |    6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> index d97ed58..45726dc 100644
> --- a/include/linux/kernel.h
> +++ b/include/linux/kernel.h
> @@ -77,13 +77,15 @@
>  
>  /*
>   * Divide positive or negative dividend by positive divisor and round
> - * to closest integer. Result is undefined for negative divisors.
> + * to closest integer. Result is undefined for negative divisors and
> + * for negative dividends if the divisor variable type is unsigned.

Thinking a bit more about this... Documenting the non-working cases is
great, however I don't really expect all developers to pay attention. I
can also imagine variable types changing from signed to unsigned later,
and never thinking this can introduce a bug.

So, is there nothing we can do to spot at least the second issue at
build time? For regular division there's nothing we can do (although I
don't understand why gcc doesn't warn...) but here we get the
opportunity to report the issue, let's take it.

And given that the divisor is almost always a constant,
maybe we can check for negative divisors too, this would be safer and
the code size increase would probably be very small in practice.
Opinions?

>   */
>  #define DIV_ROUND_CLOSEST(x, divisor)(			\
>  {							\
>  	typeof(x) __x = x;				\
>  	typeof(divisor) __d = divisor;			\
> -	(((typeof(x))-1) > 0 || (__x) > 0) ?		\
> +	(((typeof(x))-1) > 0 ||				\
> +	 ((typeof(divisor))-1) > 0 || (__x) > 0) ?	\
>  		(((__x) + ((__d) / 2)) / (__d)) :	\
>  		(((__x) - ((__d) / 2)) / (__d));	\
>  }							\

Looks good.

-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@lm-sensors.org
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors

WARNING: multiple messages have this Message-ID (diff)
From: Jean Delvare <khali@linux-fr.org>
To: Guenter Roeck <linux@roeck-us.net>
Cc: linux-kernel@vger.kernel.org,
	Andrew Morton <akpm@linux-foundation.org>,
	lm-sensors@lm-sensors.org, Juergen Beisert <jbe@pengutronix.de>
Subject: Re: [PATCH v2] linux/kernel.h: Fix DIV_ROUND_CLOSEST with unsigned divisors
Date: Wed, 19 Dec 2012 23:21:15 +0100	[thread overview]
Message-ID: <20121219232115.727a2e91@endymion.delvare> (raw)
In-Reply-To: <1355928015-26212-1-git-send-email-linux@roeck-us.net>

Hi Guenter,

On Wed, 19 Dec 2012 06:40:15 -0800, Guenter Roeck wrote:
> Commit 263a523 fixes a warning seen with W=1 due to change in
> DIV_ROUND_CLOSEST. Unfortunately, the C compiler converts divide operations
> with unsigned divisors to unsigned, even if the dividend is signed and
> negative (for example, -10 / 5U = 858993457). The C standard says "If one
> operand has unsigned int type, the other operand is converted to unsigned
> int", so the compiler is not to blame.

This is surprising to say the least. But if the C standard says so...

I wouldn't be surprised if there are bugs because of this in the kernel
and in other projects.

> As a result, DIV_ROUND_CLOSEST(0, 2U) and similar operations now return
> bad values, since the automatic conversion of expressions such as "0 - 2U/2"
> to unsigned was not taken into account.
> 
> Fix by checking for the divisor variable type when deciding which operation
> to perform. This fixes DIV_ROUND_CLOSEST(0, 2U), but still returns bad values
> for negative dividends divided by unsigned divisors. Mark the latter case as
> unsupported.

True but this last issue isn't specific to the DIV_ROUND_CLOSEST
implementation, it would also happen with a simple division.

> Reported-by: Juergen Beisert <jbe@pengutronix.de>
> Tested-by: Juergen Beisert <jbe@pengutronix.de>
> Cc: Jean Delvare <khali@linux-fr.org>
> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
> ---
> v2: Description update (v1 wasn't supposed to make it to lkml)
> 
>  include/linux/kernel.h |    6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> index d97ed58..45726dc 100644
> --- a/include/linux/kernel.h
> +++ b/include/linux/kernel.h
> @@ -77,13 +77,15 @@
>  
>  /*
>   * Divide positive or negative dividend by positive divisor and round
> - * to closest integer. Result is undefined for negative divisors.
> + * to closest integer. Result is undefined for negative divisors and
> + * for negative dividends if the divisor variable type is unsigned.

Thinking a bit more about this... Documenting the non-working cases is
great, however I don't really expect all developers to pay attention. I
can also imagine variable types changing from signed to unsigned later,
and never thinking this can introduce a bug.

So, is there nothing we can do to spot at least the second issue at
build time? For regular division there's nothing we can do (although I
don't understand why gcc doesn't warn...) but here we get the
opportunity to report the issue, let's take it.

And given that the divisor is almost always a constant,
maybe we can check for negative divisors too, this would be safer and
the code size increase would probably be very small in practice.
Opinions?

>   */
>  #define DIV_ROUND_CLOSEST(x, divisor)(			\
>  {							\
>  	typeof(x) __x = x;				\
>  	typeof(divisor) __d = divisor;			\
> -	(((typeof(x))-1) > 0 || (__x) > 0) ?		\
> +	(((typeof(x))-1) > 0 ||				\
> +	 ((typeof(divisor))-1) > 0 || (__x) > 0) ?	\
>  		(((__x) + ((__d) / 2)) / (__d)) :	\
>  		(((__x) - ((__d) / 2)) / (__d));	\
>  }							\

Looks good.

-- 
Jean Delvare

  parent reply	other threads:[~2012-12-19 22:21 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-12-19 14:40 [lm-sensors] [PATCH v2] linux/kernel.h: Fix DIV_ROUND_CLOSEST with unsigned divisors Guenter Roeck
2012-12-19 14:40 ` Guenter Roeck
2012-12-19 21:47 ` [lm-sensors] " Andrew Morton
2012-12-19 21:47   ` Andrew Morton
2012-12-19 22:41   ` [lm-sensors] " Guenter Roeck
2012-12-19 22:41     ` Guenter Roeck
2012-12-20 10:22     ` [lm-sensors] " Jean Delvare
2012-12-20 10:22       ` Jean Delvare
2012-12-20 10:30       ` [lm-sensors] " Juergen Beisert
2012-12-20 10:30         ` Juergen Beisert
2012-12-20 11:00         ` [lm-sensors] " Jean Delvare
2012-12-20 11:00           ` Jean Delvare
2012-12-20 14:13       ` [lm-sensors] " Guenter Roeck
2012-12-20 14:13         ` Guenter Roeck
2012-12-19 22:21 ` Jean Delvare [this message]
2012-12-19 22:21   ` Jean Delvare
2012-12-19 23:01   ` [lm-sensors] " Guenter Roeck
2012-12-19 23:01     ` Guenter Roeck
2012-12-20 11:48     ` [lm-sensors] " Jean Delvare
2012-12-20 11:48       ` Jean Delvare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20121219232115.727a2e91@endymion.delvare \
    --to=khali@linux-fr.org \
    --cc=akpm@linux-foundation.org \
    --cc=jbe@pengutronix.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@roeck-us.net \
    --cc=lm-sensors@lm-sensors.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.