* Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
[not found] <E1J3RD5-0006yU-00@gondolin.me.apana.org.au>
@ 2007-12-17 17:28 ` Linus Torvalds
2007-12-17 17:48 ` Al Viro
2007-12-17 17:55 ` Eric Dumazet
0 siblings, 2 replies; 9+ messages in thread
From: Linus Torvalds @ 2007-12-17 17:28 UTC (permalink / raw)
To: Herbert Xu
Cc: John Reiser, Andrew Morton, security, tytso,
Linux Kernel Mailing List, mpm, linux-sparse
On Sat, 15 Dec 2007, Herbert Xu wrote:
>
> There ought to be a warning about this sort of thing.
We could add it to sparse. The appended (untested) patch seems to say
there's a lot of those signed divides-by-power-of-twos.
However, the problem with such warnings is that it encourages people to do
the simple fix that may be *wrong*. For example, you fixed it with patches
like
> - int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
> + int rsvd = r->limit ? 0 : random_read_wakeup_thresh / 4u;
which is really quite dangerous for several reasons:
- it depends intimately on the type of the thing being divided (try it:
it will do nothing at all if the thing you divide is larger than
"unsigned int", since then the "4u" will be turned into a _signed_
larger type by the C type expansion).
So in general, the above doesn't even do what it's supposed to do on a
64-bit architecture if the thing to be divided is 64-bit!
- it changes behaviour. If that thing really is signed and can be
negative, that "trivial" patch just changed the divide to be
fundamentally something totally different.
so I think this patch is horribly wrong.
The *correct* way to fix signed divisions is by doing one of two things:
- really make the data we divide be unsigned. With all the thinking that
involves!
This is the good change, but it does involve making sure that there are
no tests against zero and that the value really cannot go negative.
Usually the unsigned types are (a) faster and (b) more robust, but if
somebody is depending on signs, unsigned types are obviously not
appropriate.
- change a divide-by-power-of-2 into a signed shift instead.
Yes, this also changes the end result for negative values, but it
changes it in a sane and generally good way (ie it will still be a
"valid" divide, it will just be a divide that rounds differently, and
is more likely than turning it into an unsigned divide to generally
result in working code).
Hmm?
Linus
---
simplify.c | 30 ++++++++++++++++++++++++++++++
1 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/simplify.c b/simplify.c
index 94e14d2..91f1120 100644
--- a/simplify.c
+++ b/simplify.c
@@ -286,6 +286,36 @@ static int simplify_constant_rightside(struct instruction *insn)
if (!value)
return replace_with_pseudo(insn, insn->src2);
return 0;
+
+ case OP_DIVU: case OP_DIVS:
+ if (!value)
+ break;
+ if (value == 1)
+ return replace_with_pseudo(insn, insn->src1);
+ /* Power-of-two? */
+ if (!(value & (value-1))) {
+ int log2 = -1;
+ do {
+ log2++;
+ value >>= 1;
+ } while (value);
+
+ /* Turn unsigned divides into shifts */
+ if (insn->opcode == OP_DIVU) {
+ insn->src2->value = log2;
+ insn->opcode = OP_LSR;
+ return 1;
+ }
+
+ /*
+ * This is incorrect, but we care more about
+ * the warning than the code generation
+ */
+ warning(insn->pos, "expensive signed divide");
+ insn->src2->value = log2;
+ insn->opcode = OP_ASR;
+ return 1;
+ }
}
return 0;
}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 17:28 ` Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data) Linus Torvalds
@ 2007-12-17 17:48 ` Al Viro
2007-12-17 17:55 ` Eric Dumazet
1 sibling, 0 replies; 9+ messages in thread
From: Al Viro @ 2007-12-17 17:48 UTC (permalink / raw)
To: Linus Torvalds
Cc: Herbert Xu, John Reiser, Andrew Morton, security, tytso,
Linux Kernel Mailing List, mpm, linux-sparse
On Mon, Dec 17, 2007 at 09:28:57AM -0800, Linus Torvalds wrote:
>
>
> On Sat, 15 Dec 2007, Herbert Xu wrote:
> >
> > There ought to be a warning about this sort of thing.
>
> We could add it to sparse. The appended (untested) patch seems to say
> there's a lot of those signed divides-by-power-of-twos.
I'm not sure that you are warning about the right things. If you want
a real nightmare scenario in that area, consider this:
int x[20];
int *p = x + n;
int *q = x + m;
p - q
((char *)p - (char *)q)/4
((char *)p - (char *)q)/sizeof(int)
The first two are equivalent on all targets we care about. However, an
attempt to make the second one "more portable" silently creates the
code that'll do something entirely different as soon as we get m > n...
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 17:28 ` Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data) Linus Torvalds
2007-12-17 17:48 ` Al Viro
@ 2007-12-17 17:55 ` Eric Dumazet
2007-12-17 18:05 ` Ray Lee
` (2 more replies)
1 sibling, 3 replies; 9+ messages in thread
From: Eric Dumazet @ 2007-12-17 17:55 UTC (permalink / raw)
To: Linus Torvalds
Cc: Herbert Xu, John Reiser, Andrew Morton, security, tytso,
Linux Kernel Mailing List, mpm, linux-sparse
On Mon, 17 Dec 2007 09:28:57 -0800 (PST)
Linus Torvalds <torvalds@linux-foundation.org> wrote:
>
>
> On Sat, 15 Dec 2007, Herbert Xu wrote:
> >
> > There ought to be a warning about this sort of thing.
>
> We could add it to sparse. The appended (untested) patch seems to say
> there's a lot of those signed divides-by-power-of-twos.
>
> However, the problem with such warnings is that it encourages people to do
> the simple fix that may be *wrong*. For example, you fixed it with patches
> like
>
> > - int rsvd = r->limit ? 0 : random_read_wakeup_thresh/4;
> > + int rsvd = r->limit ? 0 : random_read_wakeup_thresh / 4u;
>
> which is really quite dangerous for several reasons:
>
> - it depends intimately on the type of the thing being divided (try it:
> it will do nothing at all if the thing you divide is larger than
> "unsigned int", since then the "4u" will be turned into a _signed_
> larger type by the C type expansion).
>
I was looking at lib/extable.c which does emit a signed divide on i386 but not on x86_64:
mid = (last - first) / 2 + first;
So I tried to compiled this on x86_64 :
long *mid(long *a, long *b)
{
return ((a - b) / 2 + a);
}
It gave :
mid:
movq %rdi, %rdx
subq %rsi, %rdx
sarq $3, %rdx
movq %rdx, %rax
shrq $63, %rax
addq %rdx, %rax
sarq %rax
leaq (%rdi,%rax,8), %rax
ret
while
long *mid(long *a, long *b)
{
return ((a - b) / 2u + a);
}
gave :
mid:
movq %rdi, %rdx
subq %rsi, %rdx
sarq $3, %rdx
movq %rdx, %rax
shrq $63, %rax
addq %rdx, %rax
sarq %rax
leaq (%rdi,%rax,8), %rax
ret
and while :
long *mid(long *a, long *b)
{
return (((unsigned long)(a - b)) / 2 + a);
}
gave :
mid:
movq %rdi, %rax
subq %rsi, %rax
sarq %rax
andq $-8, %rax
addq %rdi, %rax
ret
But I found this cast ugly so I cooked this patch.
[PATCH] Avoid signed arithmetics in search_extable()
On i386 and gcc-4.2.{1|2}, search_extable() currently does integer divides (by 2 !!!), while
we can certainly use a right shift. This looks more a typical bsearch() implementation.
Signed-off-by: Eric Dumazet <dada1@cosmosbay.com>
diff --git a/lib/extable.c b/lib/extable.c
index 463f456..03a81bd 100644
--- a/lib/extable.c
+++ b/lib/extable.c
@@ -54,20 +54,20 @@ search_extable(const struct exception_table_entry *first,
const struct exception_table_entry *last,
unsigned long value)
{
- while (first <= last) {
- const struct exception_table_entry *mid;
+ unsigned long mid, low = 0, high = (last - first);
- mid = (last - first) / 2 + first;
+ while (low <= high) {
+ mid = (low + high) / 2;
/*
* careful, the distance between entries can be
- * larger than 2GB:
+ * larger than MAX_LONG:
*/
- if (mid->insn < value)
- first = mid + 1;
- else if (mid->insn > value)
- last = mid - 1;
+ if (first[mid].insn < value)
+ low = mid + 1;
+ else if (first[mid].insn > value)
+ high = mid - 1;
else
- return mid;
+ return first + mid;
}
return NULL;
}
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 17:55 ` Eric Dumazet
@ 2007-12-17 18:05 ` Ray Lee
2007-12-17 18:10 ` Eric Dumazet
2007-12-17 18:23 ` Al Viro
2007-12-17 18:28 ` [Security] Signed divides vs shifts (Re: " Linus Torvalds
2 siblings, 1 reply; 9+ messages in thread
From: Ray Lee @ 2007-12-17 18:05 UTC (permalink / raw)
To: Eric Dumazet
Cc: Linus Torvalds, Herbert Xu, John Reiser, Andrew Morton, security,
tytso, Linux Kernel Mailing List, mpm, linux-sparse
On Dec 17, 2007 9:55 AM, Eric Dumazet <dada1@cosmosbay.com> wrote:
> - mid = (last - first) / 2 + first;
> + while (low <= high) {
> + mid = (low + high) / 2;
I think you just introduced a bug. Think about what happens if
low=high=MAX_LONG/2 + 1.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 18:05 ` Ray Lee
@ 2007-12-17 18:10 ` Eric Dumazet
2007-12-17 18:12 ` Ray Lee
0 siblings, 1 reply; 9+ messages in thread
From: Eric Dumazet @ 2007-12-17 18:10 UTC (permalink / raw)
To: Ray Lee
Cc: Linus Torvalds, Herbert Xu, John Reiser, Andrew Morton, security,
tytso, Linux Kernel Mailing List, mpm, linux-sparse
On Mon, 17 Dec 2007 10:05:35 -0800
"Ray Lee" <ray-lk@madrabbit.org> wrote:
> On Dec 17, 2007 9:55 AM, Eric Dumazet <dada1@cosmosbay.com> wrote:
> > - mid = (last - first) / 2 + first;
> > + while (low <= high) {
> > + mid = (low + high) / 2;
>
> I think you just introduced a bug. Think about what happens if
> low=high=MAX_LONG/2 + 1.
>
Fortunatly this is not possible :)
Hint : sizeof(struct exception_table_entry) is >= 8
so high is garanteed to be <= MAX_LONG/8
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 18:10 ` Eric Dumazet
@ 2007-12-17 18:12 ` Ray Lee
0 siblings, 0 replies; 9+ messages in thread
From: Ray Lee @ 2007-12-17 18:12 UTC (permalink / raw)
To: Eric Dumazet
Cc: Linus Torvalds, Herbert Xu, John Reiser, Andrew Morton, security,
tytso, Linux Kernel Mailing List, mpm, linux-sparse
On Dec 17, 2007 10:10 AM, Eric Dumazet <dada1@cosmosbay.com> wrote:
> On Mon, 17 Dec 2007 10:05:35 -0800
> "Ray Lee" <ray-lk@madrabbit.org> wrote:
>
> > On Dec 17, 2007 9:55 AM, Eric Dumazet <dada1@cosmosbay.com> wrote:
> > > - mid = (last - first) / 2 + first;
> > > + while (low <= high) {
> > > + mid = (low + high) / 2;
> >
> > I think you just introduced a bug. Think about what happens if
> > low=high=MAX_LONG/2 + 1.
> >
>
> Fortunatly this is not possible :)
>
> Hint : sizeof(struct exception_table_entry) is >= 8
>
> so high is garanteed to be <= MAX_LONG/8
Ah, my bad. One of these days I'll learn to not post before coffee.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 17:55 ` Eric Dumazet
2007-12-17 18:05 ` Ray Lee
@ 2007-12-17 18:23 ` Al Viro
2007-12-17 18:28 ` [Security] Signed divides vs shifts (Re: " Linus Torvalds
2 siblings, 0 replies; 9+ messages in thread
From: Al Viro @ 2007-12-17 18:23 UTC (permalink / raw)
To: Eric Dumazet
Cc: Linus Torvalds, Herbert Xu, John Reiser, Andrew Morton, security,
tytso, Linux Kernel Mailing List, mpm, linux-sparse
On Mon, Dec 17, 2007 at 06:55:57PM +0100, Eric Dumazet wrote:
> long *mid(long *a, long *b)
> {
> return ((a - b) / 2 + a);
> }
... is not actually a middle (you'd want b-a, not a-b there), but anyway
> It gave :
> mid:
> movq %rdi, %rdx
> subq %rsi, %rdx
> sarq $3, %rdx
> movq %rdx, %rax
> shrq $63, %rax
> addq %rdx, %rax
> sarq %rax
> leaq (%rdi,%rax,8), %rax
> ret
>
> while
>
> long *mid(long *a, long *b)
> {
> return ((a - b) / 2u + a);
> }
... undefined behaviour if a < b
> and while :
>
> long *mid(long *a, long *b)
> {
> return (((unsigned long)(a - b)) / 2 + a);
> }
undefined behaviour, again.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Security] Signed divides vs shifts (Re: /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 17:55 ` Eric Dumazet
2007-12-17 18:05 ` Ray Lee
2007-12-17 18:23 ` Al Viro
@ 2007-12-17 18:28 ` Linus Torvalds
2007-12-17 19:08 ` Al Viro
2 siblings, 1 reply; 9+ messages in thread
From: Linus Torvalds @ 2007-12-17 18:28 UTC (permalink / raw)
To: Eric Dumazet
Cc: security, tytso, Herbert Xu, John Reiser,
Linux Kernel Mailing List, linux-sparse, mpm, Andrew Morton
On Mon, 17 Dec 2007, Eric Dumazet wrote:
>
> while
>
> long *mid(long *a, long *b)
> {
> return ((a - b) / 2u + a);
> }
This is exactly what I'm talking about. That "2u" is TOTALLY POINTLESS.
It's an "unsigned int", but since (a-b) will be of type ptrdiff_t, and is
*wider* on a 64-bit architecture (it's the same as "long" on x86-64), then
the 2u will just be converted to "long", and be signed again!
So you thought that you did an unsigned divide, but you did no such thing.
If you change the "2u" to a "2ul", it works again, and you get
mid:
movq %rdi, %rax
subq %rsi, %rax
sarq %rax
andq $-8, %rax
addq %rdi, %rax
ret
which is the code you wanted. But quite frankly, you could just have
written it with a shift to start with, and avoided the subtle type issue,
although gcc then generates
movq %rdi, %rax
subq %rsi, %rax
sarq $4, %rax
leaq (%rdi,%rax,8), %rax
ret
instead. Of course, this all *does* still have subtle sign issues, because
the "a-b" part implies a signed divide in itself, which is why you see
that "sarq" in he first place (rather than a "shrq").
Signed divides are hard. The "a-b" pointer subtraction is actually cheaper
than a general signed divide by sizeof, since the compiler can then assume
that the two pointers are mutually aligned, which is why gcc can generate
just a single "sarq" instead of having to do an extra "add negative bit"
thing to get the rounding right.
[ So Al, when you said that
(a-b)
is equivalent to
((char *)a-(char *)b)/4
for a "int *" a and b, you're right in the sense that the *result* is
the same, but the code generation likely isn't. The "a-b" thing can (and
does) allow the compiler to avoid the whole "align up for signed
numbers" thing, and the difference in code generation is clear:
subq %rsi, %rdi
sarq $2, %rdi
vs
subq %rsi, %rdi
leaq 3(%rdi), %rax
testq %rdi, %rdi
cmovs %rax, %rdi
sarq $2, %rdi
exactly because the first case *knows* that the low two bits have to be
zero, and thus there is no rounding issue. ]
Linus
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Security] Signed divides vs shifts (Re: /dev/urandom uses uninit bytes, leaks user data)
2007-12-17 18:28 ` [Security] Signed divides vs shifts (Re: " Linus Torvalds
@ 2007-12-17 19:08 ` Al Viro
0 siblings, 0 replies; 9+ messages in thread
From: Al Viro @ 2007-12-17 19:08 UTC (permalink / raw)
To: Linus Torvalds
Cc: Eric Dumazet, security, tytso, Herbert Xu, John Reiser,
Linux Kernel Mailing List, linux-sparse, mpm, Andrew Morton
On Mon, Dec 17, 2007 at 10:28:38AM -0800, Linus Torvalds wrote:
> [ So Al, when you said that
>
> (a-b)
>
> is equivalent to
>
> ((char *)a-(char *)b)/4
>
> for a "int *" a and b, you're right in the sense that the *result* is
> the same, but the code generation likely isn't. The "a-b" thing can (and
Sure. And yes, I very much do prefer code that uses C as it ought to be
used and doesn't play games with casts from hell, etc. For a lot of reasons,
both correctness- and efficiency-related.
We _do_ have such turds. In spades. And such places are potential timebombs,
since well-intentioned idiotic patch ("I've read in lecture notes that sizeof
is better than explicit constant, so replacement surely can only improve the
things and the best part is, I don't need to understand what I'm doing")
turns an ugly FPOS into equally ugly FPOS that silently doesn't work ;-/
[sorry about the rant, I'm about 3/4 through the drivers/net colonoscopy,
with >300Kb of patches and a pile of assorted bugs so far - and then there's
drivers/scsi to deal with. Endianness stuff, mostly...]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2007-12-17 19:08 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <E1J3RD5-0006yU-00@gondolin.me.apana.org.au>
2007-12-17 17:28 ` Signed divides vs shifts (Re: [Security] /dev/urandom uses uninit bytes, leaks user data) Linus Torvalds
2007-12-17 17:48 ` Al Viro
2007-12-17 17:55 ` Eric Dumazet
2007-12-17 18:05 ` Ray Lee
2007-12-17 18:10 ` Eric Dumazet
2007-12-17 18:12 ` Ray Lee
2007-12-17 18:23 ` Al Viro
2007-12-17 18:28 ` [Security] Signed divides vs shifts (Re: " Linus Torvalds
2007-12-17 19:08 ` Al Viro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).