[Maria-developers] Patch for unaligned word access in CONNECT storage engine
Who should be contacted about issues in the CONNECT storage engine? The attached patch is from Debian Bug#838914 https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=838914 Apparently the code does direct unaligned accesses of word data. This works fine on the x86 architecture, but on some other architectures (like MIPS), it causes a bus error. In other places in the server code, the similar issue is handled correctly with uint4korr() and similar macros (though these also deal with byte order). I think the patch (or something similar) is good and should be upstreamed. But I am not sure how the CONNECT storage engine is maintained - should this go directly into MariaDB? If there is an upstream maintained CONNECT storage engine, probably it should preferably go there first? - Kristian.g
Hi, Kristian! Yes, it is maintained, I've forwarded your email to the maintainer. On Oct 21, Kristian Nielsen wrote:
Sender: Maria-developers
From: Kristian Nielsen To: MariaDB Developers Subject: [Maria-developers] Patch for unaligned word access in CONNECT storage engine Date: Fri, 21 Oct 2016 23:16:45 +0200 List-Archive: http://lists.launchpad.net/maria-developers Who should be contacted about issues in the CONNECT storage engine?
The attached patch is from Debian Bug#838914
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=838914
Apparently the code does direct unaligned accesses of word data. This works fine on the x86 architecture, but on some other architectures (like MIPS), it causes a bus error.
In other places in the server code, the similar issue is handled correctly with uint4korr() and similar macros (though these also deal with byte order).
I think the patch (or something similar) is good and should be upstreamed. But I am not sure how the CONNECT storage engine is maintained - should this go directly into MariaDB? If there is an upstream maintained CONNECT storage engine, probably it should preferably go there first?
- Kristian.g
Regards, Sergei Chief Architect MariaDB and security@mariadb.org
Hi, Kristian! On Oct 22, Sergei Golubchik wrote:
Hi, Kristian!
Yes, it is maintained, I've forwarded your email to the maintainer.
And that's the reply I got: ===================== Hi, Sergei, Le 23/10/2016 à 00:32, Sergei Golubchik a écrit :
Hi, Olivier! On Oct 23, Olivier Bertrand wrote:
Hi Sergei,
Is this just a sort of precaution or did bus errors effectively occured?
I cannot say for sure, I've just forwarded an email from the maria-developers@ mailing list. And in that email Kristian Nielsen forwarded a patch that Debian applies to Connect engine...
But I'm almost certain that the bus error did occur. First, because Debian packages only fix issues that fail in Debian - on the test machines or as reported by users. They don't do precautions. And second, because this kind of crash is farily common on some architectures.
What is strange is that seems to occur only now. CONNECT exists for at least four years and the code of VALBLK was still existing at that time. This is why I'd like to know exactly when crashes occur and under what circumstances.
My question is because all (sub)allocations made by connect are always done on a memory whose address is a multiple of 8. This means that in a value block that is aligned on a multiple of 8 location, big integer and doubles should be correctly aligned, integers are aligned on a multiple of 4, smallint of 2 etc.
Is this enough? If not, the patch is required but if it is ok the patch is useless.
Apparently, there were crashes, the email mentions https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=838914 and the bug report talks about crashes on MIPS.
Also, if the patch is required, because it can have a performance impact when dealing with big tables, it should be made conditional and applied only when necassary. Something like:
#if !patch_requied #define UnalignedRead(N) Typp[N] #define UnalignedWrite(N,V) Typp[N] = V #endif
Remains to find how to set the patch_required variable.
Three thoughts here.
1. first, I'd check the assembly on x86 - it's a memcpy with a constant and small (2-4-8 bytes) size. There's a good chance that a compiler can optimize it.
2. if you say that all memory accesses should be aligned, you can add asserts to these UnalignedRead/UnalignedWrite macros - perhaps the access is unintentionally unaligned somewhere and you might want to fix it?
Here we go to the interesting part. Firstly I checked out what were the rules of alignment in mips (and risc?) processor and, according to a web site, it seems to be: Alignment is important for a MIPS processor, it only likes to read multi-byte values from memory at an address that's a multiple of the data size. This means that a memory block that will contain an array of multi-byte values must be aligned at an address multiple of the byte number. I checked in CONNECT source, VALBLK blocks are allocated by PlgDBalloc in plgdbutl.cpp line 1206 or PlgDBrealloc line 1268. And indeed, depending on the block size, they can be suballocated or normally allocated by malloc or realloc. As I said, if they are suballocated there should be no problem (except the entire sarea is not aligned but this would cause unalignment errors for all suballocations) However, I don't know how malloc and realloc work on mips processor. These blocks are declared as pointed by a void* pointer. Perhaps this does not make them to make an allocation on a properly aligned memory. In any case, this is the place to check and, if a patch is necessary, this is the place to place it. Note that it would be usefull for all processors because even if no crash occur, wrongly aligned blocks will cause a performance penalty, chiefly because they are big blocks. In any case, the proposed patch is inappropriate and should not be done. However, if we discover that malloc always returns a memory address that is a mutiple of eight, this shows that the problem is elsewhere and would show that the proposed patch is useless. Regards, Olivier
3. And, the last - you use use __mips__ for your patch_required to detect MIPS and add further architectures later in a similar way, if the need arises.
Le 22/10/2016 à 12:28, Sergei Golubchik a écrit :
Bonjour, Olivier
This is just FYI, in case you didn't see the email below (I don't know if you're on that mailing list)
Regards, Sergei Chief Architect MariaDB and security@mariadb.org
----- Forwarded message from Kristian Nielsen
----- Sender: Maria-developers
From: Kristian Nielsen To: MariaDB Developers Subject: [Maria-developers] Patch for unaligned word access in CONNECT storage engine Date: Fri, 21 Oct 2016 23:16:45 +0200 List-Archive: http://lists.launchpad.net/maria-developers Who should be contacted about issues in the CONNECT storage engine?
The attached patch is from Debian Bug#838914
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=838914
Apparently the code does direct unaligned accesses of word data. This works fine on the x86 architecture, but on some other architectures (like MIPS), it causes a bus error.
In other places in the server code, the similar issue is handled correctly with uint4korr() and similar macros (though these also deal with byte order).
I think the patch (or something similar) is good and should be upstreamed. But I am not sure how the CONNECT storage engine is maintained - should this go directly into MariaDB? If there is an upstream maintained CONNECT storage engine, probably it should preferably go there first?
- Kristian.g
Description: Handle unaligned buffers in connect's TYPBLK class On MIPS platforms (and probably others) unaligned memory access results in a bus error. In the connect storage engine, block data for some data formats is stored packed in memory and the TYPBLK class is used to read values from it. Since TYPBLK does not have special handling for this packed memory, it can quite easily result in unaligned memory accesses. . The simple way to fix this is to perform all accesses to the main buffer through memcpy. With GCC and optimizations turned on, this call to memcpy is completely optimized away on architectures where unaligned accesses are ok (like x86). Author: James Cowgill
--- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ Index: mariadb-10.0-10.0.27/storage/connect/valblk.h =================================================================== --- mariadb-10.0-10.0.27.orig/storage/connect/valblk.h +++ mariadb-10.0-10.0.27/storage/connect/valblk.h @@ -139,6 +139,7 @@ class VALBLK : public BLOCK { int Prec; // Precision of float values }; // end of class VALBLK + /***********************************************************************/ /* Class TYPBLK: represents a block of typed values. */ /***********************************************************************/ @@ -151,40 +152,40 @@ class TYPBLK : public VALBLK { // Implementation virtual bool Init(PGLOBAL g, bool check); virtual int GetVlen(void) {return sizeof(TYPE);} - virtual char GetTinyValue(int n) {return (char)Typp[n];} - virtual uchar GetUTinyValue(int n) {return (uchar)Typp[n];} - virtual short GetShortValue(int n) {return (short)Typp[n];} - virtual ushort GetUShortValue(int n) {return (ushort)Typp[n];} - virtual int GetIntValue(int n) {return (int)Typp[n];} - virtual uint GetUIntValue(int n) {return (uint)Typp[n];} - virtual longlong GetBigintValue(int n) {return (longlong)Typp[n];} - virtual ulonglong GetUBigintValue(int n) {return (ulonglong)Typp[n];} - virtual double GetFloatValue(int n) {return (double)Typp[n];} + virtual char GetTinyValue(int n) {return (char)UnalignedRead(n);} + virtual uchar GetUTinyValue(int n) {return (uchar)UnalignedRead(n);} + virtual short GetShortValue(int n) {return (short)UnalignedRead(n);} + virtual ushort GetUShortValue(int n) {return (ushort)UnalignedRead(n);} + virtual int GetIntValue(int n) {return (int)UnalignedRead(n);} + virtual uint GetUIntValue(int n) {return (uint)UnalignedRead(n);} + virtual longlong GetBigintValue(int n) {return (longlong)UnalignedRead(n);} + virtual ulonglong GetUBigintValue(int n) {return (ulonglong)UnalignedRead(n);} + virtual double GetFloatValue(int n) {return (double)UnalignedRead(n);} virtual char *GetCharString(char *p, int n); - virtual void Reset(int n) {Typp[n] = 0;} + virtual void Reset(int n) {UnalignedWrite(n, 0);}
// Methods using VALBLK::SetValue; virtual void SetValue(PSZ sp, int n); virtual void SetValue(char *sp, uint len, int n); virtual void SetValue(short sval, int n) - {Typp[n] = (TYPE)sval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)sval); SetNull(n, false);} virtual void SetValue(ushort sval, int n) - {Typp[n] = (TYPE)sval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)sval); SetNull(n, false);} virtual void SetValue(int lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(uint lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(longlong lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(ulonglong lval, int n) - {Typp[n] = (TYPE)lval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)lval); SetNull(n, false);} virtual void SetValue(double fval, int n) - {Typp[n] = (TYPE)fval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)fval); SetNull(n, false);} virtual void SetValue(char cval, int n) - {Typp[n] = (TYPE)cval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)cval); SetNull(n, false);} virtual void SetValue(uchar cval, int n) - {Typp[n] = (TYPE)cval; SetNull(n, false);} + {UnalignedWrite(n, (TYPE)cval); SetNull(n, false);} virtual void SetValue(PVAL valp, int n); virtual void SetValue(PVBLK pv, int n1, int n2); virtual void SetMin(PVAL valp, int n); @@ -206,6 +207,17 @@ class TYPBLK : public VALBLK { // Members TYPE* const &Typp; const char *Fmt; + + // Unaligned access + TYPE UnalignedRead(int n) const { + TYPE result; + memcpy(&result, Typp + n, sizeof(TYPE)); + return result; + } + + void UnalignedWrite(int n, TYPE value) { + memcpy(Typp + n, &value, sizeof(TYPE)); + } }; // end of class TYPBLK
/***********************************************************************/ Index: mariadb-10.0-10.0.27/storage/connect/valblk.cpp =================================================================== --- mariadb-10.0-10.0.27.orig/storage/connect/valblk.cpp +++ mariadb-10.0-10.0.27/storage/connect/valblk.cpp @@ -265,14 +265,14 @@ bool TYPBLK<TYPE>::Init(PGLOBAL g, bool template <class TYPE> char *TYPBLK<TYPE>::GetCharString(char *p, int n) { - sprintf(p, Fmt, Typp[n]); + sprintf(p, Fmt, UnalignedRead(n)); return p; } // end of GetCharString
template <> char *TYPBLK<double>::GetCharString(char *p, int n) { - sprintf(p, Fmt, Prec, Typp[n]); + sprintf(p, Fmt, Prec, UnalignedRead(n)); return p; } // end of GetCharString
@@ -288,7 +288,7 @@ void TYPBLK<TYPE>::SetValue(PVAL valp, i ChkTyp(valp);
if (!(b = valp->IsNull())) - Typp[n] = GetTypedValue(valp); + UnalignedWrite(n, GetTypedValue(valp)); else Reset(n);
@@ -350,9 +350,9 @@ void TYPBLK<TYPE>::SetValue(PSZ p, int n ulonglong val = CharToNumber(p, strlen(p), maxval, Unsigned, &minus);
if (minus && val < maxval) - Typp[n] = (TYPE)(-(signed)val); + UnalignedWrite(n, (TYPE)(-(signed)val)); else - Typp[n] = (TYPE)val; + UnalignedWrite(n, (TYPE)val);
SetNull(n, false); } // end of SetValue @@ -395,7 +395,7 @@ void TYPBLK<double>::SetValue(PSZ p, int longjmp(g->jumper[g->jump_level], Type); } // endif Check
- Typp[n] = atof(p); + UnalignedWrite(n, atof(p)); SetNull(n, false); } // end of SetValue
@@ -427,7 +427,7 @@ void TYPBLK<TYPE>::SetValue(PVBLK pv, in ChkTyp(pv);
if (!(b = pv->IsNull(n2) && Nullable)) - Typp[n1] = GetTypedValue(pv, n2); + UnalignedWrite(n1, GetTypedValue(pv, n2)); else Reset(n1);
@@ -478,10 +478,10 @@ void TYPBLK<TYPE>::SetMin(PVAL valp, int { CheckParms(valp, n) TYPE tval = GetTypedValue(valp); - TYPE& tmin = Typp[n]; + TYPE tmin = UnalignedRead(n);
if (tval < tmin) - tmin = tval; + UnalignedWrite(n, tval);
} // end of SetMin
@@ -493,10 +493,10 @@ void TYPBLK<TYPE>::SetMax(PVAL valp, int { CheckParms(valp, n) TYPE tval = GetTypedValue(valp); - TYPE& tmin = Typp[n]; + TYPE tmin = UnalignedRead(n);
if (tval > tmin) - tmin = tval; + UnalignedWrite(n, tval);
} // end of SetMax
@@ -522,7 +522,7 @@ void TYPBLK<TYPE>::SetValues(PVBLK pv, i template <class TYPE> void TYPBLK<TYPE>::Move(int i, int j) { - Typp[j] = Typp[i]; + UnalignedWrite(j, UnalignedRead(i)); MoveNull(i, j); } // end of Move
@@ -536,7 +536,7 @@ int TYPBLK<TYPE>::CompVal(PVAL vp, int n ChkIndx(n); ChkTyp(vp); #endif // _DEBUG - TYPE mlv = Typp[n]; + TYPE mlv = UnalignedRead(n); TYPE vlv = GetTypedValue(vp);
return (vlv > mlv) ? 1 : (vlv < mlv) ? (-1) : 0; @@ -548,8 +548,8 @@ int TYPBLK<TYPE>::CompVal(PVAL vp, int n template <class TYPE> int TYPBLK<TYPE>::CompVal(int i1, int i2) { - TYPE lv1 = Typp[i1]; - TYPE lv2 = Typp[i2]; + TYPE lv1 = UnalignedRead(i1); + TYPE lv2 = UnalignedRead(i2);
return (lv1 > lv2) ? 1 : (lv1 < lv2) ? (-1) : 0; } // end of CompVal @@ -586,7 +586,7 @@ int TYPBLK<TYPE>::Find(PVAL vp) TYPE n = GetTypedValue(vp);
for (i = 0; i < Nval; i++) - if (n == Typp[i]) + if (n == UnalignedRead(i)) break;
return (i < Nval) ? i : (-1); @@ -602,7 +602,7 @@ int TYPBLK<TYPE>::GetMaxLength(void) int i, n, m;
for (i = n = 0; i < Nval; i++) { - m = sprintf(buf, Fmt, Typp[i]); + m = sprintf(buf, Fmt, UnalignedRead(i)); n = MY_MAX(n, m); } // endfor i
@@ -1332,7 +1332,7 @@ char *DATBLK::GetCharString(char *p, int char *vp;
if (Dvalp) { - Dvalp->SetValue(Typp[n]); + Dvalp->SetValue(UnalignedRead(n)); vp = Dvalp->GetCharString(p); } else vp = TYPBLK<int>::GetCharString(p, n); @@ -1348,7 +1348,7 @@ void DATBLK::SetValue(PSZ p, int n) if (Dvalp) { // Decode the string according to format Dvalp->SetValue_psz(p); - Typp[n] = Dvalp->GetIntValue(); + UnalignedWrite(n, Dvalp->GetIntValue()); } else TYPBLK<int>::SetValue(p, n);
----- End forwarded message -----
Regards, Sergei Chief Architect MariaDB and security@mariadb.org
Is this just a sort of precaution or did bus errors effectively occured?
I've included James Cowgill who saw the problem and made the patch. James, do you have a stacktrace showing a bus error, to narrow down where exactly the code is doing unaligned accesses?
What is strange is that seems to occur only now. CONNECT exists for at least four years and the code of VALBLK was still existing at that time.
Well, MIPS is not a common platform, certainly not for running MariaDB with the CONNECT engine. Most main-stream platforms will not cause a crash for unaligned accesses.
My question is because all (sub)allocations made by connect are always done on a memory whose address is a multiple of 8. This means that in a value block that is aligned on a multiple of 8 location, big integer and doubles should be correctly aligned, integers are aligned on a multiple of 4, smallint of 2 etc.
Is this enough? If not, the patch is required but if it is ok the patch is useless.
Yes, this should be enough, according to my understanding.
However, I don't know how malloc and realloc work on mips processor. These blocks are declared as pointed by a void* pointer. Perhaps this does not make them to make an allocation on a properly aligned memory.
Malloc and friends should always return memory aligned properly for any data type, irrespectively of void* or other pointer declaration.
I checked in CONNECT source, VALBLK blocks are allocated by PlgDBalloc in plgdbutl.cpp line 1206 or PlgDBrealloc line 1268. And indeed, depending on the block size, they can be suballocated or normally allocated by malloc or realloc.
Agree, that looks correct. My understanding from the patch was that CONNECT was storing data packed, eg. two bytes of short followed immediately by 8 bytes of double without padding, or similar. But it sounds from you like this should not be happening in the code. And agree, we should understand exactly how the unaligned access occurs, or what else is the root cause of the errors on MIPS. My guess is that James just saw this during running the test suite on MIPS. It is a bit hard to reproduce without access to MIPS hardware, but maybe qemu can help here. But let's first see if James has a stacktrace that shows exactly where the bus error occurs. - Kristian.
participants (2)
-
Kristian Nielsen
-
Sergei Golubchik