Source: 4ti2
Version: 1.6.15+ds-1
Severity: important
Tags: patch upstream
Forwarded: https://github.com/4ti2/4ti2/pull/61

Dear Maintainer,

4ti2 fails its build-time test suite (test/ppi/check_ppi) on ppc64el when built 
with GCC 15/16 at -O2 or -O3 (including Ubuntu's default -O3 on ppc64el, 
tracked in https://bugs.launchpad.net/ubuntu/+source/4ti2/+bug/2170794).

In src/ppi/ppi.cpp, struct VectorAux declares a fixed-size array `signed char 
Stuff[4]`, stores the vector length in `Stuff[0]`, and returns `Stuff + 1` from 
`Data()`. Accessing `v(i)` for `i >= 4` indexes `Stuff[i]` past the 4-byte 
array bound.

In `ExtendPPI()`, the loop:

  for (int p = 1; p <= (n+1)/2; p++) {
    v(p)--, v(n+1-p)--;
    ...
  }

decrements both `v(p)` and `v(n+1-p)`. Because `p` starts within `Stuff[1..3]` 
while `n+1-p` exceeds `Stuff[3]` for `n >= 3`, GCC alias analysis on ppc64el 
concludes that the two accesses cannot alias and schedules the load `lbzu` of 
`v(n+1-p)` before the store `stb` of `v(p)`. When `n + 1` is even and `p == n + 
1 - p` (first hit at `n = 3, p = 2`), `v(2)` is decremented from 0 to -1 
instead of -2, producing the invalid identity `0 -1 0 1` and causing `4ti2-ppi 
5` to output 50 vectors instead of 47.

The attached patch replaces `Stuff[4]` in `VectorAux` with an explicit 
`unsigned char len_` field and a `signed char data_[]` flexible array member. 
Verified on ppc64el at -O3 -flto=auto across ppi5 (47 vectors), ppi10 (2465 
vectors), and ppi16 (99328 vectors).

Thanks,
Viktor Pashaiev
diff --git a/src/ppi/ppi.cpp b/src/ppi/ppi.cpp
index e42f4ae..da9bb12 100644
--- a/src/ppi/ppi.cpp
+++ b/src/ppi/ppi.cpp
@@ -82,21 +82,21 @@ typedef unsigned int attr_type; /* 32 bits are enough */
 struct VectorAux {
   /* order important */
   attr_type Attribute;
-  signed char Stuff[4];
+  unsigned char len_;
+  signed char data_[];
   void *operator new(size_t s, int length) {
-    if (length < 3) length=3;
-    return malloc(s - 3 +length);
+    return malloc(s + length);
   }
   void operator delete(void *v) {
     free(v);
   }
   VectorAux(int length) { Length() = length; }
   VectorAux(const VectorAux &aux) {
-    memcpy(this, &aux, sizeof(VectorAux) -4 + 1 + aux.Length());
+    memcpy(this, &aux, sizeof(VectorAux) + aux.Length());
   }
-  inline unsigned char &Length() { return (unsigned char &) Stuff[0]; }
-  inline int Length() const { return (unsigned char) Stuff[0]; }
-  inline signed char *Data() { return Stuff+1; }
+  inline unsigned char &Length() { return len_; }
+  inline int Length() const { return len_; }
+  inline signed char *Data() { return data_; }
 };
 
 class Vector {
@@ -118,7 +118,7 @@ public:
       if (aux == NULL) 
 	aux = new(v.aux->Length()) VectorAux(*v.aux);
       else if (aux->Length() == v.aux->Length())
-	memcpy(aux, v.aux, sizeof(VectorAux) -4 + 1 + v.aux->Length());
+	memcpy(aux, v.aux, sizeof(VectorAux) + v.aux->Length());
       else {
 	delete aux;
 	aux = new(v.aux->Length()) VectorAux(*v.aux);

Reply via email to