Bug#453903: ghostscript BusError cause

2008-05-12 Thread Pierre Habouzit
On Sun, May 11, 2008 at 09:06:19AM +, Bernhard R. Link wrote:
 I've identified one cause of BusError on startup. (I'm not entirely sure
 it is the only one, but it definitely is the cause for the easily
 reproduceable one, and while I think I saw one with this worked around
 in the early tests, I cannot reproduce the other, so there might either
 be another issue left or I once forget to save or recompile).
 
 The cause is corruption at the following piece of code in igcref.c:715:
 
 715:ref rtemp;
 716:
 717:if_debug2('8',   [8]ref 0x%lx copied to 0x%lx\n,
 718:  (ulong) src, (ulong) dest);
 719:/* We can't just use ref_assign_inline, */
 720:/* because the source and destination */
 721:/* might overlap! */
 722:ref_assign_inline(rtemp, (ref *) src);
 723:r_clear_attrs(rtemp, l_mark);
 724:ref_assign_inline((ref *) dest, rtemp);
 
 As far as I understand, the problem is that gcc knows that two (ref*)
 pointers cannot possibly overlap, so it optimizes away the temporary
 variable (at least partially), causing corruption of the packed array.
 (Which then again causes bus errors in other code like the garbage 
 collection).
 
 Here is the assember generated by gcc with -O2:
 .loc 1 722 0
 ld  [%g3+4], %g2
 .loc 1 724 0
 st  %g2, [%i1+4]
 sth %g1, [%i1]
 .loc 1 722 0
 st  %g2, [%fp-20]
 lduh[%g3+2], %g1
 .loc 1 725 0
 add %g3, 8, %g3
 .loc 1 724 0
 sth %g1, [%i1+2]
 
 It first does part of the move from src to register, then from register to 
 dest,
 only then copying another part of src to register, clearing the bit and 
 storing
 that part to dest. As the clearing is done in the part with the lower address,
 it even copies the second part first, thus if compressing an array quite 
 likely
 overwriting a part it just reads after that.
 
 The attached patch fixes it for me, and I think it also is the correct 
 solution
 that will survive more inteligent compilers.

  If that's really the problem, and that you believe there are other in
the code, then the proper solution with GCC is to build gs with
-fno-strict-aliasing.

-- 
·O·  Pierre Habouzit
··O[EMAIL PROTECTED]
OOOhttp://www.madism.org


pgpktRerWcnsT.pgp
Description: PGP signature


Bug#453903: ghostscript BusError cause

2008-05-11 Thread Bernhard R. Link
I've identified one cause of BusError on startup. (I'm not entirely sure
it is the only one, but it definitely is the cause for the easily
reproduceable one, and while I think I saw one with this worked around
in the early tests, I cannot reproduce the other, so there might either
be another issue left or I once forget to save or recompile).

The cause is corruption at the following piece of code in igcref.c:715:

715:ref rtemp;
716:
717:if_debug2('8',   [8]ref 0x%lx copied to 0x%lx\n,
718:  (ulong) src, (ulong) dest);
719:/* We can't just use ref_assign_inline, */
720:/* because the source and destination */
721:/* might overlap! */
722:ref_assign_inline(rtemp, (ref *) src);
723:r_clear_attrs(rtemp, l_mark);
724:ref_assign_inline((ref *) dest, rtemp);

As far as I understand, the problem is that gcc knows that two (ref*)
pointers cannot possibly overlap, so it optimizes away the temporary
variable (at least partially), causing corruption of the packed array.
(Which then again causes bus errors in other code like the garbage collection).

Here is the assember generated by gcc with -O2:
.loc 1 722 0
ld  [%g3+4], %g2
.loc 1 724 0
st  %g2, [%i1+4]
sth %g1, [%i1]
.loc 1 722 0
st  %g2, [%fp-20]
lduh[%g3+2], %g1
.loc 1 725 0
add %g3, 8, %g3
.loc 1 724 0
sth %g1, [%i1+2]

It first does part of the move from src to register, then from register to dest,
only then copying another part of src to register, clearing the bit and storing
that part to dest. As the clearing is done in the part with the lower address,
it even copies the second part first, thus if compressing an array quite likely
overwriting a part it just reads after that.

The attached patch fixes it for me, and I think it also is the correct solution
that will survive more inteligent compilers.

Hochachtungsvoll,
Bernhard R. Link
--- src/igcref.c	2007-11-30 07:43:12.0 +0100
+++ src/igcref.c	2008-05-11 10:58:50.0 +0200
@@ -712,16 +712,17 @@
 	src++;
 	} else {		/* full-size ref */
 	if (r_has_attr((ref *) src, l_mark)) {
-		ref rtemp;
-
 		if_debug2('8',   [8]ref 0x%lx copied to 0x%lx\n,
 			  (ulong) src, (ulong) dest);
 		/* We can't just use ref_assign_inline, */
 		/* because the source and destination */
-		/* might overlap! */
-		ref_assign_inline(rtemp, (ref *) src);
-		r_clear_attrs(rtemp, l_mark);
-		ref_assign_inline((ref *) dest, rtemp);
+		/* might overlap! A temp variable does not */
+		/* help either as long as ref_assign* are used, */
+		/* as that needs casting to (ref*) which is not */
+		/* allowed to overlap so the compiler might */
+		/* (and sometimes actually does) reorder it */
+		memmove(dest, src, sizeof(ref));
+		r_clear_attrs((ref *)dest, l_mark);
 		src += packed_per_ref;
 		dest += packed_per_ref;
 	} else {		/* check for end of block */