From e3d36cc0a80ff0f85499718db7eb8adfedcc87b3 Mon Sep 17 00:00:00 2001 From: Tomas Vondra Date: Wed, 3 Apr 2019 00:05:52 +0200 Subject: [PATCH 2/2] extra memory-alignment asserts --- src/backend/statistics/mcv.c | 68 ++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c index 633f73e1be..8f0651f9fe 100644 --- a/src/backend/statistics/mcv.c +++ b/src/backend/statistics/mcv.c @@ -439,6 +439,11 @@ statext_mcv_load(Oid mvoid) return result; } +static void +AssertIsAligned(const void *base, const void *ptr) +{ + Assert(((char *) ptr - (char *) base) == MAXALIGN((char *) ptr - (char *) base)); +} /* * statext_mcv_serialize @@ -672,16 +677,23 @@ statext_mcv_serialize(MCVList * mcvlist, VacAttrStats **stats) /* the header may not be exactly aligned, so make sure it is */ ptr = raw + MAXALIGN(ptr - raw); + AssertIsAligned(raw, ptr); + /* store information about the attributes */ memcpy(ptr, info, sizeof(DimensionInfo) * ndims); ptr += MAXALIGN(sizeof(DimensionInfo) * ndims); + AssertIsAligned(raw, ptr); + /* Copy the deduplicated values for all attributes to the output. */ for (dim = 0; dim < ndims; dim++) { /* remember the starting point for Asserts later */ char *start PG_USED_FOR_ASSERTS_ONLY = ptr; + /* proper alignment before earch dimension */ + AssertIsAligned(raw, ptr); + for (i = 0; i < info[dim].nvalues; i++) { Datum value = values[dim][i]; @@ -714,6 +726,9 @@ statext_mcv_serialize(MCVList * mcvlist, VacAttrStats **stats) { int len = VARSIZE_ANY(value); + /* proper alignment before varlena values */ + AssertIsAligned(raw, ptr); + memcpy(ptr, DatumGetPointer(value), len); ptr += MAXALIGN(len); } @@ -721,6 +736,9 @@ statext_mcv_serialize(MCVList * mcvlist, VacAttrStats **stats) { Size len = strlen(DatumGetCString(value)) + 1; /* terminator */ + /* proper alignment before cstring values */ + AssertIsAligned(raw, ptr); + memcpy(ptr, DatumGetCString(value), len); ptr += MAXALIGN(len); } @@ -773,6 +791,9 @@ statext_mcv_serialize(MCVList * mcvlist, VacAttrStats **stats) memcpy(ITEM_FREQUENCY(item, ndims), &mcvitem->frequency, sizeof(double)); memcpy(ITEM_BASE_FREQUENCY(item, ndims), &mcvitem->base_frequency, sizeof(double)); + /* each serialized item is aligned */ + AssertIsAligned(raw, ptr); + /* copy the serialized item into the array */ memcpy(ptr, item, itemsize); @@ -886,18 +907,13 @@ statext_mcv_deserialize(bytea *data) nitems = mcvlist->nitems; ndims = mcvlist->ndimensions; - /* - * Check amount of data including DimensionInfo for all dimensions and - * also the serialized items (including uint16 indexes). Also, walk - * through the dimension information and add it to the sum. - */ - expected_size = SizeOfMCVList(ndims, nitems); - /* * Check that we have at least the dimension and info records, along with * the items. We don't know the size of the serialized values yet. We need * to do this check first, before accessing the dimension info. */ + expected_size = SizeOfMCVList(ndims, nitems); + if (VARSIZE_ANY(data) < expected_size) elog(ERROR, "invalid MCV size %zd (expected %zu)", VARSIZE_ANY(data), expected_size); @@ -909,6 +925,9 @@ statext_mcv_deserialize(bytea *data) /* ensure alignment of the pointer (after the header fields) */ ptr = raw + MAXALIGN(ptr - raw); + /* proper alignment before serialized dimension info */ + AssertIsAligned(raw, ptr); + /* Now it's safe to access the dimension info. */ info = (DimensionInfo *) ptr; ptr += MAXALIGN(ndims * sizeof(DimensionInfo)); @@ -982,6 +1001,11 @@ statext_mcv_deserialize(bytea *data) dataptr = isnullptr + (nitems * MAXALIGN(sizeof(bool) * ndims)); + /* proper alignment of pointers used to allocate MCV parts */ + AssertIsAligned((char *) mcvlist, valuesptr); + AssertIsAligned((char *) mcvlist, isnullptr); + AssertIsAligned((char *) mcvlist, dataptr); + /* * Build mapping (index => value) for translating the serialized data into * the in-memory representation. @@ -991,6 +1015,9 @@ statext_mcv_deserialize(bytea *data) /* remember start position in the input array */ char *start PG_USED_FOR_ASSERTS_ONLY = ptr; + /* proper alignment before serialized values in input buffer */ + AssertIsAligned(raw, ptr); + if (info[dim].typbyval) { /* for by-val types we simply copy data into the mapping */ @@ -1029,7 +1056,13 @@ statext_mcv_deserialize(bytea *data) /* varlena */ for (i = 0; i < info[dim].nvalues; i++) { - Size len = VARSIZE_ANY(ptr); + Size len; + + /* proper alignment before serialized varlena value */ + AssertIsAligned(raw, ptr); + AssertIsAligned((char *) mcvlist, dataptr); + + len = VARSIZE_ANY(ptr); memcpy(dataptr, ptr, len); ptr += MAXALIGN(len); @@ -1044,7 +1077,13 @@ statext_mcv_deserialize(bytea *data) /* cstring */ for (i = 0; i < info[dim].nvalues; i++) { - Size len = (strlen(ptr) + 1); /* don't forget the \0 */ + Size len; + + /* proper alignment before serialized cstring value */ + AssertIsAligned(raw, ptr); + AssertIsAligned((char *) mcvlist, dataptr); + + len = (strlen(ptr) + 1); /* don't forget the \0 */ memcpy(dataptr, ptr, len); ptr += MAXALIGN(len); @@ -1069,7 +1108,10 @@ statext_mcv_deserialize(bytea *data) ptr = raw + MAXALIGN(ptr - raw); } - /* we should have also filled the MCV list exactly */ + /* + * We should have also filled the MCV list exactly (data is allocated + * at the very end of the allocated buffer). + */ Assert(dataptr == ((char *) mcvlist + mcvlen)); /* deserialize the MCV items and translate the indexes to Datums */ @@ -1084,6 +1126,12 @@ statext_mcv_deserialize(bytea *data) item->isnull = (bool *) isnullptr; isnullptr += MAXALIGN(sizeof(bool) * ndims); + /* proper alignment of serialized MCV items */ + AssertIsAligned(raw, ptr); + + /* proper alignment of deserialized values/isnull arrays */ + AssertIsAligned((char *) mcvlist, valuesptr); + AssertIsAligned((char *) mcvlist, isnullptr); /* just point to the right place */ indexes = ITEM_INDEXES(ptr); -- 2.20.1