All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] kcmp: Fix standard comparison bug
@ 2014-09-04 10:40 Rasmus Villemoes
  2014-09-04 13:29 ` Cyrill Gorcunov
  2014-09-05 17:50 ` Cyrill Gorcunov
  0 siblings, 2 replies; 4+ messages in thread
From: Rasmus Villemoes @ 2014-09-04 10:40 UTC (permalink / raw)
  To: Andrew Morton, Eric W. Biederman, Cyrill Gorcunov
  Cc: linux-kernel, Rasmus Villemoes, stable

The C operator <= defines a perfectly fine total ordering on the set
of values representable in a long. However, unlike its namesake in the
integers, it is not translation invariant, meaning that we do not have
"b <= c" iff "a+b <= a+c" for all a,b,c.

This means that it is always wrong to try to boil down the
relationship between two longs to a question about the sign of their
difference, because the resulting relation [a LEQ b iff a-b <= 0] is
neither anti-symmetric or transitive. The former is due to
-LONG_MIN==LONG_MIN (take any two a,b with a-b = LONG_MIN; then a LEQ
b and b LEQ a, but a != b). The latter can either be seen observing
that x LEQ x+1 for all x, implying x LEQ x+1 LEQ x+2 ... LEQ x-1 LEQ
x; or more directly with the simple example a=LONG_MIN, b=0, c=1, for
which a-b < 0, b-c < 0, but a-c > 0.

Note that it makes absolutely no difference that a transmogrying
bijection has been applied before the comparison is done. In fact, had
the obfuscation not been done, one could probably not observe the bug
(assuming all values being compared always lie in one half of the
address space, the mathematical value of a-b is always representable
in a long). As it stands, one can easily obtain three file descriptors
exhibiting the non-transitivity of kcmp().

Cc: <stable@vger.kernel.org>
Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
---

Side note 1: I can't see that ensuring the MSB of the multiplier is
set serves any purpose other than obfuscating the obfuscating code.

Side note 2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <sys/syscall.h>

enum kcmp_type {
        KCMP_FILE,
        KCMP_VM,
        KCMP_FILES,
        KCMP_FS,
        KCMP_SIGHAND,
        KCMP_IO,
        KCMP_SYSVSEM,

        KCMP_TYPES,
};

pid_t pid;

int kcmp(pid_t pid1, pid_t pid2, int type,
	 unsigned long idx1, unsigned long idx2)
{
	return syscall(SYS_kcmp, pid1, pid2, type, idx1, idx2);
}

int cmp_fd(int fd1, int fd2)
{
	int c = kcmp(pid, pid, KCMP_FILE, fd1, fd2);
	if (c < 0) {
		perror("kcmp");
		exit(1);
	}
	assert(0 <= c && c < 3);
	return c;
}

int cmp_fdp(const void *a, const void *b)
{
	static const int normalize[] = {0, -1, 1};
	return normalize[cmp_fd(*(int*)a, *(int*)b)];
}

#define MAX 100 /* This is plenty; I've seen it trigger for MAX==3 */
int main(int argc, char *argv[])
{
	int r, s, count = 0;
	int REL[3] = {0,0,0};
	int fd[MAX];
	pid = getpid();

	while (count < MAX) {
		r = open("/dev/null", O_RDONLY);
		if (r < 0)
			break;
		fd[count++] = r;
	}
	printf("opened %d file descriptors\n", count);

	for (r = 0; r < count; ++r) {
		for (s = r+1; s < count; ++s) {
			REL[cmp_fd(fd[r], fd[s])]++;
		}
	}
	printf("== %d\t< %d\t> %d\n", REL[0], REL[1], REL[2]);

	qsort(fd, count, sizeof(fd[0]), cmp_fdp);
	memset(REL, 0, sizeof(REL));

	for (r = 0; r < count; ++r) {
		for (s = r+1; s < count; ++s) {
			REL[cmp_fd(fd[r], fd[s])]++;
		}
	}
	printf("== %d\t< %d\t> %d\n", REL[0], REL[1], REL[2]);

	return (REL[0] + REL[2] != 0);
}




 kernel/kcmp.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/kernel/kcmp.c b/kernel/kcmp.c
index e30ac0f..0aa69ea 100644
--- a/kernel/kcmp.c
+++ b/kernel/kcmp.c
@@ -44,11 +44,12 @@ static long kptr_obfuscate(long v, int type)
  */
 static int kcmp_ptr(void *v1, void *v2, enum kcmp_type type)
 {
-	long ret;
+	long t1, t2;
 
-	ret = kptr_obfuscate((long)v1, type) - kptr_obfuscate((long)v2, type);
+	t1 = kptr_obfuscate((long)v1, type);
+	t2 = kptr_obfuscate((long)v2, type);
 
-	return (ret < 0) | ((ret > 0) << 1);
+	return (t1 < t2) | ((t1 > t2) << 1);
 }
 
 /* The caller must have pinned the task */
-- 
2.0.4


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

* Re: [PATCH] kcmp: Fix standard comparison bug
  2014-09-04 10:40 [PATCH] kcmp: Fix standard comparison bug Rasmus Villemoes
