All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kernfs: Fix kernfs_name_compare
@ 2014-12-04 16:19 Rasmus Villemoes
  2014-12-04 16:53 ` Tejun Heo
  2014-12-05 22:41 ` [PATCH v2] " Rasmus Villemoes
  0 siblings, 2 replies; 5+ messages in thread
From: Rasmus Villemoes @ 2014-12-04 16:19 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Tejun Heo; +Cc: Rasmus Villemoes, linux-kernel

If the void pointers ns and kn->ns happen to differ by a multiple of
2^32, kernfs_name_compare returns 0, falsely reporting a match to the
caller.

If the return type was changed to long, returning ns - kn->ns would
still be wrong (see acbbe6fbb240 "kcmp: fix standard comparison bug"),
and it would also break the primary comparison of the hashes: The
expression hash - kn->hash has type unsigned int; on 64-bit, long is
perfectly capable of representing all unsigned int values, hence the
return value would always be positive if the hashes are
different. Since the latter is slightly subtle and since returning
hash - kn->hash in the first place is only ok because the hashes are
restricted to 31 bits, add a comment explaining that.

Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
---
 fs/kernfs/dir.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 1c771931bb60..64959716936d 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -201,10 +201,17 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns)
 static int kernfs_name_compare(unsigned int hash, const char *name,
 			       const void *ns, const struct kernfs_node *kn)
 {
+	/*
+	 * This is ok because the hash values are restricted to [0,
+	 * 2^31-1] and because we are returning int (it would be wrong
+	 * had the return type been wider!).
+	 */
 	if (hash != kn->hash)
 		return hash - kn->hash;
-	if (ns != kn->ns)
-		return ns - kn->ns;
+	if (ns < kn->ns)
+		return -1;
+	if (ns > kn->ns)
+		return 1;
 	return strcmp(name, kn->name);
 }
 
-- 
2.0.4


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH] kernfs: Fix kernfs_name_compare
  2014-12-04 16:19 [PATCH] kernfs: Fix kernfs_name_compare Rasmus Villemoes
@ 2014-12-04 16:53 ` Tejun Heo
  2014-12-05 22:41 ` [PATCH v2] " Rasmus Villemoes
  1 sibling, 0 replies; 5+ messages in thread
From: Tejun Heo @ 2014-12-04 16:53 UTC (permalink / raw)
  To: Rasmus Villemoes; +Cc: Greg Kroah-Hartman, linux-kernel

Hello,

On Thu, Dec 04, 2014 at 05:19:34PM +0100, Rasmus Villemoes wrote:
...
>  {
> +	/*
> +	 * This is ok because the hash values are restricted to [0,
> +	 * 2^31-1] and because we are returning int (it would be wrong
> +	 * had the return type been wider!).
> +	 */
>  	if (hash != kn->hash)
>  		return hash - kn->hash;

Let's just do < > comparisons here too and just note that trying to
determine ordering through subtraction is dangerous.

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2] kernfs: Fix kernfs_name_compare
  2014-12-04 16:19 [PATCH] kernfs: Fix kernfs_name_compare Rasmus Villemoes
  2014-12-04 16:53 ` Tejun Heo
@ 2014-12-05 22:41 ` Rasmus Villemoes
  2015-01-05 14:13   ` Tejun Heo
  1 sibling, 1 reply; 5+ messages in thread
From: Rasmus Villemoes @ 2014-12-05 22:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Tejun Heo; +Cc: Rasmus Villemoes, linux-kernel

Returning a difference from a comparison functions is usually wrong
(see acbbe6fbb240 "kcmp: fix standard comparison bug" for the long
story). Here there is the additional twist that if the void pointers
ns and kn->ns happen to differ by a multiple of 2^32,
kernfs_name_compare returns 0, falsely reporting a match to the
caller.

Technically 'hash - kn->hash' is ok since the hashes are restricted to
31 bits, but it's better to avoid that subtlety.

Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
---

Notes:
    v2: Also use explicit < > comparisons of the hashes.

 fs/kernfs/dir.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index 1c771931bb60..bf9e2f44c3df 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -201,10 +201,14 @@ static unsigned int kernfs_name_hash(const char *name, const void *ns)
 static int kernfs_name_compare(unsigned int hash, const char *name,
 			       const void *ns, const struct kernfs_node *kn)
 {
-	if (hash != kn->hash)
-		return hash - kn->hash;
-	if (ns != kn->ns)
-		return ns - kn->ns;
+	if (hash < kn->hash)
+		return -1;
+	if (hash > kn->hash)
+		return 1;
+	if (ns < kn->ns)
+		return -1;
+	if (ns > kn->ns)
+		return 1;
 	return strcmp(name, kn->name);
 }
 
-- 
2.1.3


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] kernfs: Fix kernfs_name_compare
  2014-12-05 22:41 ` [PATCH v2] " Rasmus Villemoes
@ 2015-01-05 14:13   ` Tejun Heo
  2015-01-05 16:14     ` Greg Kroah-Hartman
  0 siblings, 1 reply; 5+ messages in thread
From: Tejun Heo @ 2015-01-05 14:13 UTC (permalink / raw)
  To: Rasmus Villemoes; +Cc: Greg Kroah-Hartman, linux-kernel

On Fri, Dec 05, 2014 at 11:41:33PM +0100, Rasmus Villemoes wrote:
> Returning a difference from a comparison functions is usually wrong
> (see acbbe6fbb240 "kcmp: fix standard comparison bug" for the long
> story). Here there is the additional twist that if the void pointers
> ns and kn->ns happen to differ by a multiple of 2^32,
> kernfs_name_compare returns 0, falsely reporting a match to the
> caller.
> 
> Technically 'hash - kn->hash' is ok since the hashes are restricted to
> 31 bits, but it's better to avoid that subtlety.
> 
> Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>

Acked-by: Tejun Heo <tj@kernel.org>

And this is a -stable material.  Greg, can you please pick this one
up?

Thanks.

-- 
tejun

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v2] kernfs: Fix kernfs_name_compare
  2015-01-05 14:13   ` Tejun Heo
@ 2015-01-05 16:14     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 5+ messages in thread
From: Greg Kroah-Hartman @ 2015-01-05 16:14 UTC (permalink / raw)
  To: Tejun Heo; +Cc: Rasmus Villemoes, linux-kernel

On Mon, Jan 05, 2015 at 09:13:47AM -0500, Tejun Heo wrote:
> On Fri, Dec 05, 2014 at 11:41:33PM +0100, Rasmus Villemoes wrote:
> > Returning a difference from a comparison functions is usually wrong
> > (see acbbe6fbb240 "kcmp: fix standard comparison bug" for the long
> > story). Here there is the additional twist that if the void pointers
> > ns and kn->ns happen to differ by a multiple of 2^32,
> > kernfs_name_compare returns 0, falsely reporting a match to the
> > caller.
> > 
> > Technically 'hash - kn->hash' is ok since the hashes are restricted to
> > 31 bits, but it's better to avoid that subtlety.
> > 
> > Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
> 
> Acked-by: Tejun Heo <tj@kernel.org>
> 
> And this is a -stable material.  Greg, can you please pick this one
> up?

Will do, thanks.

greg k-h

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2015-01-05 16:14 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-04 16:19 [PATCH] kernfs: Fix kernfs_name_compare Rasmus Villemoes
2014-12-04 16:53 ` Tejun Heo
2014-12-05 22:41 ` [PATCH v2] " Rasmus Villemoes
2015-01-05 14:13   ` Tejun Heo
2015-01-05 16:14     ` Greg Kroah-Hartman

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.