@ 2014-09-04 13:29 ` Cyrill Gorcunov
  2014-09-04 15:35   ` Rasmus Villemoes
  2014-09-05 17:50 ` Cyrill Gorcunov
  1 sibling, 1 reply; 4+ messages in thread
From: Cyrill Gorcunov @ 2014-09-04 13:29 UTC (permalink / raw)
  To: Rasmus Villemoes
  Cc: Andrew Morton, Eric W. Biederman, linux-kernel, stable,
	H. Peter Anvin, Pavel Emelyanov

On Thu, Sep 04, 2014 at 12:40:06PM +0200, Rasmus Villemoes wrote:
> The C operator <= defines a perfectly fine total ordering on the set
> of values representable in a long. However, unlike its namesake in the
> integers, it is not translation invariant, meaning that we do not have
> "b <= c" iff "a+b <= a+c" for all a,b,c.
> 
> This means that it is always wrong to try to boil down the
> relationship between two longs to a question about the sign of their
> difference, because the resulting relation [a LEQ b iff a-b <= 0] is
> neither anti-symmetric or transitive. The former is due to
> -LONG_MIN==LONG_MIN (take any two a,b with a-b = LONG_MIN; then a LEQ
> b and b LEQ a, but a != b). The latter can either be seen observing
> that x LEQ x+1 for all x, implying x LEQ x+1 LEQ x+2 ... LEQ x-1 LEQ
> x; or more directly with the simple example a=LONG_MIN, b=0, c=1, for
> which a-b < 0, b-c < 0, but a-c > 0.
> 
> Note that it makes absolutely no difference that a transmogrying
> bijection has been applied before the comparison is done. In fact, had
> the obfuscation not been done, one could probably not observe the bug
> (assuming all values being compared always lie in one half of the
> address space, the mathematical value of a-b is always representable
> in a long). As it stands, one can easily obtain three file descriptors
> exhibiting the non-transitivity of kcmp().
> 
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>

It's more than that, I looked into disassembly code and
found that compiler overoptimize obfuscation, instead of doing
^ and * sequently it substracts xor production of both operands
first and only then multiplies the result.

Lets stick with the fix.

Reviewed-by: Cyrill Gorcunov <gorcunov@openvz.org>

I'll take more precise look at evening, thanks!

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

* Re: [PATCH] kcmp: Fix standard comparison bug
  2014-09-04 13:29 ` Cyrill Gorcunov
@ 2014-09-04 15:35   ` Rasmus Villemoes
  0 siblings, 0 replies; 4+ messages in thread
From: Rasmus Villemoes @ 2014-09-04 15:35 UTC (permalink / raw)
  To: Cyrill Gorcunov
  Cc: Andrew Morton, Eric W. Biederman, linux-kernel, stable,
	H. Peter Anvin, Pavel Emelyanov

On Thu, Sep 04 2014, Cyrill Gorcunov <gorcunov@gmail.com> wrote:

> On Thu, Sep 04, 2014 at 12:40:06PM +0200, Rasmus Villemoes wrote:
>> 
>> Note that it makes absolutely no difference that a transmogrying
                                                       ^^^^
                                                    transmogrifying

> It's more than that, I looked into disassembly code and
> found that compiler overoptimize obfuscation, instead of doing
> ^ and * sequently it substracts xor production of both operands
> first and only then multiplies the result.

...which it seems to be perfectly entitled to; xz - yz == (x-y)z holds
in any ring. 

Anyway, I'll stop beating the dead horse.

Rasmus

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

* Re: [PATCH] kcmp: Fix standard comparison bug
  2014-09-04 10:40 [PATCH] kcmp: Fix standard comparison bug Rasmus Villemoes
  2014-09-04 13:29 ` Cyrill Gorcunov
@ 2014-09-05 17:50 ` Cyrill Gorcunov
  1 sibling, 0 replies; 4+ messages in thread
From: Cyrill Gorcunov @ 2014-09-05 17:50 UTC (permalink / raw)
  To: Rasmus Villemoes, Andrew Morton; +Cc: Eric W. Biederman, linux-kernel, stable

On Thu, Sep 04, 2014 at 12:40:06PM +0200, Rasmus Villemoes wrote:
> The C operator <= defines a perfectly fine total ordering on the set
> of values representable in a long. However, unlike its namesake in the
> integers, it is not translation invariant, meaning that we do not have
> "b <= c" iff "a+b <= a+c" for all a,b,c.
> 
> This means that it is always wrong to try to boil down the
> relationship between two longs to a question about the sign of their
> difference, because the resulting relation [a LEQ b iff a-b <= 0] is
> neither anti-symmetric or transitive. The former is due to
> -LONG_MIN==LONG_MIN (take any two a,b with a-b = LONG_MIN; then a LEQ
> b and b LEQ a, but a != b). The latter can either be seen observing
> that x LEQ x+1 for all x, implying x LEQ x+1 LEQ x+2 ... LEQ x-1 LEQ
> x; or more directly with the simple example a=LONG_MIN, b=0, c=1, for
> which a-b < 0, b-c < 0, but a-c > 0.
> 
> Note that it makes absolutely no difference that a transmogrying
> bijection has been applied before the comparison is done. In fact, had
> the obfuscation not been done, one could probably not observe the bug
> (assuming all values being compared always lie in one half of the
> address space, the mathematical value of a-b is always representable
> in a long). As it stands, one can easily obtain three file descriptors
> exhibiting the non-transitivity of kcmp().
> 
> Cc: <stable@vger.kernel.org>
> Signed-off-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>

Just noted that Andrew's email address has been screwed for some reason,
fixed. Guys, pick it up please, and many thanks to Rasmus for finding it.

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

end of thread, other threads:[~2014-09-05 17:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-04 10:40 [PATCH] kcmp: Fix standard comparison bug Rasmus Villemoes
2014-09-04 13:29 ` Cyrill Gorcunov
2014-09-04 15:35   ` Rasmus Villemoes
2014-09-05 17:50 ` Cyrill Gorcunov

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.