diff options
| author | Roy Li <rongqing.li@windriver.com> | 2015-06-09 11:07:24 +0800 |
|---|---|---|
| committer | Martin Jansa <Martin.Jansa@gmail.com> | 2015-06-23 12:40:10 +0200 |
| commit | 13a7f47192ffdce659da610322122d887c53b325 (patch) | |
| tree | 62fd83b9fcf9d21eff05792fa203fa2bd63fc927 /meta-oe | |
| parent | 780333ce0d786748530078a81b56568ad72119c9 (diff) | |
| download | meta-openembedded-13a7f47192ffdce659da610322122d887c53b325.tar.gz | |
postgresql: upgrade to 9.4.2
1. remove Backport patches
2. Update the checksume, include CopyRight file, since date in it
is changed
3. remove --without-krb5 configure options, since it become useless
4. Update remove.autoconf.version.check.patch
5. skip to check libperl
Signed-off-by: Roy Li <rongqing.li@windriver.com>
Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com>
Diffstat (limited to 'meta-oe')
13 files changed, 48 insertions, 3184 deletions
diff --git a/meta-oe/recipes-support/postgresql/files/0002-Predict-integer-overflow-to-avoid-buffer-overruns.patch b/meta-oe/recipes-support/postgresql/files/0002-Predict-integer-overflow-to-avoid-buffer-overruns.patch deleted file mode 100644 index c8b4c80aa3..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0002-Predict-integer-overflow-to-avoid-buffer-overruns.patch +++ /dev/null | |||
| @@ -1,605 +0,0 @@ | |||
| 1 | From 12bbce15d93d7692ddff1405aa04b67f8a327f57 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Noah Misch <noah@leadboat.com> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Predict integer overflow to avoid buffer overruns. | ||
| 5 | |||
| 6 | commit 12bbce15d93d7692ddff1405aa04b67f8a327f57 REL9_2_STABLE | ||
| 7 | |||
| 8 | Several functions, mostly type input functions, calculated an allocation | ||
| 9 | size such that the calculation wrapped to a small positive value when | ||
| 10 | arguments implied a sufficiently-large requirement. Writes past the end | ||
| 11 | of the inadvertent small allocation followed shortly thereafter. | ||
| 12 | Coverity identified the path_in() vulnerability; code inspection led to | ||
| 13 | the rest. In passing, add check_stack_depth() to prevent stack overflow | ||
| 14 | in related functions. | ||
| 15 | |||
| 16 | Back-patch to 8.4 (all supported versions). The non-comment hstore | ||
| 17 | changes touch code that did not exist in 8.4, so that part stops at 9.0. | ||
| 18 | |||
| 19 | Noah Misch and Heikki Linnakangas, reviewed by Tom Lane. | ||
| 20 | |||
| 21 | Security: CVE-2014-0064 | ||
| 22 | |||
| 23 | Upstream-Status: Backport | ||
| 24 | |||
| 25 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 26 | --- | ||
| 27 | contrib/hstore/hstore.h | 15 ++++++++++++--- | ||
| 28 | contrib/hstore/hstore_io.c | 21 +++++++++++++++++++++ | ||
| 29 | contrib/hstore/hstore_op.c | 15 +++++++++++++++ | ||
| 30 | contrib/intarray/_int.h | 2 ++ | ||
| 31 | contrib/intarray/_int_bool.c | 9 +++++++++ | ||
| 32 | contrib/ltree/ltree.h | 3 +++ | ||
| 33 | contrib/ltree/ltree_io.c | 11 +++++++++++ | ||
| 34 | contrib/ltree/ltxtquery_io.c | 13 ++++++++++++- | ||
| 35 | src/backend/utils/adt/geo_ops.c | 30 ++++++++++++++++++++++++++++-- | ||
| 36 | src/backend/utils/adt/tsquery.c | 7 ++++++- | ||
| 37 | src/backend/utils/adt/tsquery_util.c | 5 +++++ | ||
| 38 | src/backend/utils/adt/txid.c | 15 +++++---------- | ||
| 39 | src/backend/utils/adt/varbit.c | 32 ++++++++++++++++++++++++++++++-- | ||
| 40 | src/include/tsearch/ts_type.h | 3 +++ | ||
| 41 | src/include/utils/varbit.h | 7 +++++++ | ||
| 42 | 15 files changed, 169 insertions(+), 19 deletions(-) | ||
| 43 | |||
| 44 | diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h | ||
| 45 | index 8906397..4e55f6e 100644 | ||
| 46 | --- a/contrib/hstore/hstore.h | ||
| 47 | +++ b/contrib/hstore/hstore.h | ||
| 48 | @@ -49,9 +49,12 @@ typedef struct | ||
| 49 | } HStore; | ||
| 50 | |||
| 51 | /* | ||
| 52 | - * it's not possible to get more than 2^28 items into an hstore, | ||
| 53 | - * so we reserve the top few bits of the size field. See hstore_compat.c | ||
| 54 | - * for one reason why. Some bits are left for future use here. | ||
| 55 | + * It's not possible to get more than 2^28 items into an hstore, so we reserve | ||
| 56 | + * the top few bits of the size field. See hstore_compat.c for one reason | ||
| 57 | + * why. Some bits are left for future use here. MaxAllocSize makes the | ||
| 58 | + * practical count limit slightly more than 2^28 / 3, or INT_MAX / 24, the | ||
| 59 | + * limit for an hstore full of 4-byte keys and null values. Therefore, we | ||
| 60 | + * don't explicitly check the format-imposed limit. | ||
| 61 | */ | ||
| 62 | #define HS_FLAG_NEWVERSION 0x80000000 | ||
| 63 | |||
| 64 | @@ -59,6 +62,12 @@ typedef struct | ||
| 65 | #define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION) | ||
| 66 | |||
| 67 | |||
| 68 | +/* | ||
| 69 | + * "x" comes from an existing HS_COUNT() (as discussed, <= INT_MAX/24) or a | ||
| 70 | + * Pairs array length (due to MaxAllocSize, <= INT_MAX/40). "lenstr" is no | ||
| 71 | + * more than INT_MAX, that extreme case arising in hstore_from_arrays(). | ||
| 72 | + * Therefore, this calculation is limited to about INT_MAX / 5 + INT_MAX. | ||
| 73 | + */ | ||
| 74 | #define HSHRDSIZE (sizeof(HStore)) | ||
| 75 | #define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) | ||
| 76 | |||
| 77 | diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c | ||
| 78 | index dde6c4b..5bcdc95 100644 | ||
| 79 | --- a/contrib/hstore/hstore_io.c | ||
| 80 | +++ b/contrib/hstore/hstore_io.c | ||
| 81 | @@ -9,6 +9,7 @@ | ||
| 82 | #include "funcapi.h" | ||
| 83 | #include "libpq/pqformat.h" | ||
| 84 | #include "utils/lsyscache.h" | ||
| 85 | +#include "utils/memutils.h" | ||
| 86 | #include "utils/typcache.h" | ||
| 87 | |||
| 88 | #include "hstore.h" | ||
| 89 | @@ -437,6 +438,11 @@ hstore_recv(PG_FUNCTION_ARGS) | ||
| 90 | PG_RETURN_POINTER(out); | ||
| 91 | } | ||
| 92 | |||
| 93 | + if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs)) | ||
| 94 | + ereport(ERROR, | ||
| 95 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 96 | + errmsg("number of pairs (%d) exceeds the maximum allowed (%d)", | ||
| 97 | + pcount, (int) (MaxAllocSize / sizeof(Pairs))))); | ||
| 98 | pairs = palloc(pcount * sizeof(Pairs)); | ||
| 99 | |||
| 100 | for (i = 0; i < pcount; ++i) | ||
| 101 | @@ -552,6 +558,13 @@ hstore_from_arrays(PG_FUNCTION_ARGS) | ||
| 102 | TEXTOID, -1, false, 'i', | ||
| 103 | &key_datums, &key_nulls, &key_count); | ||
| 104 | |||
| 105 | + /* see discussion in hstoreArrayToPairs() */ | ||
| 106 | + if (key_count > MaxAllocSize / sizeof(Pairs)) | ||
| 107 | + ereport(ERROR, | ||
| 108 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 109 | + errmsg("number of pairs (%d) exceeds the maximum allowed (%d)", | ||
| 110 | + key_count, (int) (MaxAllocSize / sizeof(Pairs))))); | ||
| 111 | + | ||
| 112 | /* value_array might be NULL */ | ||
| 113 | |||
| 114 | if (PG_ARGISNULL(1)) | ||
| 115 | @@ -674,6 +687,13 @@ hstore_from_array(PG_FUNCTION_ARGS) | ||
| 116 | |||
| 117 | count = in_count / 2; | ||
| 118 | |||
| 119 | + /* see discussion in hstoreArrayToPairs() */ | ||
| 120 | + if (count > MaxAllocSize / sizeof(Pairs)) | ||
| 121 | + ereport(ERROR, | ||
| 122 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 123 | + errmsg("number of pairs (%d) exceeds the maximum allowed (%d)", | ||
| 124 | + count, (int) (MaxAllocSize / sizeof(Pairs))))); | ||
| 125 | + | ||
| 126 | pairs = palloc(count * sizeof(Pairs)); | ||
| 127 | |||
| 128 | for (i = 0; i < count; ++i) | ||
| 129 | @@ -805,6 +825,7 @@ hstore_from_record(PG_FUNCTION_ARGS) | ||
| 130 | my_extra->ncolumns = ncolumns; | ||
| 131 | } | ||
| 132 | |||
| 133 | + Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */ | ||
| 134 | pairs = palloc(ncolumns * sizeof(Pairs)); | ||
| 135 | |||
| 136 | if (rec) | ||
| 137 | diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c | ||
| 138 | index fee2c3c..8de175a 100644 | ||
| 139 | --- a/contrib/hstore/hstore_op.c | ||
| 140 | +++ b/contrib/hstore/hstore_op.c | ||
| 141 | @@ -7,6 +7,7 @@ | ||
| 142 | #include "catalog/pg_type.h" | ||
| 143 | #include "funcapi.h" | ||
| 144 | #include "utils/builtins.h" | ||
| 145 | +#include "utils/memutils.h" | ||
| 146 | |||
| 147 | #include "hstore.h" | ||
| 148 | |||
| 149 | @@ -89,6 +90,19 @@ hstoreArrayToPairs(ArrayType *a, int *npairs) | ||
| 150 | return NULL; | ||
| 151 | } | ||
| 152 | |||
| 153 | + /* | ||
| 154 | + * A text array uses at least eight bytes per element, so any overflow in | ||
| 155 | + * "key_count * sizeof(Pairs)" is small enough for palloc() to catch. | ||
| 156 | + * However, credible improvements to the array format could invalidate | ||
| 157 | + * that assumption. Therefore, use an explicit check rather than relying | ||
| 158 | + * on palloc() to complain. | ||
| 159 | + */ | ||
| 160 | + if (key_count > MaxAllocSize / sizeof(Pairs)) | ||
| 161 | + ereport(ERROR, | ||
| 162 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 163 | + errmsg("number of pairs (%d) exceeds the maximum allowed (%d)", | ||
| 164 | + key_count, (int) (MaxAllocSize / sizeof(Pairs))))); | ||
| 165 | + | ||
| 166 | key_pairs = palloc(sizeof(Pairs) * key_count); | ||
| 167 | |||
| 168 | for (i = 0, j = 0; i < key_count; i++) | ||
| 169 | @@ -647,6 +661,7 @@ hstore_slice_to_hstore(PG_FUNCTION_ARGS) | ||
| 170 | PG_RETURN_POINTER(out); | ||
| 171 | } | ||
| 172 | |||
| 173 | + /* hstoreArrayToPairs() checked overflow */ | ||
| 174 | out_pairs = palloc(sizeof(Pairs) * nkeys); | ||
| 175 | bufsiz = 0; | ||
| 176 | |||
| 177 | diff --git a/contrib/intarray/_int.h b/contrib/intarray/_int.h | ||
| 178 | index 11c0698..755cd9e 100644 | ||
| 179 | --- a/contrib/intarray/_int.h | ||
| 180 | +++ b/contrib/intarray/_int.h | ||
| 181 | @@ -5,6 +5,7 @@ | ||
| 182 | #define ___INT_H__ | ||
| 183 | |||
| 184 | #include "utils/array.h" | ||
| 185 | +#include "utils/memutils.h" | ||
| 186 | |||
| 187 | /* number ranges for compression */ | ||
| 188 | #define MAXNUMRANGE 100 | ||
| 189 | @@ -137,6 +138,7 @@ typedef struct QUERYTYPE | ||
| 190 | |||
| 191 | #define HDRSIZEQT offsetof(QUERYTYPE, items) | ||
| 192 | #define COMPUTESIZE(size) ( HDRSIZEQT + (size) * sizeof(ITEM) ) | ||
| 193 | +#define QUERYTYPEMAXITEMS ((MaxAllocSize - HDRSIZEQT) / sizeof(ITEM)) | ||
| 194 | #define GETQUERY(x) ( (x)->items ) | ||
| 195 | |||
| 196 | /* "type" codes for ITEM */ | ||
| 197 | diff --git a/contrib/intarray/_int_bool.c b/contrib/intarray/_int_bool.c | ||
| 198 | index 4e63f6d..62294d1 100644 | ||
| 199 | --- a/contrib/intarray/_int_bool.c | ||
| 200 | +++ b/contrib/intarray/_int_bool.c | ||
| 201 | @@ -451,6 +451,9 @@ boolop(PG_FUNCTION_ARGS) | ||
| 202 | static void | ||
| 203 | findoprnd(ITEM *ptr, int4 *pos) | ||
| 204 | { | ||
| 205 | + /* since this function recurses, it could be driven to stack overflow. */ | ||
| 206 | + check_stack_depth(); | ||
| 207 | + | ||
| 208 | #ifdef BS_DEBUG | ||
| 209 | elog(DEBUG3, (ptr[*pos].type == OPR) ? | ||
| 210 | "%d %c" : "%d %d", *pos, ptr[*pos].val); | ||
| 211 | @@ -511,7 +514,13 @@ bqarr_in(PG_FUNCTION_ARGS) | ||
| 212 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
| 213 | errmsg("empty query"))); | ||
| 214 | |||
| 215 | + if (state.num > QUERYTYPEMAXITEMS) | ||
| 216 | + ereport(ERROR, | ||
| 217 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 218 | + errmsg("number of query items (%d) exceeds the maximum allowed (%d)", | ||
| 219 | + state.num, (int) QUERYTYPEMAXITEMS))); | ||
| 220 | commonlen = COMPUTESIZE(state.num); | ||
| 221 | + | ||
| 222 | query = (QUERYTYPE *) palloc(commonlen); | ||
| 223 | SET_VARSIZE(query, commonlen); | ||
| 224 | query->size = state.num; | ||
| 225 | diff --git a/contrib/ltree/ltree.h b/contrib/ltree/ltree.h | ||
| 226 | index aec4458..49e9907 100644 | ||
| 227 | --- a/contrib/ltree/ltree.h | ||
| 228 | +++ b/contrib/ltree/ltree.h | ||
| 229 | @@ -5,6 +5,7 @@ | ||
| 230 | |||
| 231 | #include "fmgr.h" | ||
| 232 | #include "tsearch/ts_locale.h" | ||
| 233 | +#include "utils/memutils.h" | ||
| 234 | |||
| 235 | typedef struct | ||
| 236 | { | ||
| 237 | @@ -111,6 +112,8 @@ typedef struct | ||
| 238 | |||
| 239 | #define HDRSIZEQT MAXALIGN(VARHDRSZ + sizeof(int4)) | ||
| 240 | #define COMPUTESIZE(size,lenofoperand) ( HDRSIZEQT + (size) * sizeof(ITEM) + (lenofoperand) ) | ||
| 241 | +#define LTXTQUERY_TOO_BIG(size,lenofoperand) \ | ||
| 242 | + ((size) > (MaxAllocSize - HDRSIZEQT - (lenofoperand)) / sizeof(ITEM)) | ||
| 243 | #define GETQUERY(x) (ITEM*)( (char*)(x)+HDRSIZEQT ) | ||
| 244 | #define GETOPERAND(x) ( (char*)GETQUERY(x) + ((ltxtquery*)x)->size * sizeof(ITEM) ) | ||
| 245 | |||
| 246 | diff --git a/contrib/ltree/ltree_io.c b/contrib/ltree/ltree_io.c | ||
| 247 | index 3e88b81..d64debb 100644 | ||
| 248 | --- a/contrib/ltree/ltree_io.c | ||
| 249 | +++ b/contrib/ltree/ltree_io.c | ||
| 250 | @@ -8,6 +8,7 @@ | ||
| 251 | #include <ctype.h> | ||
| 252 | |||
| 253 | #include "ltree.h" | ||
| 254 | +#include "utils/memutils.h" | ||
| 255 | #include "crc32.h" | ||
| 256 | |||
| 257 | PG_FUNCTION_INFO_V1(ltree_in); | ||
| 258 | @@ -64,6 +65,11 @@ ltree_in(PG_FUNCTION_ARGS) | ||
| 259 | ptr += charlen; | ||
| 260 | } | ||
| 261 | |||
| 262 | + if (num + 1 > MaxAllocSize / sizeof(nodeitem)) | ||
| 263 | + ereport(ERROR, | ||
| 264 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 265 | + errmsg("number of levels (%d) exceeds the maximum allowed (%d)", | ||
| 266 | + num + 1, (int) (MaxAllocSize / sizeof(nodeitem))))); | ||
| 267 | list = lptr = (nodeitem *) palloc(sizeof(nodeitem) * (num + 1)); | ||
| 268 | ptr = buf; | ||
| 269 | while (*ptr) | ||
| 270 | @@ -228,6 +234,11 @@ lquery_in(PG_FUNCTION_ARGS) | ||
| 271 | } | ||
| 272 | |||
| 273 | num++; | ||
| 274 | + if (num > MaxAllocSize / ITEMSIZE) | ||
| 275 | + ereport(ERROR, | ||
| 276 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 277 | + errmsg("number of levels (%d) exceeds the maximum allowed (%d)", | ||
| 278 | + num, (int) (MaxAllocSize / ITEMSIZE)))); | ||
| 279 | curqlevel = tmpql = (lquery_level *) palloc0(ITEMSIZE * num); | ||
| 280 | ptr = buf; | ||
| 281 | while (*ptr) | ||
| 282 | diff --git a/contrib/ltree/ltxtquery_io.c b/contrib/ltree/ltxtquery_io.c | ||
| 283 | index 826f4e1..13ea58d 100644 | ||
| 284 | --- a/contrib/ltree/ltxtquery_io.c | ||
| 285 | +++ b/contrib/ltree/ltxtquery_io.c | ||
| 286 | @@ -9,6 +9,7 @@ | ||
| 287 | |||
| 288 | #include "crc32.h" | ||
| 289 | #include "ltree.h" | ||
| 290 | +#include "miscadmin.h" | ||
| 291 | |||
| 292 | PG_FUNCTION_INFO_V1(ltxtq_in); | ||
| 293 | Datum ltxtq_in(PG_FUNCTION_ARGS); | ||
| 294 | @@ -213,6 +214,9 @@ makepol(QPRS_STATE *state) | ||
| 295 | int4 lenstack = 0; | ||
| 296 | uint16 flag = 0; | ||
| 297 | |||
| 298 | + /* since this function recurses, it could be driven to stack overflow */ | ||
| 299 | + check_stack_depth(); | ||
| 300 | + | ||
| 301 | while ((type = gettoken_query(state, &val, &lenval, &strval, &flag)) != END) | ||
| 302 | { | ||
| 303 | switch (type) | ||
| 304 | @@ -277,6 +281,9 @@ makepol(QPRS_STATE *state) | ||
| 305 | static void | ||
| 306 | findoprnd(ITEM *ptr, int4 *pos) | ||
| 307 | { | ||
| 308 | + /* since this function recurses, it could be driven to stack overflow. */ | ||
| 309 | + check_stack_depth(); | ||
| 310 | + | ||
| 311 | if (ptr[*pos].type == VAL || ptr[*pos].type == VALTRUE) | ||
| 312 | { | ||
| 313 | ptr[*pos].left = 0; | ||
| 314 | @@ -341,8 +348,12 @@ queryin(char *buf) | ||
| 315 | errmsg("syntax error"), | ||
| 316 | errdetail("Empty query."))); | ||
| 317 | |||
| 318 | - /* make finish struct */ | ||
| 319 | + if (LTXTQUERY_TOO_BIG(state.num, state.sumlen)) | ||
| 320 | + ereport(ERROR, | ||
| 321 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 322 | + errmsg("ltxtquery is too large"))); | ||
| 323 | commonlen = COMPUTESIZE(state.num, state.sumlen); | ||
| 324 | + | ||
| 325 | query = (ltxtquery *) palloc(commonlen); | ||
| 326 | SET_VARSIZE(query, commonlen); | ||
| 327 | query->size = state.num; | ||
| 328 | diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c | ||
| 329 | index ac7b4b8..7ebcaaa 100644 | ||
| 330 | --- a/src/backend/utils/adt/geo_ops.c | ||
| 331 | +++ b/src/backend/utils/adt/geo_ops.c | ||
| 332 | @@ -1403,6 +1403,7 @@ path_in(PG_FUNCTION_ARGS) | ||
| 333 | char *s; | ||
| 334 | int npts; | ||
| 335 | int size; | ||
| 336 | + int base_size; | ||
| 337 | int depth = 0; | ||
| 338 | |||
| 339 | if ((npts = pair_count(str, ',')) <= 0) | ||
| 340 | @@ -1421,7 +1422,15 @@ path_in(PG_FUNCTION_ARGS) | ||
| 341 | depth++; | ||
| 342 | } | ||
| 343 | |||
| 344 | - size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * npts; | ||
| 345 | + base_size = sizeof(path->p[0]) * npts; | ||
| 346 | + size = offsetof(PATH, p[0]) + base_size; | ||
| 347 | + | ||
| 348 | + /* Check for integer overflow */ | ||
| 349 | + if (base_size / npts != sizeof(path->p[0]) || size <= base_size) | ||
| 350 | + ereport(ERROR, | ||
| 351 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 352 | + errmsg("too many points requested"))); | ||
| 353 | + | ||
| 354 | path = (PATH *) palloc(size); | ||
| 355 | |||
| 356 | SET_VARSIZE(path, size); | ||
| 357 | @@ -3465,6 +3474,7 @@ poly_in(PG_FUNCTION_ARGS) | ||
| 358 | POLYGON *poly; | ||
| 359 | int npts; | ||
| 360 | int size; | ||
| 361 | + int base_size; | ||
| 362 | int isopen; | ||
| 363 | char *s; | ||
| 364 | |||
| 365 | @@ -3473,7 +3483,15 @@ poly_in(PG_FUNCTION_ARGS) | ||
| 366 | (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), | ||
| 367 | errmsg("invalid input syntax for type polygon: \"%s\"", str))); | ||
| 368 | |||
| 369 | - size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * npts; | ||
| 370 | + base_size = sizeof(poly->p[0]) * npts; | ||
| 371 | + size = offsetof(POLYGON, p[0]) + base_size; | ||
| 372 | + | ||
| 373 | + /* Check for integer overflow */ | ||
| 374 | + if (base_size / npts != sizeof(poly->p[0]) || size <= base_size) | ||
| 375 | + ereport(ERROR, | ||
| 376 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 377 | + errmsg("too many points requested"))); | ||
| 378 | + | ||
| 379 | poly = (POLYGON *) palloc0(size); /* zero any holes */ | ||
| 380 | |||
| 381 | SET_VARSIZE(poly, size); | ||
| 382 | @@ -4379,6 +4397,10 @@ path_poly(PG_FUNCTION_ARGS) | ||
| 383 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
| 384 | errmsg("open path cannot be converted to polygon"))); | ||
| 385 | |||
| 386 | + /* | ||
| 387 | + * Never overflows: the old size fit in MaxAllocSize, and the new size is | ||
| 388 | + * just a small constant larger. | ||
| 389 | + */ | ||
| 390 | size = offsetof(POLYGON, p[0]) +sizeof(poly->p[0]) * path->npts; | ||
| 391 | poly = (POLYGON *) palloc(size); | ||
| 392 | |||
| 393 | @@ -4484,6 +4506,10 @@ poly_path(PG_FUNCTION_ARGS) | ||
| 394 | int size; | ||
| 395 | int i; | ||
| 396 | |||
| 397 | + /* | ||
| 398 | + * Never overflows: the old size fit in MaxAllocSize, and the new size is | ||
| 399 | + * smaller by a small constant. | ||
| 400 | + */ | ||
| 401 | size = offsetof(PATH, p[0]) +sizeof(path->p[0]) * poly->npts; | ||
| 402 | path = (PATH *) palloc(size); | ||
| 403 | |||
| 404 | diff --git a/src/backend/utils/adt/tsquery.c b/src/backend/utils/adt/tsquery.c | ||
| 405 | index 6e1f8cf..1322b5e 100644 | ||
| 406 | --- a/src/backend/utils/adt/tsquery.c | ||
| 407 | +++ b/src/backend/utils/adt/tsquery.c | ||
| 408 | @@ -515,8 +515,13 @@ parse_tsquery(char *buf, | ||
| 409 | return query; | ||
| 410 | } | ||
| 411 | |||
| 412 | - /* Pack the QueryItems in the final TSQuery struct to return to caller */ | ||
| 413 | + if (TSQUERY_TOO_BIG(list_length(state.polstr), state.sumlen)) | ||
| 414 | + ereport(ERROR, | ||
| 415 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 416 | + errmsg("tsquery is too large"))); | ||
| 417 | commonlen = COMPUTESIZE(list_length(state.polstr), state.sumlen); | ||
| 418 | + | ||
| 419 | + /* Pack the QueryItems in the final TSQuery struct to return to caller */ | ||
| 420 | query = (TSQuery) palloc0(commonlen); | ||
| 421 | SET_VARSIZE(query, commonlen); | ||
| 422 | query->size = list_length(state.polstr); | ||
| 423 | diff --git a/src/backend/utils/adt/tsquery_util.c b/src/backend/utils/adt/tsquery_util.c | ||
| 424 | index 0724d33..9003702 100644 | ||
| 425 | --- a/src/backend/utils/adt/tsquery_util.c | ||
| 426 | +++ b/src/backend/utils/adt/tsquery_util.c | ||
| 427 | @@ -333,6 +333,11 @@ QTN2QT(QTNode *in) | ||
| 428 | QTN2QTState state; | ||
| 429 | |||
| 430 | cntsize(in, &sumlen, &nnode); | ||
| 431 | + | ||
| 432 | + if (TSQUERY_TOO_BIG(nnode, sumlen)) | ||
| 433 | + ereport(ERROR, | ||
| 434 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 435 | + errmsg("tsquery is too large"))); | ||
| 436 | len = COMPUTESIZE(nnode, sumlen); | ||
| 437 | |||
| 438 | out = (TSQuery) palloc0(len); | ||
| 439 | diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c | ||
| 440 | index 08a8c89..c71daaf 100644 | ||
| 441 | --- a/src/backend/utils/adt/txid.c | ||
| 442 | +++ b/src/backend/utils/adt/txid.c | ||
| 443 | @@ -27,6 +27,7 @@ | ||
| 444 | #include "miscadmin.h" | ||
| 445 | #include "libpq/pqformat.h" | ||
| 446 | #include "utils/builtins.h" | ||
| 447 | +#include "utils/memutils.h" | ||
| 448 | #include "utils/snapmgr.h" | ||
| 449 | |||
| 450 | |||
| 451 | @@ -66,6 +67,8 @@ typedef struct | ||
| 452 | |||
| 453 | #define TXID_SNAPSHOT_SIZE(nxip) \ | ||
| 454 | (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip)) | ||
| 455 | +#define TXID_SNAPSHOT_MAX_NXIP \ | ||
| 456 | + ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid)) | ||
| 457 | |||
| 458 | /* | ||
| 459 | * Epoch values from xact.c | ||
| 460 | @@ -445,20 +448,12 @@ txid_snapshot_recv(PG_FUNCTION_ARGS) | ||
| 461 | txid last = 0; | ||
| 462 | int nxip; | ||
| 463 | int i; | ||
| 464 | - int avail; | ||
| 465 | - int expect; | ||
| 466 | txid xmin, | ||
| 467 | xmax; | ||
| 468 | |||
| 469 | - /* | ||
| 470 | - * load nxip and check for nonsense. | ||
| 471 | - * | ||
| 472 | - * (nxip > avail) check is against int overflows in 'expect'. | ||
| 473 | - */ | ||
| 474 | + /* load and validate nxip */ | ||
| 475 | nxip = pq_getmsgint(buf, 4); | ||
| 476 | - avail = buf->len - buf->cursor; | ||
| 477 | - expect = 8 + 8 + nxip * 8; | ||
| 478 | - if (nxip < 0 || nxip > avail || expect > avail) | ||
| 479 | + if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP) | ||
| 480 | goto bad_format; | ||
| 481 | |||
| 482 | xmin = pq_getmsgint64(buf); | ||
| 483 | diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c | ||
| 484 | index 2bcf5b8..0deefda 100644 | ||
| 485 | --- a/src/backend/utils/adt/varbit.c | ||
| 486 | +++ b/src/backend/utils/adt/varbit.c | ||
| 487 | @@ -148,12 +148,22 @@ bit_in(PG_FUNCTION_ARGS) | ||
| 488 | sp = input_string; | ||
| 489 | } | ||
| 490 | |||
| 491 | + /* | ||
| 492 | + * Determine bitlength from input string. MaxAllocSize ensures a regular | ||
| 493 | + * input is small enough, but we must check hex input. | ||
| 494 | + */ | ||
| 495 | slen = strlen(sp); | ||
| 496 | - /* Determine bitlength from input string */ | ||
| 497 | if (bit_not_hex) | ||
| 498 | bitlen = slen; | ||
| 499 | else | ||
| 500 | + { | ||
| 501 | + if (slen > VARBITMAXLEN / 4) | ||
| 502 | + ereport(ERROR, | ||
| 503 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 504 | + errmsg("bit string length exceeds the maximum allowed (%d)", | ||
| 505 | + VARBITMAXLEN))); | ||
| 506 | bitlen = slen * 4; | ||
| 507 | + } | ||
| 508 | |||
| 509 | /* | ||
| 510 | * Sometimes atttypmod is not supplied. If it is supplied we need to make | ||
| 511 | @@ -450,12 +460,22 @@ varbit_in(PG_FUNCTION_ARGS) | ||
| 512 | sp = input_string; | ||
| 513 | } | ||
| 514 | |||
| 515 | + /* | ||
| 516 | + * Determine bitlength from input string. MaxAllocSize ensures a regular | ||
| 517 | + * input is small enough, but we must check hex input. | ||
| 518 | + */ | ||
| 519 | slen = strlen(sp); | ||
| 520 | - /* Determine bitlength from input string */ | ||
| 521 | if (bit_not_hex) | ||
| 522 | bitlen = slen; | ||
| 523 | else | ||
| 524 | + { | ||
| 525 | + if (slen > VARBITMAXLEN / 4) | ||
| 526 | + ereport(ERROR, | ||
| 527 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 528 | + errmsg("bit string length exceeds the maximum allowed (%d)", | ||
| 529 | + VARBITMAXLEN))); | ||
| 530 | bitlen = slen * 4; | ||
| 531 | + } | ||
| 532 | |||
| 533 | /* | ||
| 534 | * Sometimes atttypmod is not supplied. If it is supplied we need to make | ||
| 535 | @@ -535,6 +555,9 @@ varbit_in(PG_FUNCTION_ARGS) | ||
| 536 | /* | ||
| 537 | * varbit_out - | ||
| 538 | * Prints the string as bits to preserve length accurately | ||
| 539 | + * | ||
| 540 | + * XXX varbit_recv() and hex input to varbit_in() can load a value that this | ||
| 541 | + * cannot emit. Consider using hex output for such values. | ||
| 542 | */ | ||
| 543 | Datum | ||
| 544 | varbit_out(PG_FUNCTION_ARGS) | ||
| 545 | @@ -944,6 +967,11 @@ bit_catenate(VarBit *arg1, VarBit *arg2) | ||
| 546 | bitlen1 = VARBITLEN(arg1); | ||
| 547 | bitlen2 = VARBITLEN(arg2); | ||
| 548 | |||
| 549 | + if (bitlen1 > VARBITMAXLEN - bitlen2) | ||
| 550 | + ereport(ERROR, | ||
| 551 | + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), | ||
| 552 | + errmsg("bit string length exceeds the maximum allowed (%d)", | ||
| 553 | + VARBITMAXLEN))); | ||
| 554 | bytelen = VARBITTOTALLEN(bitlen1 + bitlen2); | ||
| 555 | |||
| 556 | result = (VarBit *) palloc(bytelen); | ||
| 557 | diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h | ||
| 558 | index 3adc336..9ee5610 100644 | ||
| 559 | --- a/src/include/tsearch/ts_type.h | ||
| 560 | +++ b/src/include/tsearch/ts_type.h | ||
| 561 | @@ -13,6 +13,7 @@ | ||
| 562 | #define _PG_TSTYPE_H_ | ||
| 563 | |||
| 564 | #include "fmgr.h" | ||
| 565 | +#include "utils/memutils.h" | ||
| 566 | #include "utils/pg_crc.h" | ||
| 567 | |||
| 568 | |||
| 569 | @@ -244,6 +245,8 @@ typedef TSQueryData *TSQuery; | ||
| 570 | * QueryItems, and lenofoperand is the total length of all operands | ||
| 571 | */ | ||
| 572 | #define COMPUTESIZE(size, lenofoperand) ( HDRSIZETQ + (size) * sizeof(QueryItem) + (lenofoperand) ) | ||
| 573 | +#define TSQUERY_TOO_BIG(size, lenofoperand) \ | ||
| 574 | + ((size) > (MaxAllocSize - HDRSIZETQ - (lenofoperand)) / sizeof(QueryItem)) | ||
| 575 | |||
| 576 | /* Returns a pointer to the first QueryItem in a TSQuery */ | ||
| 577 | #define GETQUERY(x) ((QueryItem*)( (char*)(x)+HDRSIZETQ )) | ||
| 578 | diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h | ||
| 579 | index 52dca8b..61531a8 100644 | ||
| 580 | --- a/src/include/utils/varbit.h | ||
| 581 | +++ b/src/include/utils/varbit.h | ||
| 582 | @@ -15,6 +15,8 @@ | ||
| 583 | #ifndef VARBIT_H | ||
| 584 | #define VARBIT_H | ||
| 585 | |||
| 586 | +#include <limits.h> | ||
| 587 | + | ||
| 588 | #include "fmgr.h" | ||
| 589 | |||
| 590 | /* | ||
| 591 | @@ -53,6 +55,11 @@ typedef struct | ||
| 592 | /* Number of bytes needed to store a bit string of a given length */ | ||
| 593 | #define VARBITTOTALLEN(BITLEN) (((BITLEN) + BITS_PER_BYTE-1)/BITS_PER_BYTE + \ | ||
| 594 | VARHDRSZ + VARBITHDRSZ) | ||
| 595 | +/* | ||
| 596 | + * Maximum number of bits. Several code sites assume no overflow from | ||
| 597 | + * computing bitlen + X; VARBITTOTALLEN() has the largest such X. | ||
| 598 | + */ | ||
| 599 | +#define VARBITMAXLEN (INT_MAX - BITS_PER_BYTE + 1) | ||
| 600 | /* pointer beyond the end of the bit string (like end() in STL containers) */ | ||
| 601 | #define VARBITEND(PTR) (((bits8 *) (PTR)) + VARSIZE(PTR)) | ||
| 602 | /* Mask that will cover exactly one byte, i.e. BITS_PER_BYTE bits */ | ||
| 603 | -- | ||
| 604 | 1.7.5.4 | ||
| 605 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0003-Shore-up-ADMIN-OPTION-restrictions.patch b/meta-oe/recipes-support/postgresql/files/0003-Shore-up-ADMIN-OPTION-restrictions.patch deleted file mode 100644 index abbe142495..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0003-Shore-up-ADMIN-OPTION-restrictions.patch +++ /dev/null | |||
| @@ -1,273 +0,0 @@ | |||
| 1 | From 15a8f97b9d16aaf659f58c981242b9da591cf24c Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Noah Misch <noah@leadboat.com> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Shore up ADMIN OPTION restrictions. | ||
| 5 | |||
| 6 | commit 15a8f97b9d16aaf659f58c981242b9da591cf24c REL9_2_STABLE | ||
| 7 | |||
| 8 | Granting a role without ADMIN OPTION is supposed to prevent the grantee | ||
| 9 | from adding or removing members from the granted role. Issuing SET ROLE | ||
| 10 | before the GRANT bypassed that, because the role itself had an implicit | ||
| 11 | right to add or remove members. Plug that hole by recognizing that | ||
| 12 | implicit right only when the session user matches the current role. | ||
| 13 | Additionally, do not recognize it during a security-restricted operation | ||
| 14 | or during execution of a SECURITY DEFINER function. The restriction on | ||
| 15 | SECURITY DEFINER is not security-critical. However, it seems best for a | ||
| 16 | user testing his own SECURITY DEFINER function to see the same behavior | ||
| 17 | others will see. Back-patch to 8.4 (all supported versions). | ||
| 18 | |||
| 19 | The SQL standards do not conflate roles and users as PostgreSQL does; | ||
| 20 | only SQL roles have members, and only SQL users initiate sessions. An | ||
| 21 | application using PostgreSQL users and roles as SQL users and roles will | ||
| 22 | never attempt to grant membership in the role that is the session user, | ||
| 23 | so the implicit right to add or remove members will never arise. | ||
| 24 | |||
| 25 | The security impact was mostly that a role member could revoke access | ||
| 26 | from others, contrary to the wishes of his own grantor. Unapproved role | ||
| 27 | member additions are less notable, because the member can still largely | ||
| 28 | achieve that by creating a view or a SECURITY DEFINER function. | ||
| 29 | |||
| 30 | Reviewed by Andres Freund and Tom Lane. Reported, independently, by | ||
| 31 | Jonas Sundman and Noah Misch. | ||
| 32 | |||
| 33 | Security: CVE-2014-0060 | ||
| 34 | |||
| 35 | |||
| 36 | Upstream-Status: Backport | ||
| 37 | |||
| 38 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 39 | --- | ||
| 40 | doc/src/sgml/ref/grant.sgml | 12 ++++--- | ||
| 41 | src/backend/commands/user.c | 11 ++++++- | ||
| 42 | src/backend/utils/adt/acl.c | 50 ++++++++++++++++++++++++------ | ||
| 43 | src/test/regress/expected/privileges.out | 36 +++++++++++++++++++++- | ||
| 44 | src/test/regress/sql/privileges.sql | 29 ++++++++++++++++- | ||
| 45 | 5 files changed, 120 insertions(+), 18 deletions(-) | ||
| 46 | |||
| 47 | diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml | ||
| 48 | index fb81af4..2b5a843 100644 | ||
| 49 | --- a/doc/src/sgml/ref/grant.sgml | ||
| 50 | +++ b/doc/src/sgml/ref/grant.sgml | ||
| 51 | @@ -396,11 +396,13 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace | ||
| 52 | <para> | ||
| 53 | If <literal>WITH ADMIN OPTION</literal> is specified, the member can | ||
| 54 | in turn grant membership in the role to others, and revoke membership | ||
| 55 | - in the role as well. Without the admin option, ordinary users cannot do | ||
| 56 | - that. However, | ||
| 57 | - database superusers can grant or revoke membership in any role to anyone. | ||
| 58 | - Roles having <literal>CREATEROLE</> privilege can grant or revoke | ||
| 59 | - membership in any role that is not a superuser. | ||
| 60 | + in the role as well. Without the admin option, ordinary users cannot | ||
| 61 | + do that. A role is not considered to hold <literal>WITH ADMIN | ||
| 62 | + OPTION</literal> on itself, but it may grant or revoke membership in | ||
| 63 | + itself from a database session where the session user matches the | ||
| 64 | + role. Database superusers can grant or revoke membership in any role | ||
| 65 | + to anyone. Roles having <literal>CREATEROLE</> privilege can grant | ||
| 66 | + or revoke membership in any role that is not a superuser. | ||
| 67 | </para> | ||
| 68 | |||
| 69 | <para> | ||
| 70 | diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c | ||
| 71 | index a22092c..39bf172 100644 | ||
| 72 | --- a/src/backend/commands/user.c | ||
| 73 | +++ b/src/backend/commands/user.c | ||
| 74 | @@ -1334,7 +1334,16 @@ AddRoleMems(const char *rolename, Oid roleid, | ||
| 75 | rolename))); | ||
| 76 | } | ||
| 77 | |||
| 78 | - /* XXX not sure about this check */ | ||
| 79 | + /* | ||
| 80 | + * The role membership grantor of record has little significance at | ||
| 81 | + * present. Nonetheless, inasmuch as users might look to it for a crude | ||
| 82 | + * audit trail, let only superusers impute the grant to a third party. | ||
| 83 | + * | ||
| 84 | + * Before lifting this restriction, give the member == role case of | ||
| 85 | + * is_admin_of_role() a fresh look. Ensure that the current role cannot | ||
| 86 | + * use an explicit grantor specification to take advantage of the session | ||
| 87 | + * user's self-admin right. | ||
| 88 | + */ | ||
| 89 | if (grantorId != GetUserId() && !superuser()) | ||
| 90 | ereport(ERROR, | ||
| 91 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 92 | diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c | ||
| 93 | index 1d6ae8b..9a52edb 100644 | ||
| 94 | --- a/src/backend/utils/adt/acl.c | ||
| 95 | +++ b/src/backend/utils/adt/acl.c | ||
| 96 | @@ -4580,6 +4580,11 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) | ||
| 97 | { | ||
| 98 | if (mode & ACL_GRANT_OPTION_FOR(ACL_CREATE)) | ||
| 99 | { | ||
| 100 | + /* | ||
| 101 | + * XXX For roleid == role_oid, is_admin_of_role() also examines the | ||
| 102 | + * session and call stack. That suits two-argument pg_has_role(), but | ||
| 103 | + * it gives the three-argument version a lamentable whimsy. | ||
| 104 | + */ | ||
| 105 | if (is_admin_of_role(roleid, role_oid)) | ||
| 106 | return ACLCHECK_OK; | ||
| 107 | } | ||
| 108 | @@ -4897,11 +4902,9 @@ is_member_of_role_nosuper(Oid member, Oid role) | ||
| 109 | |||
| 110 | |||
| 111 | /* | ||
| 112 | - * Is member an admin of role (directly or indirectly)? That is, is it | ||
| 113 | - * a member WITH ADMIN OPTION? | ||
| 114 | - * | ||
| 115 | - * We could cache the result as for is_member_of_role, but currently this | ||
| 116 | - * is not used in any performance-critical paths, so we don't. | ||
| 117 | + * Is member an admin of role? That is, is member the role itself (subject to | ||
| 118 | + * restrictions below), a member (directly or indirectly) WITH ADMIN OPTION, | ||
| 119 | + * or a superuser? | ||
| 120 | */ | ||
| 121 | bool | ||
| 122 | is_admin_of_role(Oid member, Oid role) | ||
| 123 | @@ -4910,14 +4913,41 @@ is_admin_of_role(Oid member, Oid role) | ||
| 124 | List *roles_list; | ||
| 125 | ListCell *l; | ||
| 126 | |||
| 127 | - /* Fast path for simple case */ | ||
| 128 | - if (member == role) | ||
| 129 | - return true; | ||
| 130 | - | ||
| 131 | - /* Superusers have every privilege, so are part of every role */ | ||
| 132 | if (superuser_arg(member)) | ||
| 133 | return true; | ||
| 134 | |||
| 135 | + if (member == role) | ||
| 136 | + /* | ||
| 137 | + * A role can admin itself when it matches the session user and we're | ||
| 138 | + * outside any security-restricted operation, SECURITY DEFINER or | ||
| 139 | + * similar context. SQL-standard roles cannot self-admin. However, | ||
| 140 | + * SQL-standard users are distinct from roles, and they are not | ||
| 141 | + * grantable like roles: PostgreSQL's role-user duality extends the | ||
| 142 | + * standard. Checking for a session user match has the effect of | ||
| 143 | + * letting a role self-admin only when it's conspicuously behaving | ||
| 144 | + * like a user. Note that allowing self-admin under a mere SET ROLE | ||
| 145 | + * would make WITH ADMIN OPTION largely irrelevant; any member could | ||
| 146 | + * SET ROLE to issue the otherwise-forbidden command. | ||
| 147 | + * | ||
| 148 | + * Withholding self-admin in a security-restricted operation prevents | ||
| 149 | + * object owners from harnessing the session user identity during | ||
| 150 | + * administrative maintenance. Suppose Alice owns a database, has | ||
| 151 | + * issued "GRANT alice TO bob", and runs a daily ANALYZE. Bob creates | ||
| 152 | + * an alice-owned SECURITY DEFINER function that issues "REVOKE alice | ||
| 153 | + * FROM carol". If he creates an expression index calling that | ||
| 154 | + * function, Alice will attempt the REVOKE during each ANALYZE. | ||
| 155 | + * Checking InSecurityRestrictedOperation() thwarts that attack. | ||
| 156 | + * | ||
| 157 | + * Withholding self-admin in SECURITY DEFINER functions makes their | ||
| 158 | + * behavior independent of the calling user. There's no security or | ||
| 159 | + * SQL-standard-conformance need for that restriction, though. | ||
| 160 | + * | ||
| 161 | + * A role cannot have actual WITH ADMIN OPTION on itself, because that | ||
| 162 | + * would imply a membership loop. Therefore, we're done either way. | ||
| 163 | + */ | ||
| 164 | + return member == GetSessionUserId() && | ||
| 165 | + !InLocalUserIdChange() && !InSecurityRestrictedOperation(); | ||
| 166 | + | ||
| 167 | /* | ||
| 168 | * Find all the roles that member is a member of, including multi-level | ||
| 169 | * recursion. We build a list in the same way that is_member_of_role does | ||
| 170 | diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out | ||
| 171 | index e8930cb..bc6d731 100644 | ||
| 172 | --- a/src/test/regress/expected/privileges.out | ||
| 173 | +++ b/src/test/regress/expected/privileges.out | ||
| 174 | @@ -32,7 +32,7 @@ ALTER GROUP regressgroup1 ADD USER regressuser4; | ||
| 175 | ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate | ||
| 176 | NOTICE: role "regressuser2" is already a member of role "regressgroup2" | ||
| 177 | ALTER GROUP regressgroup2 DROP USER regressuser2; | ||
| 178 | -ALTER GROUP regressgroup2 ADD USER regressuser4; | ||
| 179 | +GRANT regressgroup2 TO regressuser4 WITH ADMIN OPTION; | ||
| 180 | -- test owner privileges | ||
| 181 | SET SESSION AUTHORIZATION regressuser1; | ||
| 182 | SELECT session_user, current_user; | ||
| 183 | @@ -929,6 +929,40 @@ SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION') | ||
| 184 | t | ||
| 185 | (1 row) | ||
| 186 | |||
| 187 | +-- Admin options | ||
| 188 | +SET SESSION AUTHORIZATION regressuser4; | ||
| 189 | +CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS | ||
| 190 | + 'GRANT regressgroup2 TO regressuser5'; | ||
| 191 | +GRANT regressgroup2 TO regressuser5; -- ok: had ADMIN OPTION | ||
| 192 | +SET ROLE regressgroup2; | ||
| 193 | +GRANT regressgroup2 TO regressuser5; -- fails: SET ROLE suspended privilege | ||
| 194 | +ERROR: must have admin option on role "regressgroup2" | ||
| 195 | +SET SESSION AUTHORIZATION regressuser1; | ||
| 196 | +GRANT regressgroup2 TO regressuser5; -- fails: no ADMIN OPTION | ||
| 197 | +ERROR: must have admin option on role "regressgroup2" | ||
| 198 | +SELECT dogrant_ok(); -- ok: SECURITY DEFINER conveys ADMIN | ||
| 199 | +NOTICE: role "regressuser5" is already a member of role "regressgroup2" | ||
| 200 | +CONTEXT: SQL function "dogrant_ok" statement 1 | ||
| 201 | + dogrant_ok | ||
| 202 | +------------ | ||
| 203 | + | ||
| 204 | +(1 row) | ||
| 205 | + | ||
| 206 | +SET ROLE regressgroup2; | ||
| 207 | +GRANT regressgroup2 TO regressuser5; -- fails: SET ROLE did not help | ||
| 208 | +ERROR: must have admin option on role "regressgroup2" | ||
| 209 | +SET SESSION AUTHORIZATION regressgroup2; | ||
| 210 | +GRANT regressgroup2 TO regressuser5; -- ok: a role can self-admin | ||
| 211 | +NOTICE: role "regressuser5" is already a member of role "regressgroup2" | ||
| 212 | +CREATE FUNCTION dogrant_fails() RETURNS void LANGUAGE sql SECURITY DEFINER AS | ||
| 213 | + 'GRANT regressgroup2 TO regressuser5'; | ||
| 214 | +SELECT dogrant_fails(); -- fails: no self-admin in SECURITY DEFINER | ||
| 215 | +ERROR: must have admin option on role "regressgroup2" | ||
| 216 | +CONTEXT: SQL function "dogrant_fails" statement 1 | ||
| 217 | +DROP FUNCTION dogrant_fails(); | ||
| 218 | +SET SESSION AUTHORIZATION regressuser4; | ||
| 219 | +DROP FUNCTION dogrant_ok(); | ||
| 220 | +REVOKE regressgroup2 FROM regressuser5; | ||
| 221 | -- has_sequence_privilege tests | ||
| 222 | \c - | ||
| 223 | CREATE SEQUENCE x_seq; | ||
| 224 | diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql | ||
| 225 | index d4d328e..5f1018a 100644 | ||
| 226 | --- a/src/test/regress/sql/privileges.sql | ||
| 227 | +++ b/src/test/regress/sql/privileges.sql | ||
| 228 | @@ -37,7 +37,7 @@ ALTER GROUP regressgroup1 ADD USER regressuser4; | ||
| 229 | |||
| 230 | ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate | ||
| 231 | ALTER GROUP regressgroup2 DROP USER regressuser2; | ||
| 232 | -ALTER GROUP regressgroup2 ADD USER regressuser4; | ||
| 233 | +GRANT regressgroup2 TO regressuser4 WITH ADMIN OPTION; | ||
| 234 | |||
| 235 | -- test owner privileges | ||
| 236 | |||
| 237 | @@ -581,6 +581,33 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false | ||
| 238 | SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true | ||
| 239 | |||
| 240 | |||
| 241 | +-- Admin options | ||
| 242 | + | ||
| 243 | +SET SESSION AUTHORIZATION regressuser4; | ||
| 244 | +CREATE FUNCTION dogrant_ok() RETURNS void LANGUAGE sql SECURITY DEFINER AS | ||
| 245 | + 'GRANT regressgroup2 TO regressuser5'; | ||
| 246 | +GRANT regressgroup2 TO regressuser5; -- ok: had ADMIN OPTION | ||
| 247 | +SET ROLE regressgroup2; | ||
| 248 | +GRANT regressgroup2 TO regressuser5; -- fails: SET ROLE suspended privilege | ||
| 249 | + | ||
| 250 | +SET SESSION AUTHORIZATION regressuser1; | ||
| 251 | +GRANT regressgroup2 TO regressuser5; -- fails: no ADMIN OPTION | ||
| 252 | +SELECT dogrant_ok(); -- ok: SECURITY DEFINER conveys ADMIN | ||
| 253 | +SET ROLE regressgroup2; | ||
| 254 | +GRANT regressgroup2 TO regressuser5; -- fails: SET ROLE did not help | ||
| 255 | + | ||
| 256 | +SET SESSION AUTHORIZATION regressgroup2; | ||
| 257 | +GRANT regressgroup2 TO regressuser5; -- ok: a role can self-admin | ||
| 258 | +CREATE FUNCTION dogrant_fails() RETURNS void LANGUAGE sql SECURITY DEFINER AS | ||
| 259 | + 'GRANT regressgroup2 TO regressuser5'; | ||
| 260 | +SELECT dogrant_fails(); -- fails: no self-admin in SECURITY DEFINER | ||
| 261 | +DROP FUNCTION dogrant_fails(); | ||
| 262 | + | ||
| 263 | +SET SESSION AUTHORIZATION regressuser4; | ||
| 264 | +DROP FUNCTION dogrant_ok(); | ||
| 265 | +REVOKE regressgroup2 FROM regressuser5; | ||
| 266 | + | ||
| 267 | + | ||
| 268 | -- has_sequence_privilege tests | ||
| 269 | \c - | ||
| 270 | |||
| 271 | -- | ||
| 272 | 1.7.5.4 | ||
| 273 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0004-Prevent-privilege-escalation-in-explicit-calls-to-PL.patch b/meta-oe/recipes-support/postgresql/files/0004-Prevent-privilege-escalation-in-explicit-calls-to-PL.patch deleted file mode 100644 index cc2183a2ab..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0004-Prevent-privilege-escalation-in-explicit-calls-to-PL.patch +++ /dev/null | |||
| @@ -1,267 +0,0 @@ | |||
| 1 | From 1d701d28a796ea2d1a4d2be9e9ee06209eaea040 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Noah Misch <noah@leadboat.com> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Prevent privilege escalation in explicit calls to PL | ||
| 5 | validators. | ||
| 6 | |||
| 7 | commit 1d701d28a796ea2d1a4d2be9e9ee06209eaea040 REL9_2_STABLE | ||
| 8 | |||
| 9 | The primary role of PL validators is to be called implicitly during | ||
| 10 | CREATE FUNCTION, but they are also normal functions that a user can call | ||
| 11 | explicitly. Add a permissions check to each validator to ensure that a | ||
| 12 | user cannot use explicit validator calls to achieve things he could not | ||
| 13 | otherwise achieve. Back-patch to 8.4 (all supported versions). | ||
| 14 | Non-core procedural language extensions ought to make the same two-line | ||
| 15 | change to their own validators. | ||
| 16 | |||
| 17 | Andres Freund, reviewed by Tom Lane and Noah Misch. | ||
| 18 | |||
| 19 | Security: CVE-2014-0061 | ||
| 20 | |||
| 21 | Upstream-Status: Backport | ||
| 22 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 23 | --- | ||
| 24 | doc/src/sgml/plhandler.sgml | 5 ++- | ||
| 25 | src/backend/catalog/pg_proc.c | 9 ++++ | ||
| 26 | src/backend/commands/functioncmds.c | 1 - | ||
| 27 | src/backend/utils/fmgr/fmgr.c | 84 +++++++++++++++++++++++++++++++++++ | ||
| 28 | src/include/fmgr.h | 1 + | ||
| 29 | src/pl/plperl/plperl.c | 4 ++ | ||
| 30 | src/pl/plpgsql/src/pl_handler.c | 3 + | ||
| 31 | src/pl/plpython/plpy_main.c | 4 ++ | ||
| 32 | 8 files changed, 109 insertions(+), 2 deletions(-) | ||
| 33 | |||
| 34 | diff --git a/doc/src/sgml/plhandler.sgml b/doc/src/sgml/plhandler.sgml | ||
| 35 | index 024ef9d..aa4bba3 100644 | ||
| 36 | --- a/doc/src/sgml/plhandler.sgml | ||
| 37 | +++ b/doc/src/sgml/plhandler.sgml | ||
| 38 | @@ -178,7 +178,10 @@ CREATE LANGUAGE plsample | ||
| 39 | or updated a function written in the procedural language. | ||
| 40 | The passed-in OID is the OID of the function's <classname>pg_proc</> | ||
| 41 | row. The validator must fetch this row in the usual way, and do | ||
| 42 | - whatever checking is appropriate. Typical checks include verifying | ||
| 43 | + whatever checking is appropriate. | ||
| 44 | + First, call <function>CheckFunctionValidatorAccess()</> to diagnose | ||
| 45 | + explicit calls to the validator that the user could not achieve through | ||
| 46 | + <command>CREATE FUNCTION</>. Typical checks then include verifying | ||
| 47 | that the function's argument and result types are supported by the | ||
| 48 | language, and that the function's body is syntactically correct | ||
| 49 | in the language. If the validator finds the function to be okay, | ||
| 50 | diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c | ||
| 51 | index 3812408..3124868 100644 | ||
| 52 | --- a/src/backend/catalog/pg_proc.c | ||
| 53 | +++ b/src/backend/catalog/pg_proc.c | ||
| 54 | @@ -718,6 +718,9 @@ fmgr_internal_validator(PG_FUNCTION_ARGS) | ||
| 55 | Datum tmp; | ||
| 56 | char *prosrc; | ||
| 57 | |||
| 58 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 59 | + PG_RETURN_VOID(); | ||
| 60 | + | ||
| 61 | /* | ||
| 62 | * We do not honor check_function_bodies since it's unlikely the function | ||
| 63 | * name will be found later if it isn't there now. | ||
| 64 | @@ -763,6 +766,9 @@ fmgr_c_validator(PG_FUNCTION_ARGS) | ||
| 65 | char *prosrc; | ||
| 66 | char *probin; | ||
| 67 | |||
| 68 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 69 | + PG_RETURN_VOID(); | ||
| 70 | + | ||
| 71 | /* | ||
| 72 | * It'd be most consistent to skip the check if !check_function_bodies, | ||
| 73 | * but the purpose of that switch is to be helpful for pg_dump loading, | ||
| 74 | @@ -814,6 +820,9 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) | ||
| 75 | bool haspolyarg; | ||
| 76 | int i; | ||
| 77 | |||
| 78 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 79 | + PG_RETURN_VOID(); | ||
| 80 | + | ||
| 81 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); | ||
| 82 | if (!HeapTupleIsValid(tuple)) | ||
| 83 | elog(ERROR, "cache lookup failed for function %u", funcoid); | ||
| 84 | diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c | ||
| 85 | index 9ba6dd8..ea74b5e 100644 | ||
| 86 | --- a/src/backend/commands/functioncmds.c | ||
| 87 | +++ b/src/backend/commands/functioncmds.c | ||
| 88 | @@ -997,7 +997,6 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) | ||
| 89 | prorows); | ||
| 90 | } | ||
| 91 | |||
| 92 | - | ||
| 93 | /* | ||
| 94 | * Guts of function deletion. | ||
| 95 | * | ||
| 96 | diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c | ||
| 97 | index 2ec63fa..8d6f183 100644 | ||
| 98 | --- a/src/backend/utils/fmgr/fmgr.c | ||
| 99 | +++ b/src/backend/utils/fmgr/fmgr.c | ||
| 100 | @@ -24,6 +24,7 @@ | ||
| 101 | #include "miscadmin.h" | ||
| 102 | #include "nodes/nodeFuncs.h" | ||
| 103 | #include "pgstat.h" | ||
| 104 | +#include "utils/acl.h" | ||
| 105 | #include "utils/builtins.h" | ||
| 106 | #include "utils/fmgrtab.h" | ||
| 107 | #include "utils/guc.h" | ||
| 108 | @@ -2445,3 +2446,86 @@ get_call_expr_arg_stable(Node *expr, int argnum) | ||
| 109 | |||
| 110 | return false; | ||
| 111 | } | ||
| 112 | + | ||
| 113 | +/*------------------------------------------------------------------------- | ||
| 114 | + * Support routines for procedural language implementations | ||
| 115 | + *------------------------------------------------------------------------- | ||
| 116 | + */ | ||
| 117 | + | ||
| 118 | +/* | ||
| 119 | + * Verify that a validator is actually associated with the language of a | ||
| 120 | + * particular function and that the user has access to both the language and | ||
| 121 | + * the function. All validators should call this before doing anything | ||
| 122 | + * substantial. Doing so ensures a user cannot achieve anything with explicit | ||
| 123 | + * calls to validators that he could not achieve with CREATE FUNCTION or by | ||
| 124 | + * simply calling an existing function. | ||
| 125 | + * | ||
| 126 | + * When this function returns false, callers should skip all validation work | ||
| 127 | + * and call PG_RETURN_VOID(). This never happens at present; it is reserved | ||
| 128 | + * for future expansion. | ||
| 129 | + * | ||
| 130 | + * In particular, checking that the validator corresponds to the function's | ||
| 131 | + * language allows untrusted language validators to assume they process only | ||
| 132 | + * superuser-chosen source code. (Untrusted language call handlers, by | ||
| 133 | + * definition, do assume that.) A user lacking the USAGE language privilege | ||
| 134 | + * would be unable to reach the validator through CREATE FUNCTION, so we check | ||
| 135 | + * that to block explicit calls as well. Checking the EXECUTE privilege on | ||
| 136 | + * the function is often superfluous, because most users can clone the | ||
| 137 | + * function to get an executable copy. It is meaningful against users with no | ||
| 138 | + * database TEMP right and no permanent schema CREATE right, thereby unable to | ||
| 139 | + * create any function. Also, if the function tracks persistent state by | ||
| 140 | + * function OID or name, validating the original function might permit more | ||
| 141 | + * mischief than creating and validating a clone thereof. | ||
| 142 | + */ | ||
| 143 | +bool | ||
| 144 | +CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid) | ||
| 145 | +{ | ||
| 146 | + HeapTuple procTup; | ||
| 147 | + HeapTuple langTup; | ||
| 148 | + Form_pg_proc procStruct; | ||
| 149 | + Form_pg_language langStruct; | ||
| 150 | + AclResult aclresult; | ||
| 151 | + | ||
| 152 | + /* Get the function's pg_proc entry */ | ||
| 153 | + procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(functionOid)); | ||
| 154 | + if (!HeapTupleIsValid(procTup)) | ||
| 155 | + elog(ERROR, "cache lookup failed for function %u", functionOid); | ||
| 156 | + procStruct = (Form_pg_proc) GETSTRUCT(procTup); | ||
| 157 | + | ||
| 158 | + /* | ||
| 159 | + * Fetch pg_language entry to know if this is the correct validation | ||
| 160 | + * function for that pg_proc entry. | ||
| 161 | + */ | ||
| 162 | + langTup = SearchSysCache1(LANGOID, ObjectIdGetDatum(procStruct->prolang)); | ||
| 163 | + if (!HeapTupleIsValid(langTup)) | ||
| 164 | + elog(ERROR, "cache lookup failed for language %u", procStruct->prolang); | ||
| 165 | + langStruct = (Form_pg_language) GETSTRUCT(langTup); | ||
| 166 | + | ||
| 167 | + if (langStruct->lanvalidator != validatorOid) | ||
| 168 | + ereport(ERROR, | ||
| 169 | + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 170 | + errmsg("language validation function %u called for language %u instead of %u", | ||
| 171 | + validatorOid, procStruct->prolang, | ||
| 172 | + langStruct->lanvalidator))); | ||
| 173 | + | ||
| 174 | + /* first validate that we have permissions to use the language */ | ||
| 175 | + aclresult = pg_language_aclcheck(procStruct->prolang, GetUserId(), | ||
| 176 | + ACL_USAGE); | ||
| 177 | + if (aclresult != ACLCHECK_OK) | ||
| 178 | + aclcheck_error(aclresult, ACL_KIND_LANGUAGE, | ||
| 179 | + NameStr(langStruct->lanname)); | ||
| 180 | + | ||
| 181 | + /* | ||
| 182 | + * Check whether we are allowed to execute the function itself. If we can | ||
| 183 | + * execute it, there should be no possible side-effect of | ||
| 184 | + * compiling/validation that execution can't have. | ||
| 185 | + */ | ||
| 186 | + aclresult = pg_proc_aclcheck(functionOid, GetUserId(), ACL_EXECUTE); | ||
| 187 | + if (aclresult != ACLCHECK_OK) | ||
| 188 | + aclcheck_error(aclresult, ACL_KIND_PROC, NameStr(procStruct->proname)); | ||
| 189 | + | ||
| 190 | + ReleaseSysCache(procTup); | ||
| 191 | + ReleaseSysCache(langTup); | ||
| 192 | + | ||
| 193 | + return true; | ||
| 194 | +} | ||
| 195 | diff --git a/src/include/fmgr.h b/src/include/fmgr.h | ||
| 196 | index 0a25776..f944cc6 100644 | ||
| 197 | --- a/src/include/fmgr.h | ||
| 198 | +++ b/src/include/fmgr.h | ||
| 199 | @@ -624,6 +624,7 @@ extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum); | ||
| 200 | extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum); | ||
| 201 | extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum); | ||
| 202 | extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum); | ||
| 203 | +extern bool CheckFunctionValidatorAccess(Oid validatorOid, Oid functionOid); | ||
| 204 | |||
| 205 | /* | ||
| 206 | * Routines in dfmgr.c | ||
| 207 | diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c | ||
| 208 | index 7c2aee9..49d50c4 100644 | ||
| 209 | --- a/src/pl/plperl/plperl.c | ||
| 210 | +++ b/src/pl/plperl/plperl.c | ||
| 211 | @@ -1847,6 +1847,9 @@ plperl_validator(PG_FUNCTION_ARGS) | ||
| 212 | bool istrigger = false; | ||
| 213 | int i; | ||
| 214 | |||
| 215 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 216 | + PG_RETURN_VOID(); | ||
| 217 | + | ||
| 218 | /* Get the new function's pg_proc entry */ | ||
| 219 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); | ||
| 220 | if (!HeapTupleIsValid(tuple)) | ||
| 221 | @@ -1926,6 +1929,7 @@ PG_FUNCTION_INFO_V1(plperlu_validator); | ||
| 222 | Datum | ||
| 223 | plperlu_validator(PG_FUNCTION_ARGS) | ||
| 224 | { | ||
| 225 | + /* call plperl validator with our fcinfo so it gets our oid */ | ||
| 226 | return plperl_validator(fcinfo); | ||
| 227 | } | ||
| 228 | |||
| 229 | diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c | ||
| 230 | index 022ec3f..00b1a6f 100644 | ||
| 231 | --- a/src/pl/plpgsql/src/pl_handler.c | ||
| 232 | +++ b/src/pl/plpgsql/src/pl_handler.c | ||
| 233 | @@ -227,6 +227,9 @@ plpgsql_validator(PG_FUNCTION_ARGS) | ||
| 234 | bool istrigger = false; | ||
| 235 | int i; | ||
| 236 | |||
| 237 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 238 | + PG_RETURN_VOID(); | ||
| 239 | + | ||
| 240 | /* Get the new function's pg_proc entry */ | ||
| 241 | tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcoid)); | ||
| 242 | if (!HeapTupleIsValid(tuple)) | ||
| 243 | diff --git a/src/pl/plpython/plpy_main.c b/src/pl/plpython/plpy_main.c | ||
| 244 | index c4de762..3847847 100644 | ||
| 245 | --- a/src/pl/plpython/plpy_main.c | ||
| 246 | +++ b/src/pl/plpython/plpy_main.c | ||
| 247 | @@ -159,6 +159,9 @@ plpython_validator(PG_FUNCTION_ARGS) | ||
| 248 | Form_pg_proc procStruct; | ||
| 249 | bool is_trigger; | ||
| 250 | |||
| 251 | + if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid)) | ||
| 252 | + PG_RETURN_VOID(); | ||
| 253 | + | ||
| 254 | if (!check_function_bodies) | ||
| 255 | { | ||
| 256 | PG_RETURN_VOID(); | ||
| 257 | @@ -184,6 +187,7 @@ plpython_validator(PG_FUNCTION_ARGS) | ||
| 258 | Datum | ||
| 259 | plpython2_validator(PG_FUNCTION_ARGS) | ||
| 260 | { | ||
| 261 | + /* call plpython validator with our fcinfo so it gets our oid */ | ||
| 262 | return plpython_validator(fcinfo); | ||
| 263 | } | ||
| 264 | #endif /* PY_MAJOR_VERSION < 3 */ | ||
| 265 | -- | ||
| 266 | 1.7.5.4 | ||
| 267 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch b/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch deleted file mode 100644 index f1aa212502..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch +++ /dev/null | |||
| @@ -1,1082 +0,0 @@ | |||
| 1 | From 820ab11fbfd508fc75a39c43ad2c1b3e79c4982b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Robert Haas <rhaas@postgresql.org> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Avoid repeated name lookups during table and index DDL. | ||
| 5 | |||
| 6 | commit 820ab11fbfd508fc75a39c43ad2c1b3e79c4982b REL9_2_STABLE | ||
| 7 | |||
| 8 | If the name lookups come to different conclusions due to concurrent | ||
| 9 | activity, we might perform some parts of the DDL on a different table | ||
| 10 | than other parts. At least in the case of CREATE INDEX, this can be | ||
| 11 | used to cause the permissions checks to be performed against a | ||
| 12 | different table than the index creation, allowing for a privilege | ||
| 13 | escalation attack. | ||
| 14 | |||
| 15 | This changes the calling convention for DefineIndex, CreateTrigger, | ||
| 16 | transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible | ||
| 17 | (in 9.2 and newer), and AlterTable (in 9.1 and older). In addition, | ||
| 18 | CheckRelationOwnership is removed in 9.2 and newer and the calling | ||
| 19 | convention is changed in older branches. A field has also been added | ||
| 20 | to the Constraint node (FkConstraint in 8.4). Third-party code calling | ||
| 21 | these functions or using the Constraint node will require updating. | ||
| 22 | |||
| 23 | Report by Andres Freund. Patch by Robert Haas and Andres Freund, | ||
| 24 | reviewed by Tom Lane. | ||
| 25 | |||
| 26 | Security: CVE-2014-0062 | ||
| 27 | |||
| 28 | Upstream-Status: Backport | ||
| 29 | |||
| 30 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 31 | --- | ||
| 32 | src/backend/bootstrap/bootparse.y | 17 ++++- | ||
| 33 | src/backend/catalog/index.c | 10 +-- | ||
| 34 | src/backend/catalog/pg_constraint.c | 19 +++++ | ||
| 35 | src/backend/commands/indexcmds.c | 22 ++++-- | ||
| 36 | src/backend/commands/tablecmds.c | 137 +++++++++++++++++++++++++---------- | ||
| 37 | src/backend/commands/trigger.c | 28 ++++++-- | ||
| 38 | src/backend/nodes/copyfuncs.c | 1 + | ||
| 39 | src/backend/nodes/equalfuncs.c | 1 + | ||
| 40 | src/backend/nodes/outfuncs.c | 1 + | ||
| 41 | src/backend/parser/parse_utilcmd.c | 64 ++++++----------- | ||
| 42 | src/backend/tcop/utility.c | 73 +++++++------------ | ||
| 43 | src/include/catalog/pg_constraint.h | 1 + | ||
| 44 | src/include/commands/defrem.h | 4 +- | ||
| 45 | src/include/commands/tablecmds.h | 2 + | ||
| 46 | src/include/commands/trigger.h | 2 +- | ||
| 47 | src/include/nodes/parsenodes.h | 2 + | ||
| 48 | src/include/parser/parse_utilcmd.h | 5 +- | ||
| 49 | src/include/tcop/utility.h | 2 - | ||
| 50 | 18 files changed, 234 insertions(+), 157 deletions(-) | ||
| 51 | |||
| 52 | diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y | ||
| 53 | index f4a1b8f..eeffb0f 100644 | ||
| 54 | --- a/src/backend/bootstrap/bootparse.y | ||
| 55 | +++ b/src/backend/bootstrap/bootparse.y | ||
| 56 | @@ -27,6 +27,7 @@ | ||
| 57 | #include "bootstrap/bootstrap.h" | ||
| 58 | #include "catalog/catalog.h" | ||
| 59 | #include "catalog/heap.h" | ||
| 60 | +#include "catalog/namespace.h" | ||
| 61 | #include "catalog/pg_am.h" | ||
| 62 | #include "catalog/pg_attribute.h" | ||
| 63 | #include "catalog/pg_authid.h" | ||
| 64 | @@ -281,6 +282,7 @@ Boot_DeclareIndexStmt: | ||
| 65 | XDECLARE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN | ||
| 66 | { | ||
| 67 | IndexStmt *stmt = makeNode(IndexStmt); | ||
| 68 | + Oid relationId; | ||
| 69 | |||
| 70 | do_start(); | ||
| 71 | |||
| 72 | @@ -302,7 +304,12 @@ Boot_DeclareIndexStmt: | ||
| 73 | stmt->initdeferred = false; | ||
| 74 | stmt->concurrent = false; | ||
| 75 | |||
| 76 | - DefineIndex(stmt, | ||
| 77 | + /* locks and races need not concern us in bootstrap mode */ | ||
| 78 | + relationId = RangeVarGetRelid(stmt->relation, NoLock, | ||
| 79 | + false); | ||
| 80 | + | ||
| 81 | + DefineIndex(relationId, | ||
| 82 | + stmt, | ||
| 83 | $4, | ||
| 84 | false, | ||
| 85 | false, | ||
| 86 | @@ -316,6 +323,7 @@ Boot_DeclareUniqueIndexStmt: | ||
| 87 | XDECLARE UNIQUE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN | ||
| 88 | { | ||
| 89 | IndexStmt *stmt = makeNode(IndexStmt); | ||
| 90 | + Oid relationId; | ||
| 91 | |||
| 92 | do_start(); | ||
| 93 | |||
| 94 | @@ -337,7 +345,12 @@ Boot_DeclareUniqueIndexStmt: | ||
| 95 | stmt->initdeferred = false; | ||
| 96 | stmt->concurrent = false; | ||
| 97 | |||
| 98 | - DefineIndex(stmt, | ||
| 99 | + /* locks and races need not concern us in bootstrap mode */ | ||
| 100 | + relationId = RangeVarGetRelid(stmt->relation, NoLock, | ||
| 101 | + false); | ||
| 102 | + | ||
| 103 | + DefineIndex(relationId, | ||
| 104 | + stmt, | ||
| 105 | $5, | ||
| 106 | false, | ||
| 107 | false, | ||
| 108 | diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c | ||
| 109 | index 7d6346a..ca8acf3 100644 | ||
| 110 | --- a/src/backend/catalog/index.c | ||
| 111 | +++ b/src/backend/catalog/index.c | ||
| 112 | @@ -1202,18 +1202,13 @@ index_constraint_create(Relation heapRelation, | ||
| 113 | */ | ||
| 114 | if (deferrable) | ||
| 115 | { | ||
| 116 | - RangeVar *heapRel; | ||
| 117 | CreateTrigStmt *trigger; | ||
| 118 | |||
| 119 | - heapRel = makeRangeVar(get_namespace_name(namespaceId), | ||
| 120 | - pstrdup(RelationGetRelationName(heapRelation)), | ||
| 121 | - -1); | ||
| 122 | - | ||
| 123 | trigger = makeNode(CreateTrigStmt); | ||
| 124 | trigger->trigname = (constraintType == CONSTRAINT_PRIMARY) ? | ||
| 125 | "PK_ConstraintTrigger" : | ||
| 126 | "Unique_ConstraintTrigger"; | ||
| 127 | - trigger->relation = heapRel; | ||
| 128 | + trigger->relation = NULL; | ||
| 129 | trigger->funcname = SystemFuncName("unique_key_recheck"); | ||
| 130 | trigger->args = NIL; | ||
| 131 | trigger->row = true; | ||
| 132 | @@ -1226,7 +1221,8 @@ index_constraint_create(Relation heapRelation, | ||
| 133 | trigger->initdeferred = initdeferred; | ||
| 134 | trigger->constrrel = NULL; | ||
| 135 | |||
| 136 | - (void) CreateTrigger(trigger, NULL, conOid, indexRelationId, true); | ||
| 137 | + (void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation), | ||
| 138 | + InvalidOid, conOid, indexRelationId, true); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* | ||
| 142 | diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c | ||
| 143 | index 107a780..08a94cf 100644 | ||
| 144 | --- a/src/backend/catalog/pg_constraint.c | ||
| 145 | +++ b/src/backend/catalog/pg_constraint.c | ||
| 146 | @@ -746,6 +746,25 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, | ||
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | + * get_constraint_relation_oids | ||
| 151 | + * Find the IDs of the relations to which a constraint refers. | ||
| 152 | + */ | ||
| 153 | +void | ||
| 154 | +get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid) | ||
| 155 | +{ | ||
| 156 | + HeapTuple tup; | ||
| 157 | + Form_pg_constraint con; | ||
| 158 | + | ||
| 159 | + tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraint_oid)); | ||
| 160 | + if (!HeapTupleIsValid(tup)) /* should not happen */ | ||
| 161 | + elog(ERROR, "cache lookup failed for constraint %u", constraint_oid); | ||
| 162 | + con = (Form_pg_constraint) GETSTRUCT(tup); | ||
| 163 | + *conrelid = con->conrelid; | ||
| 164 | + *confrelid = con->confrelid; | ||
| 165 | + ReleaseSysCache(tup); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +/* | ||
| 169 | * get_relation_constraint_oid | ||
| 170 | * Find a constraint on the specified relation with the specified name. | ||
| 171 | * Returns constraint's OID. | ||
| 172 | diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c | ||
| 173 | index f3ee278..ec5fb0d 100644 | ||
| 174 | --- a/src/backend/commands/indexcmds.c | ||
| 175 | +++ b/src/backend/commands/indexcmds.c | ||
| 176 | @@ -111,7 +111,6 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation, | ||
| 177 | */ | ||
| 178 | bool | ||
| 179 | CheckIndexCompatible(Oid oldId, | ||
| 180 | - RangeVar *heapRelation, | ||
| 181 | char *accessMethodName, | ||
| 182 | List *attributeList, | ||
| 183 | List *exclusionOpNames) | ||
| 184 | @@ -139,7 +138,7 @@ CheckIndexCompatible(Oid oldId, | ||
| 185 | Datum d; | ||
| 186 | |||
| 187 | /* Caller should already have the relation locked in some way. */ | ||
| 188 | - relationId = RangeVarGetRelid(heapRelation, NoLock, false); | ||
| 189 | + relationId = IndexGetRelation(oldId, false); | ||
| 190 | |||
| 191 | /* | ||
| 192 | * We can pretend isconstraint = false unconditionally. It only serves to | ||
| 193 | @@ -279,6 +278,8 @@ CheckIndexCompatible(Oid oldId, | ||
| 194 | * DefineIndex | ||
| 195 | * Creates a new index. | ||
| 196 | * | ||
| 197 | + * 'relationId': the OID of the heap relation on which the index is to be | ||
| 198 | + * created | ||
| 199 | * 'stmt': IndexStmt describing the properties of the new index. | ||
| 200 | * 'indexRelationId': normally InvalidOid, but during bootstrap can be | ||
| 201 | * nonzero to specify a preselected OID for the index. | ||
| 202 | @@ -292,7 +293,8 @@ CheckIndexCompatible(Oid oldId, | ||
| 203 | * Returns the OID of the created index. | ||
| 204 | */ | ||
| 205 | Oid | ||
| 206 | -DefineIndex(IndexStmt *stmt, | ||
| 207 | +DefineIndex(Oid relationId, | ||
| 208 | + IndexStmt *stmt, | ||
| 209 | Oid indexRelationId, | ||
| 210 | bool is_alter_table, | ||
| 211 | bool check_rights, | ||
| 212 | @@ -305,7 +307,6 @@ DefineIndex(IndexStmt *stmt, | ||
| 213 | Oid *collationObjectId; | ||
| 214 | Oid *classObjectId; | ||
| 215 | Oid accessMethodId; | ||
| 216 | - Oid relationId; | ||
| 217 | Oid namespaceId; | ||
| 218 | Oid tablespaceId; | ||
| 219 | List *indexColNames; | ||
| 220 | @@ -325,6 +326,7 @@ DefineIndex(IndexStmt *stmt, | ||
| 221 | int n_old_snapshots; | ||
| 222 | LockRelId heaprelid; | ||
| 223 | LOCKTAG heaplocktag; | ||
| 224 | + LOCKMODE lockmode; | ||
| 225 | Snapshot snapshot; | ||
| 226 | int i; | ||
| 227 | |||
| 228 | @@ -343,14 +345,18 @@ DefineIndex(IndexStmt *stmt, | ||
| 229 | INDEX_MAX_KEYS))); | ||
| 230 | |||
| 231 | /* | ||
| 232 | - * Open heap relation, acquire a suitable lock on it, remember its OID | ||
| 233 | - * | ||
| 234 | * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard | ||
| 235 | * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE | ||
| 236 | * (but not VACUUM). | ||
| 237 | + * | ||
| 238 | + * NB: Caller is responsible for making sure that relationId refers | ||
| 239 | + * to the relation on which the index should be built; except in bootstrap | ||
| 240 | + * mode, this will typically require the caller to have already locked | ||
| 241 | + * the relation. To avoid lock upgrade hazards, that lock should be at | ||
| 242 | + * least as strong as the one we take here. | ||
| 243 | */ | ||
| 244 | - rel = heap_openrv(stmt->relation, | ||
| 245 | - (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock)); | ||
| 246 | + lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock; | ||
| 247 | + rel = heap_open(relationId, lockmode); | ||
| 248 | |||
| 249 | relationId = RelationGetRelid(rel); | ||
| 250 | namespaceId = RelationGetNamespace(rel); | ||
| 251 | diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c | ||
| 252 | index 7c1f779..bcb81ea 100644 | ||
| 253 | --- a/src/backend/commands/tablecmds.c | ||
| 254 | +++ b/src/backend/commands/tablecmds.c | ||
| 255 | @@ -283,7 +283,8 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup); | ||
| 256 | static void validateForeignKeyConstraint(char *conname, | ||
| 257 | Relation rel, Relation pkrel, | ||
| 258 | Oid pkindOid, Oid constraintOid); | ||
| 259 | -static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 260 | +static void createForeignKeyTriggers(Relation rel, Oid refRelOid, | ||
| 261 | + Constraint *fkconstraint, | ||
| 262 | Oid constraintOid, Oid indexOid); | ||
| 263 | static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); | ||
| 264 | static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, | ||
| 265 | @@ -360,8 +361,9 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, | ||
| 266 | static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName, | ||
| 267 | List *options, LOCKMODE lockmode); | ||
| 268 | static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode); | ||
| 269 | -static void ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 270 | - List **wqueue, LOCKMODE lockmode, bool rewrite); | ||
| 271 | +static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, | ||
| 272 | + char *cmd, List **wqueue, LOCKMODE lockmode, | ||
| 273 | + bool rewrite); | ||
| 274 | static void TryReuseIndex(Oid oldId, IndexStmt *stmt); | ||
| 275 | static void TryReuseForeignKey(Oid oldId, Constraint *con); | ||
| 276 | static void change_owner_fix_column_acls(Oid relationOid, | ||
| 277 | @@ -5406,7 +5408,8 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, | ||
| 278 | |||
| 279 | /* The IndexStmt has already been through transformIndexStmt */ | ||
| 280 | |||
| 281 | - new_index = DefineIndex(stmt, | ||
| 282 | + new_index = DefineIndex(RelationGetRelid(rel), | ||
| 283 | + stmt, | ||
| 284 | InvalidOid, /* no predefined OID */ | ||
| 285 | true, /* is_alter_table */ | ||
| 286 | check_rights, | ||
| 287 | @@ -5728,7 +5731,10 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, | ||
| 288 | * table; trying to start with a lesser lock will just create a risk of | ||
| 289 | * deadlock.) | ||
| 290 | */ | ||
| 291 | - pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); | ||
| 292 | + if (OidIsValid(fkconstraint->old_pktable_oid)) | ||
| 293 | + pkrel = heap_open(fkconstraint->old_pktable_oid, AccessExclusiveLock); | ||
| 294 | + else | ||
| 295 | + pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Validity checks (permission checks wait till we have the column | ||
| 299 | @@ -6066,7 +6072,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, | ||
| 300 | /* | ||
| 301 | * Create the triggers that will enforce the constraint. | ||
| 302 | */ | ||
| 303 | - createForeignKeyTriggers(rel, fkconstraint, constrOid, indexOid); | ||
| 304 | + createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint, | ||
| 305 | + constrOid, indexOid); | ||
| 306 | |||
| 307 | /* | ||
| 308 | * Tell Phase 3 to check that the constraint is satisfied by existing | ||
| 309 | @@ -6736,7 +6743,7 @@ validateForeignKeyConstraint(char *conname, | ||
| 310 | } | ||
| 311 | |||
| 312 | static void | ||
| 313 | -CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 314 | +CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, | ||
| 315 | Oid constraintOid, Oid indexOid, bool on_insert) | ||
| 316 | { | ||
| 317 | CreateTrigStmt *fk_trigger; | ||
| 318 | @@ -6752,7 +6759,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 319 | */ | ||
| 320 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 321 | fk_trigger->trigname = "RI_ConstraintTrigger_c"; | ||
| 322 | - fk_trigger->relation = myRel; | ||
| 323 | + fk_trigger->relation = NULL; | ||
| 324 | fk_trigger->row = true; | ||
| 325 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 326 | |||
| 327 | @@ -6773,10 +6780,11 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 328 | fk_trigger->isconstraint = true; | ||
| 329 | fk_trigger->deferrable = fkconstraint->deferrable; | ||
| 330 | fk_trigger->initdeferred = fkconstraint->initdeferred; | ||
| 331 | - fk_trigger->constrrel = fkconstraint->pktable; | ||
| 332 | + fk_trigger->constrrel = NULL; | ||
| 333 | fk_trigger->args = NIL; | ||
| 334 | |||
| 335 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 336 | + (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid, | ||
| 337 | + indexOid, true); | ||
| 338 | |||
| 339 | /* Make changes-so-far visible */ | ||
| 340 | CommandCounterIncrement(); | ||
| 341 | @@ -6786,18 +6794,13 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 342 | * Create the triggers that implement an FK constraint. | ||
| 343 | */ | ||
| 344 | static void | ||
| 345 | -createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 346 | +createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, | ||
| 347 | Oid constraintOid, Oid indexOid) | ||
| 348 | { | ||
| 349 | - RangeVar *myRel; | ||
| 350 | + Oid myRelOid; | ||
| 351 | CreateTrigStmt *fk_trigger; | ||
| 352 | |||
| 353 | - /* | ||
| 354 | - * Reconstruct a RangeVar for my relation (not passed in, unfortunately). | ||
| 355 | - */ | ||
| 356 | - myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), | ||
| 357 | - pstrdup(RelationGetRelationName(rel)), | ||
| 358 | - -1); | ||
| 359 | + myRelOid = RelationGetRelid(rel); | ||
| 360 | |||
| 361 | /* Make changes-so-far visible */ | ||
| 362 | CommandCounterIncrement(); | ||
| 363 | @@ -6808,14 +6811,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 364 | */ | ||
| 365 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 366 | fk_trigger->trigname = "RI_ConstraintTrigger_a"; | ||
| 367 | - fk_trigger->relation = fkconstraint->pktable; | ||
| 368 | + fk_trigger->relation = NULL; | ||
| 369 | fk_trigger->row = true; | ||
| 370 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 371 | fk_trigger->events = TRIGGER_TYPE_DELETE; | ||
| 372 | fk_trigger->columns = NIL; | ||
| 373 | fk_trigger->whenClause = NULL; | ||
| 374 | fk_trigger->isconstraint = true; | ||
| 375 | - fk_trigger->constrrel = myRel; | ||
| 376 | + fk_trigger->constrrel = NULL; | ||
| 377 | switch (fkconstraint->fk_del_action) | ||
| 378 | { | ||
| 379 | case FKCONSTR_ACTION_NOACTION: | ||
| 380 | @@ -6850,7 +6853,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 381 | } | ||
| 382 | fk_trigger->args = NIL; | ||
| 383 | |||
| 384 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 385 | + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, | ||
| 386 | + indexOid, true); | ||
| 387 | |||
| 388 | /* Make changes-so-far visible */ | ||
| 389 | CommandCounterIncrement(); | ||
| 390 | @@ -6861,14 +6865,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 391 | */ | ||
| 392 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 393 | fk_trigger->trigname = "RI_ConstraintTrigger_a"; | ||
| 394 | - fk_trigger->relation = fkconstraint->pktable; | ||
| 395 | + fk_trigger->relation = NULL; | ||
| 396 | fk_trigger->row = true; | ||
| 397 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 398 | fk_trigger->events = TRIGGER_TYPE_UPDATE; | ||
| 399 | fk_trigger->columns = NIL; | ||
| 400 | fk_trigger->whenClause = NULL; | ||
| 401 | fk_trigger->isconstraint = true; | ||
| 402 | - fk_trigger->constrrel = myRel; | ||
| 403 | + fk_trigger->constrrel = NULL; | ||
| 404 | switch (fkconstraint->fk_upd_action) | ||
| 405 | { | ||
| 406 | case FKCONSTR_ACTION_NOACTION: | ||
| 407 | @@ -6903,7 +6907,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 408 | } | ||
| 409 | fk_trigger->args = NIL; | ||
| 410 | |||
| 411 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 412 | + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, | ||
| 413 | + indexOid, true); | ||
| 414 | |||
| 415 | /* Make changes-so-far visible */ | ||
| 416 | CommandCounterIncrement(); | ||
| 417 | @@ -6912,8 +6917,10 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 418 | * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK | ||
| 419 | * action for both INSERTs and UPDATEs on the referencing table. | ||
| 420 | */ | ||
| 421 | - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, true); | ||
| 422 | - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, false); | ||
| 423 | + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, | ||
| 424 | + indexOid, true); | ||
| 425 | + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, | ||
| 426 | + indexOid, false); | ||
| 427 | } | ||
| 428 | |||
| 429 | /* | ||
| 430 | @@ -7832,15 +7839,36 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) | ||
| 431 | * lock on the table the constraint is attached to, and we need to get | ||
| 432 | * that before dropping. It's safe because the parser won't actually look | ||
| 433 | * at the catalogs to detect the existing entry. | ||
| 434 | + * | ||
| 435 | + * We can't rely on the output of deparsing to tell us which relation | ||
| 436 | + * to operate on, because concurrent activity might have made the name | ||
| 437 | + * resolve differently. Instead, we've got to use the OID of the | ||
| 438 | + * constraint or index we're processing to figure out which relation | ||
| 439 | + * to operate on. | ||
| 440 | */ | ||
| 441 | forboth(oid_item, tab->changedConstraintOids, | ||
| 442 | def_item, tab->changedConstraintDefs) | ||
| 443 | - ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item), | ||
| 444 | + { | ||
| 445 | + Oid oldId = lfirst_oid(oid_item); | ||
| 446 | + Oid relid; | ||
| 447 | + Oid confrelid; | ||
| 448 | + | ||
| 449 | + get_constraint_relation_oids(oldId, &relid, &confrelid); | ||
| 450 | + ATPostAlterTypeParse(oldId, relid, confrelid, | ||
| 451 | + (char *) lfirst(def_item), | ||
| 452 | wqueue, lockmode, tab->rewrite); | ||
| 453 | + } | ||
| 454 | forboth(oid_item, tab->changedIndexOids, | ||
| 455 | def_item, tab->changedIndexDefs) | ||
| 456 | - ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item), | ||
| 457 | + { | ||
| 458 | + Oid oldId = lfirst_oid(oid_item); | ||
| 459 | + Oid relid; | ||
| 460 | + | ||
| 461 | + relid = IndexGetRelation(oldId, false); | ||
| 462 | + ATPostAlterTypeParse(oldId, relid, InvalidOid, | ||
| 463 | + (char *) lfirst(def_item), | ||
| 464 | wqueue, lockmode, tab->rewrite); | ||
| 465 | + } | ||
| 466 | |||
| 467 | /* | ||
| 468 | * Now we can drop the existing constraints and indexes --- constraints | ||
| 469 | @@ -7873,12 +7901,13 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) | ||
| 470 | } | ||
| 471 | |||
| 472 | static void | ||
| 473 | -ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 474 | +ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, | ||
| 475 | List **wqueue, LOCKMODE lockmode, bool rewrite) | ||
| 476 | { | ||
| 477 | List *raw_parsetree_list; | ||
| 478 | List *querytree_list; | ||
| 479 | ListCell *list_item; | ||
| 480 | + Relation rel; | ||
| 481 | |||
| 482 | /* | ||
| 483 | * We expect that we will get only ALTER TABLE and CREATE INDEX | ||
| 484 | @@ -7894,16 +7923,21 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 485 | |||
| 486 | if (IsA(stmt, IndexStmt)) | ||
| 487 | querytree_list = lappend(querytree_list, | ||
| 488 | - transformIndexStmt((IndexStmt *) stmt, | ||
| 489 | + transformIndexStmt(oldRelId, | ||
| 490 | + (IndexStmt *) stmt, | ||
| 491 | cmd)); | ||
| 492 | else if (IsA(stmt, AlterTableStmt)) | ||
| 493 | querytree_list = list_concat(querytree_list, | ||
| 494 | - transformAlterTableStmt((AlterTableStmt *) stmt, | ||
| 495 | + transformAlterTableStmt(oldRelId, | ||
| 496 | + (AlterTableStmt *) stmt, | ||
| 497 | cmd)); | ||
| 498 | else | ||
| 499 | querytree_list = lappend(querytree_list, stmt); | ||
| 500 | } | ||
| 501 | |||
| 502 | + /* Caller should already have acquired whatever lock we need. */ | ||
| 503 | + rel = relation_open(oldRelId, NoLock); | ||
| 504 | + | ||
| 505 | /* | ||
| 506 | * Attach each generated command to the proper place in the work queue. | ||
| 507 | * Note this could result in creation of entirely new work-queue entries. | ||
| 508 | @@ -7915,7 +7949,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 509 | foreach(list_item, querytree_list) | ||
| 510 | { | ||
| 511 | Node *stm = (Node *) lfirst(list_item); | ||
| 512 | - Relation rel; | ||
| 513 | AlteredTableInfo *tab; | ||
| 514 | |||
| 515 | switch (nodeTag(stm)) | ||
| 516 | @@ -7928,14 +7961,12 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 517 | if (!rewrite) | ||
| 518 | TryReuseIndex(oldId, stmt); | ||
| 519 | |||
| 520 | - rel = relation_openrv(stmt->relation, lockmode); | ||
| 521 | tab = ATGetQueueEntry(wqueue, rel); | ||
| 522 | newcmd = makeNode(AlterTableCmd); | ||
| 523 | newcmd->subtype = AT_ReAddIndex; | ||
| 524 | newcmd->def = (Node *) stmt; | ||
| 525 | tab->subcmds[AT_PASS_OLD_INDEX] = | ||
| 526 | lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd); | ||
| 527 | - relation_close(rel, NoLock); | ||
| 528 | break; | ||
| 529 | } | ||
| 530 | case T_AlterTableStmt: | ||
| 531 | @@ -7943,7 +7974,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 532 | AlterTableStmt *stmt = (AlterTableStmt *) stm; | ||
| 533 | ListCell *lcmd; | ||
| 534 | |||
| 535 | - rel = relation_openrv(stmt->relation, lockmode); | ||
| 536 | tab = ATGetQueueEntry(wqueue, rel); | ||
| 537 | foreach(lcmd, stmt->cmds) | ||
| 538 | { | ||
| 539 | @@ -7964,6 +7994,7 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 540 | case AT_AddConstraint: | ||
| 541 | Assert(IsA(cmd->def, Constraint)); | ||
| 542 | con = (Constraint *) cmd->def; | ||
| 543 | + con->old_pktable_oid = refRelId; | ||
| 544 | /* rewriting neither side of a FK */ | ||
| 545 | if (con->contype == CONSTR_FOREIGN && | ||
| 546 | !rewrite && !tab->rewrite) | ||
| 547 | @@ -7977,7 +8008,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 548 | (int) cmd->subtype); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | - relation_close(rel, NoLock); | ||
| 552 | break; | ||
| 553 | } | ||
| 554 | default: | ||
| 555 | @@ -7985,6 +8015,8 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 556 | (int) nodeTag(stm)); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | + | ||
| 560 | + relation_close(rel, NoLock); | ||
| 561 | } | ||
| 562 | |||
| 563 | /* | ||
| 564 | @@ -7995,7 +8027,6 @@ static void | ||
| 565 | TryReuseIndex(Oid oldId, IndexStmt *stmt) | ||
| 566 | { | ||
| 567 | if (CheckIndexCompatible(oldId, | ||
| 568 | - stmt->relation, | ||
| 569 | stmt->accessMethod, | ||
| 570 | stmt->indexParams, | ||
| 571 | stmt->excludeOpNames)) | ||
| 572 | @@ -10291,6 +10322,38 @@ RangeVarCallbackOwnsTable(const RangeVar *relation, | ||
| 573 | } | ||
| 574 | |||
| 575 | /* | ||
| 576 | + * Callback to RangeVarGetRelidExtended(), similar to | ||
| 577 | + * RangeVarCallbackOwnsTable() but without checks on the type of the relation. | ||
| 578 | + */ | ||
| 579 | +void | ||
| 580 | +RangeVarCallbackOwnsRelation(const RangeVar *relation, | ||
| 581 | + Oid relId, Oid oldRelId, void *arg) | ||
| 582 | +{ | ||
| 583 | + HeapTuple tuple; | ||
| 584 | + | ||
| 585 | + /* Nothing to do if the relation was not found. */ | ||
| 586 | + if (!OidIsValid(relId)) | ||
| 587 | + return; | ||
| 588 | + | ||
| 589 | + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId)); | ||
| 590 | + if (!HeapTupleIsValid(tuple)) /* should not happen */ | ||
| 591 | + elog(ERROR, "cache lookup failed for relation %u", relId); | ||
| 592 | + | ||
| 593 | + if (!pg_class_ownercheck(relId, GetUserId())) | ||
| 594 | + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, | ||
| 595 | + relation->relname); | ||
| 596 | + | ||
| 597 | + if (!allowSystemTableMods && | ||
| 598 | + IsSystemClass((Form_pg_class) GETSTRUCT(tuple))) | ||
| 599 | + ereport(ERROR, | ||
| 600 | + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 601 | + errmsg("permission denied: \"%s\" is a system catalog", | ||
| 602 | + relation->relname))); | ||
| 603 | + | ||
| 604 | + ReleaseSysCache(tuple); | ||
| 605 | +} | ||
| 606 | + | ||
| 607 | +/* | ||
| 608 | * Common RangeVarGetRelid callback for rename, set schema, and alter table | ||
| 609 | * processing. | ||
| 610 | */ | ||
| 611 | diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c | ||
| 612 | index f546d94..9e6c954 100644 | ||
| 613 | --- a/src/backend/commands/trigger.c | ||
| 614 | +++ b/src/backend/commands/trigger.c | ||
| 615 | @@ -42,6 +42,7 @@ | ||
| 616 | #include "pgstat.h" | ||
| 617 | #include "rewrite/rewriteManip.h" | ||
| 618 | #include "storage/bufmgr.h" | ||
| 619 | +#include "storage/lmgr.h" | ||
| 620 | #include "tcop/utility.h" | ||
| 621 | #include "utils/acl.h" | ||
| 622 | #include "utils/builtins.h" | ||
| 623 | @@ -94,6 +95,13 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, | ||
| 624 | * queryString is the source text of the CREATE TRIGGER command. | ||
| 625 | * This must be supplied if a whenClause is specified, else it can be NULL. | ||
| 626 | * | ||
| 627 | + * relOid, if nonzero, is the relation on which the trigger should be | ||
| 628 | + * created. If zero, the name provided in the statement will be looked up. | ||
| 629 | + * | ||
| 630 | + * refRelOid, if nonzero, is the relation to which the constraint trigger | ||
| 631 | + * refers. If zero, the constraint relation name provided in the statement | ||
| 632 | + * will be looked up as needed. | ||
| 633 | + * | ||
| 634 | * constraintOid, if nonzero, says that this trigger is being created | ||
| 635 | * internally to implement that constraint. A suitable pg_depend entry will | ||
| 636 | * be made to link the trigger to that constraint. constraintOid is zero when | ||
| 637 | @@ -116,7 +124,7 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, | ||
| 638 | */ | ||
| 639 | Oid | ||
| 640 | CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 641 | - Oid constraintOid, Oid indexOid, | ||
| 642 | + Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, | ||
| 643 | bool isInternal) | ||
| 644 | { | ||
| 645 | int16 tgtype; | ||
| 646 | @@ -145,7 +153,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 647 | ObjectAddress myself, | ||
| 648 | referenced; | ||
| 649 | |||
| 650 | - rel = heap_openrv(stmt->relation, AccessExclusiveLock); | ||
| 651 | + if (OidIsValid(relOid)) | ||
| 652 | + rel = heap_open(relOid, AccessExclusiveLock); | ||
| 653 | + else | ||
| 654 | + rel = heap_openrv(stmt->relation, AccessExclusiveLock); | ||
| 655 | |||
| 656 | /* | ||
| 657 | * Triggers must be on tables or views, and there are additional | ||
| 658 | @@ -194,7 +205,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 659 | errmsg("permission denied: \"%s\" is a system catalog", | ||
| 660 | RelationGetRelationName(rel)))); | ||
| 661 | |||
| 662 | - if (stmt->isconstraint && stmt->constrrel != NULL) | ||
| 663 | + if (stmt->isconstraint) | ||
| 664 | { | ||
| 665 | /* | ||
| 666 | * We must take a lock on the target relation to protect against | ||
| 667 | @@ -203,7 +214,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 668 | * might end up creating a pg_constraint entry referencing a | ||
| 669 | * nonexistent table. | ||
| 670 | */ | ||
| 671 | - constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, false); | ||
| 672 | + if (OidIsValid(refRelOid)) | ||
| 673 | + { | ||
| 674 | + LockRelationOid(refRelOid, AccessShareLock); | ||
| 675 | + constrrelid = refRelOid; | ||
| 676 | + } | ||
| 677 | + else if (stmt->constrrel != NULL) | ||
| 678 | + constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, | ||
| 679 | + false); | ||
| 680 | } | ||
| 681 | |||
| 682 | /* permission checks */ | ||
| 683 | @@ -513,7 +531,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 684 | ereport(ERROR, | ||
| 685 | (errcode(ERRCODE_DUPLICATE_OBJECT), | ||
| 686 | errmsg("trigger \"%s\" for relation \"%s\" already exists", | ||
| 687 | - trigname, stmt->relation->relname))); | ||
| 688 | + trigname, RelationGetRelationName(rel)))); | ||
| 689 | } | ||
| 690 | systable_endscan(tgscan); | ||
| 691 | } | ||
| 692 | diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c | ||
| 693 | index 9bac994..dbe0f6a 100644 | ||
| 694 | --- a/src/backend/nodes/copyfuncs.c | ||
| 695 | +++ b/src/backend/nodes/copyfuncs.c | ||
| 696 | @@ -2357,6 +2357,7 @@ _copyConstraint(const Constraint *from) | ||
| 697 | COPY_SCALAR_FIELD(fk_upd_action); | ||
| 698 | COPY_SCALAR_FIELD(fk_del_action); | ||
| 699 | COPY_NODE_FIELD(old_conpfeqop); | ||
| 700 | + COPY_SCALAR_FIELD(old_pktable_oid); | ||
| 701 | COPY_SCALAR_FIELD(skip_validation); | ||
| 702 | COPY_SCALAR_FIELD(initially_valid); | ||
| 703 | |||
| 704 | diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c | ||
| 705 | index d185654..f8770b0 100644 | ||
| 706 | --- a/src/backend/nodes/equalfuncs.c | ||
| 707 | +++ b/src/backend/nodes/equalfuncs.c | ||
| 708 | @@ -2143,6 +2143,7 @@ _equalConstraint(const Constraint *a, const Constraint *b) | ||
| 709 | COMPARE_SCALAR_FIELD(fk_upd_action); | ||
| 710 | COMPARE_SCALAR_FIELD(fk_del_action); | ||
| 711 | COMPARE_NODE_FIELD(old_conpfeqop); | ||
| 712 | + COMPARE_SCALAR_FIELD(old_pktable_oid); | ||
| 713 | COMPARE_SCALAR_FIELD(skip_validation); | ||
| 714 | COMPARE_SCALAR_FIELD(initially_valid); | ||
| 715 | |||
| 716 | diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c | ||
| 717 | index 1df71f6..888ffd2 100644 | ||
| 718 | --- a/src/backend/nodes/outfuncs.c | ||
| 719 | +++ b/src/backend/nodes/outfuncs.c | ||
| 720 | @@ -2653,6 +2653,7 @@ _outConstraint(StringInfo str, const Constraint *node) | ||
| 721 | WRITE_CHAR_FIELD(fk_upd_action); | ||
| 722 | WRITE_CHAR_FIELD(fk_del_action); | ||
| 723 | WRITE_NODE_FIELD(old_conpfeqop); | ||
| 724 | + WRITE_OID_FIELD(old_pktable_oid); | ||
| 725 | WRITE_BOOL_FIELD(skip_validation); | ||
| 726 | WRITE_BOOL_FIELD(initially_valid); | ||
| 727 | break; | ||
| 728 | diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c | ||
| 729 | index e3f9c62..5df939a 100644 | ||
| 730 | --- a/src/backend/parser/parse_utilcmd.c | ||
| 731 | +++ b/src/backend/parser/parse_utilcmd.c | ||
| 732 | @@ -1867,14 +1867,18 @@ transformFKConstraints(CreateStmtContext *cxt, | ||
| 733 | * a predicate expression. There are several code paths that create indexes | ||
| 734 | * without bothering to call this, because they know they don't have any | ||
| 735 | * such expressions to deal with. | ||
| 736 | + * | ||
| 737 | + * To avoid race conditions, it's important that this function rely only on | ||
| 738 | + * the passed-in relid (and not on stmt->relation) to determine the target | ||
| 739 | + * relation. | ||
| 740 | */ | ||
| 741 | IndexStmt * | ||
| 742 | -transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 743 | +transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) | ||
| 744 | { | ||
| 745 | - Relation rel; | ||
| 746 | ParseState *pstate; | ||
| 747 | RangeTblEntry *rte; | ||
| 748 | ListCell *l; | ||
| 749 | + Relation rel; | ||
| 750 | |||
| 751 | /* | ||
| 752 | * We must not scribble on the passed-in IndexStmt, so copy it. (This is | ||
| 753 | @@ -1882,26 +1886,17 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 754 | */ | ||
| 755 | stmt = (IndexStmt *) copyObject(stmt); | ||
| 756 | |||
| 757 | - /* | ||
| 758 | - * Open the parent table with appropriate locking. We must do this | ||
| 759 | - * because addRangeTableEntry() would acquire only AccessShareLock, | ||
| 760 | - * leaving DefineIndex() needing to do a lock upgrade with consequent risk | ||
| 761 | - * of deadlock. Make sure this stays in sync with the type of lock | ||
| 762 | - * DefineIndex() wants. If we are being called by ALTER TABLE, we will | ||
| 763 | - * already hold a higher lock. | ||
| 764 | - */ | ||
| 765 | - rel = heap_openrv(stmt->relation, | ||
| 766 | - (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock)); | ||
| 767 | - | ||
| 768 | /* Set up pstate */ | ||
| 769 | pstate = make_parsestate(NULL); | ||
| 770 | pstate->p_sourcetext = queryString; | ||
| 771 | |||
| 772 | /* | ||
| 773 | * Put the parent table into the rtable so that the expressions can refer | ||
| 774 | - * to its fields without qualification. | ||
| 775 | + * to its fields without qualification. Caller is responsible for locking | ||
| 776 | + * relation, but we still need to open it. | ||
| 777 | */ | ||
| 778 | - rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); | ||
| 779 | + rel = relation_open(relid, NoLock); | ||
| 780 | + rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true); | ||
| 781 | |||
| 782 | /* no to join list, yes to namespaces */ | ||
| 783 | addRTEtoQuery(pstate, rte, false, true, true); | ||
| 784 | @@ -1955,7 +1950,7 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 785 | |||
| 786 | free_parsestate(pstate); | ||
| 787 | |||
| 788 | - /* Close relation, but keep the lock */ | ||
| 789 | + /* Close relation */ | ||
| 790 | heap_close(rel, NoLock); | ||
| 791 | |||
| 792 | return stmt; | ||
| 793 | @@ -2277,9 +2272,14 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, | ||
| 794 | * Returns a List of utility commands to be done in sequence. One of these | ||
| 795 | * will be the transformed AlterTableStmt, but there may be additional actions | ||
| 796 | * to be done before and after the actual AlterTable() call. | ||
| 797 | + * | ||
| 798 | + * To avoid race conditions, it's important that this function rely only on | ||
| 799 | + * the passed-in relid (and not on stmt->relation) to determine the target | ||
| 800 | + * relation. | ||
| 801 | */ | ||
| 802 | List * | ||
| 803 | -transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 804 | +transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, | ||
| 805 | + const char *queryString) | ||
| 806 | { | ||
| 807 | Relation rel; | ||
| 808 | ParseState *pstate; | ||
| 809 | @@ -2291,7 +2291,6 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 810 | List *newcmds = NIL; | ||
| 811 | bool skipValidation = true; | ||
| 812 | AlterTableCmd *newcmd; | ||
| 813 | - LOCKMODE lockmode; | ||
| 814 | |||
| 815 | /* | ||
| 816 | * We must not scribble on the passed-in AlterTableStmt, so copy it. (This | ||
| 817 | @@ -2299,29 +2298,8 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 818 | */ | ||
| 819 | stmt = (AlterTableStmt *) copyObject(stmt); | ||
| 820 | |||
| 821 | - /* | ||
| 822 | - * Determine the appropriate lock level for this list of subcommands. | ||
| 823 | - */ | ||
| 824 | - lockmode = AlterTableGetLockLevel(stmt->cmds); | ||
| 825 | - | ||
| 826 | - /* | ||
| 827 | - * Acquire appropriate lock on the target relation, which will be held | ||
| 828 | - * until end of transaction. This ensures any decisions we make here | ||
| 829 | - * based on the state of the relation will still be good at execution. We | ||
| 830 | - * must get lock now because execution will later require it; taking a | ||
| 831 | - * lower grade lock now and trying to upgrade later risks deadlock. Any | ||
| 832 | - * new commands we add after this must not upgrade the lock level | ||
| 833 | - * requested here. | ||
| 834 | - */ | ||
| 835 | - rel = relation_openrv_extended(stmt->relation, lockmode, stmt->missing_ok); | ||
| 836 | - if (rel == NULL) | ||
| 837 | - { | ||
| 838 | - /* this message is consistent with relation_openrv */ | ||
| 839 | - ereport(NOTICE, | ||
| 840 | - (errmsg("relation \"%s\" does not exist, skipping", | ||
| 841 | - stmt->relation->relname))); | ||
| 842 | - return NIL; | ||
| 843 | - } | ||
| 844 | + /* Caller is responsible for locking the relation */ | ||
| 845 | + rel = relation_open(relid, NoLock); | ||
| 846 | |||
| 847 | /* Set up pstate and CreateStmtContext */ | ||
| 848 | pstate = make_parsestate(NULL); | ||
| 849 | @@ -2434,7 +2412,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 850 | IndexStmt *idxstmt = (IndexStmt *) lfirst(l); | ||
| 851 | |||
| 852 | Assert(IsA(idxstmt, IndexStmt)); | ||
| 853 | - idxstmt = transformIndexStmt(idxstmt, queryString); | ||
| 854 | + idxstmt = transformIndexStmt(relid, idxstmt, queryString); | ||
| 855 | newcmd = makeNode(AlterTableCmd); | ||
| 856 | newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex; | ||
| 857 | newcmd->def = (Node *) idxstmt; | ||
| 858 | @@ -2458,7 +2436,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 859 | newcmds = lappend(newcmds, newcmd); | ||
| 860 | } | ||
| 861 | |||
| 862 | - /* Close rel but keep lock */ | ||
| 863 | + /* Close rel */ | ||
| 864 | relation_close(rel, NoLock); | ||
| 865 | |||
| 866 | /* | ||
| 867 | diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c | ||
| 868 | index 509bf4d..7903e03 100644 | ||
| 869 | --- a/src/backend/tcop/utility.c | ||
| 870 | +++ b/src/backend/tcop/utility.c | ||
| 871 | @@ -67,49 +67,6 @@ ProcessUtility_hook_type ProcessUtility_hook = NULL; | ||
| 872 | |||
| 873 | |||
| 874 | /* | ||
| 875 | - * Verify user has ownership of specified relation, else ereport. | ||
| 876 | - * | ||
| 877 | - * If noCatalogs is true then we also deny access to system catalogs, | ||
| 878 | - * except when allowSystemTableMods is true. | ||
| 879 | - */ | ||
| 880 | -void | ||
| 881 | -CheckRelationOwnership(RangeVar *rel, bool noCatalogs) | ||
| 882 | -{ | ||
| 883 | - Oid relOid; | ||
| 884 | - HeapTuple tuple; | ||
| 885 | - | ||
| 886 | - /* | ||
| 887 | - * XXX: This is unsafe in the presence of concurrent DDL, since it is | ||
| 888 | - * called before acquiring any lock on the target relation. However, | ||
| 889 | - * locking the target relation (especially using something like | ||
| 890 | - * AccessExclusiveLock) before verifying that the user has permissions is | ||
| 891 | - * not appealing either. | ||
| 892 | - */ | ||
| 893 | - relOid = RangeVarGetRelid(rel, NoLock, false); | ||
| 894 | - | ||
| 895 | - tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); | ||
| 896 | - if (!HeapTupleIsValid(tuple)) /* should not happen */ | ||
| 897 | - elog(ERROR, "cache lookup failed for relation %u", relOid); | ||
| 898 | - | ||
| 899 | - if (!pg_class_ownercheck(relOid, GetUserId())) | ||
| 900 | - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, | ||
| 901 | - rel->relname); | ||
| 902 | - | ||
| 903 | - if (noCatalogs) | ||
| 904 | - { | ||
| 905 | - if (!allowSystemTableMods && | ||
| 906 | - IsSystemClass((Form_pg_class) GETSTRUCT(tuple))) | ||
| 907 | - ereport(ERROR, | ||
| 908 | - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 909 | - errmsg("permission denied: \"%s\" is a system catalog", | ||
| 910 | - rel->relname))); | ||
| 911 | - } | ||
| 912 | - | ||
| 913 | - ReleaseSysCache(tuple); | ||
| 914 | -} | ||
| 915 | - | ||
| 916 | - | ||
| 917 | -/* | ||
| 918 | * CommandIsReadOnly: is an executable query read-only? | ||
| 919 | * | ||
| 920 | * This is a much stricter test than we apply for XactReadOnly mode; | ||
| 921 | @@ -723,7 +680,8 @@ standard_ProcessUtility(Node *parsetree, | ||
| 922 | if (OidIsValid(relid)) | ||
| 923 | { | ||
| 924 | /* Run parse analysis ... */ | ||
| 925 | - stmts = transformAlterTableStmt(atstmt, queryString); | ||
| 926 | + stmts = transformAlterTableStmt(relid, atstmt, | ||
| 927 | + queryString); | ||
| 928 | |||
| 929 | /* ... and do it */ | ||
| 930 | foreach(l, stmts) | ||
| 931 | @@ -910,18 +868,36 @@ standard_ProcessUtility(Node *parsetree, | ||
| 932 | case T_IndexStmt: /* CREATE INDEX */ | ||
| 933 | { | ||
| 934 | IndexStmt *stmt = (IndexStmt *) parsetree; | ||
| 935 | + Oid relid; | ||
| 936 | + LOCKMODE lockmode; | ||
| 937 | |||
| 938 | if (stmt->concurrent) | ||
| 939 | PreventTransactionChain(isTopLevel, | ||
| 940 | "CREATE INDEX CONCURRENTLY"); | ||
| 941 | |||
| 942 | - CheckRelationOwnership(stmt->relation, true); | ||
| 943 | + /* | ||
| 944 | + * Look up the relation OID just once, right here at the | ||
| 945 | + * beginning, so that we don't end up repeating the name | ||
| 946 | + * lookup later and latching onto a different relation | ||
| 947 | + * partway through. To avoid lock upgrade hazards, it's | ||
| 948 | + * important that we take the strongest lock that will | ||
| 949 | + * eventually be needed here, so the lockmode calculation | ||
| 950 | + * needs to match what DefineIndex() does. | ||
| 951 | + */ | ||
| 952 | + lockmode = stmt->concurrent ? ShareUpdateExclusiveLock | ||
| 953 | + : ShareLock; | ||
| 954 | + relid = | ||
| 955 | + RangeVarGetRelidExtended(stmt->relation, lockmode, | ||
| 956 | + false, false, | ||
| 957 | + RangeVarCallbackOwnsRelation, | ||
| 958 | + NULL); | ||
| 959 | |||
| 960 | /* Run parse analysis ... */ | ||
| 961 | - stmt = transformIndexStmt(stmt, queryString); | ||
| 962 | + stmt = transformIndexStmt(relid, stmt, queryString); | ||
| 963 | |||
| 964 | /* ... and do it */ | ||
| 965 | - DefineIndex(stmt, | ||
| 966 | + DefineIndex(relid, /* OID of heap relation */ | ||
| 967 | + stmt, | ||
| 968 | InvalidOid, /* no predefined OID */ | ||
| 969 | false, /* is_alter_table */ | ||
| 970 | true, /* check_rights */ | ||
| 971 | @@ -1057,7 +1033,8 @@ standard_ProcessUtility(Node *parsetree, | ||
| 972 | |||
| 973 | case T_CreateTrigStmt: | ||
| 974 | (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString, | ||
| 975 | - InvalidOid, InvalidOid, false); | ||
| 976 | + InvalidOid, InvalidOid, InvalidOid, | ||
| 977 | + InvalidOid, false); | ||
| 978 | break; | ||
| 979 | |||
| 980 | case T_CreatePLangStmt: | ||
| 981 | diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h | ||
| 982 | index d9d40b2..d8f8da4 100644 | ||
| 983 | --- a/src/include/catalog/pg_constraint.h | ||
| 984 | +++ b/src/include/catalog/pg_constraint.h | ||
| 985 | @@ -246,6 +246,7 @@ extern char *ChooseConstraintName(const char *name1, const char *name2, | ||
| 986 | |||
| 987 | extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, | ||
| 988 | Oid newNspId, bool isType, ObjectAddresses *objsMoved); | ||
| 989 | +extern void get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid); | ||
| 990 | extern Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok); | ||
| 991 | extern Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok); | ||
| 992 | |||
| 993 | diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h | ||
| 994 | index 9b6d57a..a00fd37 100644 | ||
| 995 | --- a/src/include/commands/defrem.h | ||
| 996 | +++ b/src/include/commands/defrem.h | ||
| 997 | @@ -20,7 +20,8 @@ | ||
| 998 | extern void RemoveObjects(DropStmt *stmt); | ||
| 999 | |||
| 1000 | /* commands/indexcmds.c */ | ||
| 1001 | -extern Oid DefineIndex(IndexStmt *stmt, | ||
| 1002 | +extern Oid DefineIndex(Oid relationId, | ||
| 1003 | + IndexStmt *stmt, | ||
| 1004 | Oid indexRelationId, | ||
| 1005 | bool is_alter_table, | ||
| 1006 | bool check_rights, | ||
| 1007 | @@ -35,7 +36,6 @@ extern char *makeObjectName(const char *name1, const char *name2, | ||
| 1008 | extern char *ChooseRelationName(const char *name1, const char *name2, | ||
| 1009 | const char *label, Oid namespaceid); | ||
| 1010 | extern bool CheckIndexCompatible(Oid oldId, | ||
| 1011 | - RangeVar *heapRelation, | ||
| 1012 | char *accessMethodName, | ||
| 1013 | List *attributeList, | ||
| 1014 | List *exclusionOpNames); | ||
| 1015 | diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h | ||
| 1016 | index 4f32062..d41f8a1 100644 | ||
| 1017 | --- a/src/include/commands/tablecmds.h | ||
| 1018 | +++ b/src/include/commands/tablecmds.h | ||
| 1019 | @@ -78,4 +78,6 @@ extern void AtEOSubXact_on_commit_actions(bool isCommit, | ||
| 1020 | extern void RangeVarCallbackOwnsTable(const RangeVar *relation, | ||
| 1021 | Oid relId, Oid oldRelId, void *arg); | ||
| 1022 | |||
| 1023 | +extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, | ||
| 1024 | + Oid relId, Oid oldRelId, void *noCatalogs); | ||
| 1025 | #endif /* TABLECMDS_H */ | ||
| 1026 | diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h | ||
| 1027 | index 9303341..0869c0b 100644 | ||
| 1028 | --- a/src/include/commands/trigger.h | ||
| 1029 | +++ b/src/include/commands/trigger.h | ||
| 1030 | @@ -109,7 +109,7 @@ extern PGDLLIMPORT int SessionReplicationRole; | ||
| 1031 | #define TRIGGER_DISABLED 'D' | ||
| 1032 | |||
| 1033 | extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 1034 | - Oid constraintOid, Oid indexOid, | ||
| 1035 | + Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, | ||
| 1036 | bool isInternal); | ||
| 1037 | |||
| 1038 | extern void RemoveTriggerById(Oid trigOid); | ||
| 1039 | diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h | ||
| 1040 | index 327f7cf..31f5479 100644 | ||
| 1041 | --- a/src/include/nodes/parsenodes.h | ||
| 1042 | +++ b/src/include/nodes/parsenodes.h | ||
| 1043 | @@ -1566,6 +1566,8 @@ typedef struct Constraint | ||
| 1044 | /* Fields used for constraints that allow a NOT VALID specification */ | ||
| 1045 | bool skip_validation; /* skip validation of existing rows? */ | ||
| 1046 | bool initially_valid; /* mark the new constraint as valid? */ | ||
| 1047 | + | ||
| 1048 | + Oid old_pktable_oid; /* pg_constraint.confrelid of my former self */ | ||
| 1049 | } Constraint; | ||
| 1050 | |||
| 1051 | /* ---------------------- | ||
| 1052 | diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h | ||
| 1053 | index 4ad793a..d8b340e 100644 | ||
| 1054 | --- a/src/include/parser/parse_utilcmd.h | ||
| 1055 | +++ b/src/include/parser/parse_utilcmd.h | ||
| 1056 | @@ -18,9 +18,10 @@ | ||
| 1057 | |||
| 1058 | |||
| 1059 | extern List *transformCreateStmt(CreateStmt *stmt, const char *queryString); | ||
| 1060 | -extern List *transformAlterTableStmt(AlterTableStmt *stmt, | ||
| 1061 | +extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, | ||
| 1062 | const char *queryString); | ||
| 1063 | -extern IndexStmt *transformIndexStmt(IndexStmt *stmt, const char *queryString); | ||
| 1064 | +extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt, | ||
| 1065 | + const char *queryString); | ||
| 1066 | extern void transformRuleStmt(RuleStmt *stmt, const char *queryString, | ||
| 1067 | List **actions, Node **whereClause); | ||
| 1068 | extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); | ||
| 1069 | diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h | ||
| 1070 | index 54190b2..ae871ca 100644 | ||
| 1071 | --- a/src/include/tcop/utility.h | ||
| 1072 | +++ b/src/include/tcop/utility.h | ||
| 1073 | @@ -42,6 +42,4 @@ extern LogStmtLevel GetCommandLogLevel(Node *parsetree); | ||
| 1074 | |||
| 1075 | extern bool CommandIsReadOnly(Node *parsetree); | ||
| 1076 | |||
| 1077 | -extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs); | ||
| 1078 | - | ||
| 1079 | #endif /* UTILITY_H */ | ||
| 1080 | -- | ||
| 1081 | 1.7.5.4 | ||
| 1082 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0006-Fix-handling-of-wide-datetime-input-output.patch b/meta-oe/recipes-support/postgresql/files/0006-Fix-handling-of-wide-datetime-input-output.patch deleted file mode 100644 index fac0a7347c..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0006-Fix-handling-of-wide-datetime-input-output.patch +++ /dev/null | |||
| @@ -1,465 +0,0 @@ | |||
| 1 | From f416622be81d1320417bbc7892fd562cae0dba72 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Noah Misch <noah@leadboat.com> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Fix handling of wide datetime input/output. | ||
| 5 | MIME-Version: 1.0 | ||
| 6 | Content-Type: text/plain; charset=UTF-8 | ||
| 7 | Content-Transfer-Encoding: 8bit | ||
| 8 | |||
| 9 | commit f416622be81d1320417bbc7892fd562cae0dba72 REL9_2_STABLE | ||
| 10 | |||
| 11 | Many server functions use the MAXDATELEN constant to size a buffer for | ||
| 12 | parsing or displaying a datetime value. It was much too small for the | ||
| 13 | longest possible interval output and slightly too small for certain | ||
| 14 | valid timestamp input, particularly input with a long timezone name. | ||
| 15 | The long input was rejected needlessly; the long output caused | ||
| 16 | interval_out() to overrun its buffer. ECPG's pgtypes library has a copy | ||
| 17 | of the vulnerable functions, which bore the same vulnerabilities along | ||
| 18 | with some of its own. In contrast to the server, certain long inputs | ||
| 19 | caused stack overflow rather than failing cleanly. Back-patch to 8.4 | ||
| 20 | (all supported versions). | ||
| 21 | |||
| 22 | Reported by Daniel Schüssler, reviewed by Tom Lane. | ||
| 23 | |||
| 24 | Security: CVE-2014-0063 | ||
| 25 | |||
| 26 | |||
| 27 | Upstream-Status: Backport | ||
| 28 | |||
| 29 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 30 | --- | ||
| 31 | src/include/utils/datetime.h | 17 +++++--- | ||
| 32 | src/interfaces/ecpg/pgtypeslib/datetime.c | 4 +- | ||
| 33 | src/interfaces/ecpg/pgtypeslib/dt.h | 17 +++++--- | ||
| 34 | src/interfaces/ecpg/pgtypeslib/dt_common.c | 44 ++++++++++++++------ | ||
| 35 | src/interfaces/ecpg/pgtypeslib/interval.c | 2 +- | ||
| 36 | src/interfaces/ecpg/pgtypeslib/timestamp.c | 2 +- | ||
| 37 | .../ecpg/test/expected/pgtypeslib-dt_test2.c | 22 +++++++--- | ||
| 38 | .../ecpg/test/expected/pgtypeslib-dt_test2.stdout | 19 ++++++++ | ||
| 39 | src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc | 10 ++++ | ||
| 40 | src/test/regress/expected/interval.out | 7 +++ | ||
| 41 | src/test/regress/sql/interval.sql | 2 + | ||
| 42 | 11 files changed, 111 insertions(+), 35 deletions(-) | ||
| 43 | |||
| 44 | diff --git a/src/include/utils/datetime.h b/src/include/utils/datetime.h | ||
| 45 | index d73cc8d..4b805b6 100644 | ||
| 46 | --- a/src/include/utils/datetime.h | ||
| 47 | +++ b/src/include/utils/datetime.h | ||
| 48 | @@ -188,12 +188,17 @@ struct tzEntry; | ||
| 49 | #define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)) | ||
| 50 | #define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_ALL_SECS_M) | ||
| 51 | |||
| 52 | -#define MAXDATELEN 63 /* maximum possible length of an input date | ||
| 53 | - * string (not counting tr. null) */ | ||
| 54 | -#define MAXDATEFIELDS 25 /* maximum possible number of fields in a date | ||
| 55 | - * string */ | ||
| 56 | -#define TOKMAXLEN 10 /* only this many chars are stored in | ||
| 57 | - * datetktbl */ | ||
| 58 | +/* | ||
| 59 | + * Working buffer size for input and output of interval, timestamp, etc. | ||
| 60 | + * Inputs that need more working space will be rejected early. Longer outputs | ||
| 61 | + * will overrun buffers, so this must suffice for all possible output. As of | ||
| 62 | + * this writing, interval_out() needs the most space at ~90 bytes. | ||
| 63 | + */ | ||
| 64 | +#define MAXDATELEN 128 | ||
| 65 | +/* maximum possible number of fields in a date string */ | ||
| 66 | +#define MAXDATEFIELDS 25 | ||
| 67 | +/* only this many chars are stored in datetktbl */ | ||
| 68 | +#define TOKMAXLEN 10 | ||
| 69 | |||
| 70 | /* keep this struct small; it gets used a lot */ | ||
| 71 | typedef struct | ||
| 72 | diff --git a/src/interfaces/ecpg/pgtypeslib/datetime.c b/src/interfaces/ecpg/pgtypeslib/datetime.c | ||
| 73 | index 823626f..4adcd1e 100644 | ||
| 74 | --- a/src/interfaces/ecpg/pgtypeslib/datetime.c | ||
| 75 | +++ b/src/interfaces/ecpg/pgtypeslib/datetime.c | ||
| 76 | @@ -61,14 +61,14 @@ PGTYPESdate_from_asc(char *str, char **endptr) | ||
| 77 | int nf; | ||
| 78 | char *field[MAXDATEFIELDS]; | ||
| 79 | int ftype[MAXDATEFIELDS]; | ||
| 80 | - char lowstr[MAXDATELEN + 1]; | ||
| 81 | + char lowstr[MAXDATELEN + MAXDATEFIELDS]; | ||
| 82 | char *realptr; | ||
| 83 | char **ptr = (endptr != NULL) ? endptr : &realptr; | ||
| 84 | |||
| 85 | bool EuroDates = FALSE; | ||
| 86 | |||
| 87 | errno = 0; | ||
| 88 | - if (strlen(str) >= sizeof(lowstr)) | ||
| 89 | + if (strlen(str) > MAXDATELEN) | ||
| 90 | { | ||
| 91 | errno = PGTYPES_DATE_BAD_DATE; | ||
| 92 | return INT_MIN; | ||
| 93 | diff --git a/src/interfaces/ecpg/pgtypeslib/dt.h b/src/interfaces/ecpg/pgtypeslib/dt.h | ||
| 94 | index dfe6f9e..2780593 100644 | ||
| 95 | --- a/src/interfaces/ecpg/pgtypeslib/dt.h | ||
| 96 | +++ b/src/interfaces/ecpg/pgtypeslib/dt.h | ||
| 97 | @@ -192,12 +192,17 @@ typedef double fsec_t; | ||
| 98 | #define DTK_DATE_M (DTK_M(YEAR) | DTK_M(MONTH) | DTK_M(DAY)) | ||
| 99 | #define DTK_TIME_M (DTK_M(HOUR) | DTK_M(MINUTE) | DTK_M(SECOND)) | ||
| 100 | |||
| 101 | -#define MAXDATELEN 63 /* maximum possible length of an input date | ||
| 102 | - * string (not counting tr. null) */ | ||
| 103 | -#define MAXDATEFIELDS 25 /* maximum possible number of fields in a date | ||
| 104 | - * string */ | ||
| 105 | -#define TOKMAXLEN 10 /* only this many chars are stored in | ||
| 106 | - * datetktbl */ | ||
| 107 | +/* | ||
| 108 | + * Working buffer size for input and output of interval, timestamp, etc. | ||
| 109 | + * Inputs that need more working space will be rejected early. Longer outputs | ||
| 110 | + * will overrun buffers, so this must suffice for all possible output. As of | ||
| 111 | + * this writing, PGTYPESinterval_to_asc() needs the most space at ~90 bytes. | ||
| 112 | + */ | ||
| 113 | +#define MAXDATELEN 128 | ||
| 114 | +/* maximum possible number of fields in a date string */ | ||
| 115 | +#define MAXDATEFIELDS 25 | ||
| 116 | +/* only this many chars are stored in datetktbl */ | ||
| 117 | +#define TOKMAXLEN 10 | ||
| 118 | |||
| 119 | /* keep this struct small; it gets used a lot */ | ||
| 120 | typedef struct | ||
| 121 | diff --git a/src/interfaces/ecpg/pgtypeslib/dt_common.c b/src/interfaces/ecpg/pgtypeslib/dt_common.c | ||
| 122 | index 6b89e4a..18178dd 100644 | ||
| 123 | --- a/src/interfaces/ecpg/pgtypeslib/dt_common.c | ||
| 124 | +++ b/src/interfaces/ecpg/pgtypeslib/dt_common.c | ||
| 125 | @@ -1171,15 +1171,22 @@ DecodeNumberField(int len, char *str, int fmask, | ||
| 126 | if ((cp = strchr(str, '.')) != NULL) | ||
| 127 | { | ||
| 128 | #ifdef HAVE_INT64_TIMESTAMP | ||
| 129 | - char fstr[MAXDATELEN + 1]; | ||
| 130 | + char fstr[7]; | ||
| 131 | + int i; | ||
| 132 | + | ||
| 133 | + cp++; | ||
| 134 | |||
| 135 | /* | ||
| 136 | * OK, we have at most six digits to care about. Let's construct a | ||
| 137 | - * string and then do the conversion to an integer. | ||
| 138 | + * string with those digits, zero-padded on the right, and then do | ||
| 139 | + * the conversion to an integer. | ||
| 140 | + * | ||
| 141 | + * XXX This truncates the seventh digit, unlike rounding it as do | ||
| 142 | + * the backend and the !HAVE_INT64_TIMESTAMP case. | ||
| 143 | */ | ||
| 144 | - strcpy(fstr, (cp + 1)); | ||
| 145 | - strcpy(fstr + strlen(fstr), "000000"); | ||
| 146 | - *(fstr + 6) = '\0'; | ||
| 147 | + for (i = 0; i < 6; i++) | ||
| 148 | + fstr[i] = *cp != '\0' ? *cp++ : '0'; | ||
| 149 | + fstr[i] = '\0'; | ||
| 150 | *fsec = strtol(fstr, NULL, 10); | ||
| 151 | #else | ||
| 152 | *fsec = strtod(cp, NULL); | ||
| 153 | @@ -1531,15 +1538,22 @@ DecodeTime(char *str, int *tmask, struct tm * tm, fsec_t *fsec) | ||
| 154 | else if (*cp == '.') | ||
| 155 | { | ||
| 156 | #ifdef HAVE_INT64_TIMESTAMP | ||
| 157 | - char fstr[MAXDATELEN + 1]; | ||
| 158 | + char fstr[7]; | ||
| 159 | + int i; | ||
| 160 | + | ||
| 161 | + cp++; | ||
| 162 | |||
| 163 | /* | ||
| 164 | - * OK, we have at most six digits to work with. Let's construct a | ||
| 165 | - * string and then do the conversion to an integer. | ||
| 166 | + * OK, we have at most six digits to care about. Let's construct a | ||
| 167 | + * string with those digits, zero-padded on the right, and then do | ||
| 168 | + * the conversion to an integer. | ||
| 169 | + * | ||
| 170 | + * XXX This truncates the seventh digit, unlike rounding it as do | ||
| 171 | + * the backend and the !HAVE_INT64_TIMESTAMP case. | ||
| 172 | */ | ||
| 173 | - strncpy(fstr, (cp + 1), 7); | ||
| 174 | - strcpy(fstr + strlen(fstr), "000000"); | ||
| 175 | - *(fstr + 6) = '\0'; | ||
| 176 | + for (i = 0; i < 6; i++) | ||
| 177 | + fstr[i] = *cp != '\0' ? *cp++ : '0'; | ||
| 178 | + fstr[i] = '\0'; | ||
| 179 | *fsec = strtol(fstr, &cp, 10); | ||
| 180 | #else | ||
| 181 | str = cp; | ||
| 182 | @@ -1665,6 +1679,9 @@ DecodePosixTimezone(char *str, int *tzp) | ||
| 183 | * DTK_NUMBER can hold date fields (yy.ddd) | ||
| 184 | * DTK_STRING can hold months (January) and time zones (PST) | ||
| 185 | * DTK_DATE can hold Posix time zones (GMT-8) | ||
| 186 | + * | ||
| 187 | + * The "lowstr" work buffer must have at least strlen(timestr) + MAXDATEFIELDS | ||
| 188 | + * bytes of space. On output, field[] entries will point into it. | ||
| 189 | */ | ||
| 190 | int | ||
| 191 | ParseDateTime(char *timestr, char *lowstr, | ||
| 192 | @@ -1677,7 +1694,10 @@ ParseDateTime(char *timestr, char *lowstr, | ||
| 193 | /* outer loop through fields */ | ||
| 194 | while (*(*endstr) != '\0') | ||
| 195 | { | ||
| 196 | + /* Record start of current field */ | ||
| 197 | field[nf] = lp; | ||
| 198 | + if (nf >= MAXDATEFIELDS) | ||
| 199 | + return -1; | ||
| 200 | |||
| 201 | /* leading digit? then date or time */ | ||
| 202 | if (isdigit((unsigned char) *(*endstr))) | ||
| 203 | @@ -1818,8 +1838,6 @@ ParseDateTime(char *timestr, char *lowstr, | ||
| 204 | /* force in a delimiter after each field */ | ||
| 205 | *lp++ = '\0'; | ||
| 206 | nf++; | ||
| 207 | - if (nf > MAXDATEFIELDS) | ||
| 208 | - return -1; | ||
| 209 | } | ||
| 210 | |||
| 211 | *numfields = nf; | ||
| 212 | diff --git a/src/interfaces/ecpg/pgtypeslib/interval.c b/src/interfaces/ecpg/pgtypeslib/interval.c | ||
| 213 | index bcc10ee..fdd8f49 100644 | ||
| 214 | --- a/src/interfaces/ecpg/pgtypeslib/interval.c | ||
| 215 | +++ b/src/interfaces/ecpg/pgtypeslib/interval.c | ||
| 216 | @@ -1092,7 +1092,7 @@ PGTYPESinterval_from_asc(char *str, char **endptr) | ||
| 217 | tm->tm_sec = 0; | ||
| 218 | fsec = 0; | ||
| 219 | |||
| 220 | - if (strlen(str) >= sizeof(lowstr)) | ||
| 221 | + if (strlen(str) > MAXDATELEN) | ||
| 222 | { | ||
| 223 | errno = PGTYPES_INTVL_BAD_INTERVAL; | ||
| 224 | return NULL; | ||
| 225 | diff --git a/src/interfaces/ecpg/pgtypeslib/timestamp.c b/src/interfaces/ecpg/pgtypeslib/timestamp.c | ||
| 226 | index 7d3f7c8..4f91e63 100644 | ||
| 227 | --- a/src/interfaces/ecpg/pgtypeslib/timestamp.c | ||
| 228 | +++ b/src/interfaces/ecpg/pgtypeslib/timestamp.c | ||
| 229 | @@ -297,7 +297,7 @@ PGTYPEStimestamp_from_asc(char *str, char **endptr) | ||
| 230 | char *realptr; | ||
| 231 | char **ptr = (endptr != NULL) ? endptr : &realptr; | ||
| 232 | |||
| 233 | - if (strlen(str) >= sizeof(lowstr)) | ||
| 234 | + if (strlen(str) > MAXDATELEN) | ||
| 235 | { | ||
| 236 | errno = PGTYPES_TS_BAD_TIMESTAMP; | ||
| 237 | return (noresult); | ||
| 238 | diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c | ||
| 239 | index d3ebb0e..0ba1936 100644 | ||
| 240 | --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c | ||
| 241 | +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.c | ||
| 242 | @@ -45,6 +45,15 @@ char *dates[] = { "19990108foobar", | ||
| 243 | "1999.008", | ||
| 244 | "J2451187", | ||
| 245 | "January 8, 99 BC", | ||
| 246 | + /* | ||
| 247 | + * Maximize space usage in ParseDateTime() with 25 | ||
| 248 | + * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length. | ||
| 249 | + */ | ||
| 250 | + "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | ||
| 251 | + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
| 252 | + /* 26 fields */ | ||
| 253 | + ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | ||
| 254 | + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
| 255 | NULL }; | ||
| 256 | |||
| 257 | /* do not conflict with libc "times" symbol */ | ||
| 258 | @@ -52,6 +61,7 @@ static char *times[] = { "0:04", | ||
| 259 | "1:59 PDT", | ||
| 260 | "13:24:40 -8:00", | ||
| 261 | "13:24:40.495+3", | ||
| 262 | + "13:24:40.123456789+3", | ||
| 263 | NULL }; | ||
| 264 | |||
| 265 | char *intervals[] = { "1 minute", | ||
| 266 | @@ -73,22 +83,22 @@ main(void) | ||
| 267 | |||
| 268 | |||
| 269 | |||
| 270 | -#line 52 "dt_test2.pgc" | ||
| 271 | +#line 62 "dt_test2.pgc" | ||
| 272 | date date1 ; | ||
| 273 | |||
| 274 | -#line 53 "dt_test2.pgc" | ||
| 275 | +#line 63 "dt_test2.pgc" | ||
| 276 | timestamp ts1 , ts2 ; | ||
| 277 | |||
| 278 | -#line 54 "dt_test2.pgc" | ||
| 279 | +#line 64 "dt_test2.pgc" | ||
| 280 | char * text ; | ||
| 281 | |||
| 282 | -#line 55 "dt_test2.pgc" | ||
| 283 | +#line 65 "dt_test2.pgc" | ||
| 284 | interval * i1 ; | ||
| 285 | |||
| 286 | -#line 56 "dt_test2.pgc" | ||
| 287 | +#line 66 "dt_test2.pgc" | ||
| 288 | date * dc ; | ||
| 289 | /* exec sql end declare section */ | ||
| 290 | -#line 57 "dt_test2.pgc" | ||
| 291 | +#line 67 "dt_test2.pgc" | ||
| 292 | |||
| 293 | |||
| 294 | int i, j; | ||
| 295 | diff --git a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout | ||
| 296 | index 24e9d26..9a4587b 100644 | ||
| 297 | --- a/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout | ||
| 298 | +++ b/src/interfaces/ecpg/test/expected/pgtypeslib-dt_test2.stdout | ||
| 299 | @@ -8,85 +8,104 @@ TS[3,0]: 1999-01-08 00:04:00 | ||
| 300 | TS[3,1]: 1999-01-08 01:59:00 | ||
| 301 | TS[3,2]: 1999-01-08 13:24:40 | ||
| 302 | TS[3,3]: 1999-01-08 13:24:40.495 | ||
| 303 | +TS[3,4]: 1999-01-08 13:24:40.123456 | ||
| 304 | Date[4]: 1999-01-08 (N - F) | ||
| 305 | TS[4,0]: 1999-01-08 00:04:00 | ||
| 306 | TS[4,1]: 1999-01-08 01:59:00 | ||
| 307 | TS[4,2]: 1999-01-08 13:24:40 | ||
| 308 | TS[4,3]: 1999-01-08 13:24:40.495 | ||
| 309 | +TS[4,4]: 1999-01-08 13:24:40.123456 | ||
| 310 | Date[5]: 1999-01-08 (N - F) | ||
| 311 | TS[5,0]: 1999-01-08 00:04:00 | ||
| 312 | TS[5,1]: 1999-01-08 01:59:00 | ||
| 313 | TS[5,2]: 1999-01-08 13:24:40 | ||
| 314 | TS[5,3]: 1999-01-08 13:24:40.495 | ||
| 315 | +TS[5,4]: 1999-01-08 13:24:40.123456 | ||
| 316 | Date[6]: 1999-01-18 (N - F) | ||
| 317 | TS[6,0]: 1999-01-18 00:04:00 | ||
| 318 | TS[6,1]: 1999-01-18 01:59:00 | ||
| 319 | TS[6,2]: 1999-01-18 13:24:40 | ||
| 320 | TS[6,3]: 1999-01-18 13:24:40.495 | ||
| 321 | +TS[6,4]: 1999-01-18 13:24:40.123456 | ||
| 322 | Date[7]: 2003-01-02 (N - F) | ||
| 323 | TS[7,0]: 2003-01-02 00:04:00 | ||
| 324 | TS[7,1]: 2003-01-02 01:59:00 | ||
| 325 | TS[7,2]: 2003-01-02 13:24:40 | ||
| 326 | TS[7,3]: 2003-01-02 13:24:40.495 | ||
| 327 | +TS[7,4]: 2003-01-02 13:24:40.123456 | ||
| 328 | Date[8]: 1999-01-08 (N - F) | ||
| 329 | TS[8,0]: 1999-01-08 00:04:00 | ||
| 330 | TS[8,1]: 1999-01-08 01:59:00 | ||
| 331 | TS[8,2]: 1999-01-08 13:24:40 | ||
| 332 | TS[8,3]: 1999-01-08 13:24:40.495 | ||
| 333 | +TS[8,4]: 1999-01-08 13:24:40.123456 | ||
| 334 | Date[9]: 1999-01-08 (N - F) | ||
| 335 | TS[9,0]: 1999-01-08 00:04:00 | ||
| 336 | TS[9,1]: 1999-01-08 01:59:00 | ||
| 337 | TS[9,2]: 1999-01-08 13:24:40 | ||
| 338 | TS[9,3]: 1999-01-08 13:24:40.495 | ||
| 339 | +TS[9,4]: 1999-01-08 13:24:40.123456 | ||
| 340 | Date[10]: 1999-01-08 (N - F) | ||
| 341 | TS[10,0]: 1999-01-08 00:04:00 | ||
| 342 | TS[10,1]: 1999-01-08 01:59:00 | ||
| 343 | TS[10,2]: 1999-01-08 13:24:40 | ||
| 344 | TS[10,3]: 1999-01-08 13:24:40.495 | ||
| 345 | +TS[10,4]: 1999-01-08 13:24:40.123456 | ||
| 346 | Date[11]: 1999-01-08 (N - F) | ||
| 347 | TS[11,0]: 1999-01-08 00:04:00 | ||
| 348 | TS[11,1]: 1999-01-08 01:59:00 | ||
| 349 | TS[11,2]: 1999-01-08 13:24:40 | ||
| 350 | TS[11,3]: 1999-01-08 13:24:40.495 | ||
| 351 | +TS[11,4]: 1999-01-08 13:24:40.123456 | ||
| 352 | Date[12]: 1999-01-08 (N - F) | ||
| 353 | TS[12,0]: 1999-01-08 00:04:00 | ||
| 354 | TS[12,1]: 1999-01-08 01:59:00 | ||
| 355 | TS[12,2]: 1999-01-08 13:24:40 | ||
| 356 | TS[12,3]: 1999-01-08 13:24:40.495 | ||
| 357 | +TS[12,4]: 1999-01-08 13:24:40.123456 | ||
| 358 | Date[13]: 2006-01-08 (N - F) | ||
| 359 | TS[13,0]: 2006-01-08 00:04:00 | ||
| 360 | TS[13,1]: 2006-01-08 01:59:00 | ||
| 361 | TS[13,2]: 2006-01-08 13:24:40 | ||
| 362 | TS[13,3]: 2006-01-08 13:24:40.495 | ||
| 363 | +TS[13,4]: 2006-01-08 13:24:40.123456 | ||
| 364 | Date[14]: 1999-01-08 (N - F) | ||
| 365 | TS[14,0]: 1999-01-08 00:04:00 | ||
| 366 | TS[14,1]: 1999-01-08 01:59:00 | ||
| 367 | TS[14,2]: 1999-01-08 13:24:40 | ||
| 368 | TS[14,3]: 1999-01-08 13:24:40.495 | ||
| 369 | +TS[14,4]: 1999-01-08 13:24:40.123456 | ||
| 370 | Date[15]: 1999-01-08 (N - F) | ||
| 371 | TS[15,0]: 1999-01-08 00:04:00 | ||
| 372 | TS[15,1]: 1999-01-08 01:59:00 | ||
| 373 | TS[15,2]: 1999-01-08 13:24:40 | ||
| 374 | TS[15,3]: 1999-01-08 13:24:40.495 | ||
| 375 | +TS[15,4]: 1999-01-08 13:24:40.123456 | ||
| 376 | Date[16]: 1999-01-08 (N - F) | ||
| 377 | TS[16,0]: 1999-01-08 00:04:00 | ||
| 378 | TS[16,1]: 1999-01-08 01:59:00 | ||
| 379 | TS[16,2]: 1999-01-08 13:24:40 | ||
| 380 | TS[16,3]: 1999-01-08 13:24:40.495 | ||
| 381 | +TS[16,4]: 1999-01-08 13:24:40.123456 | ||
| 382 | Date[17]: 1999-01-08 (N - F) | ||
| 383 | TS[17,0]: 1999-01-08 00:04:00 | ||
| 384 | TS[17,1]: 1999-01-08 01:59:00 | ||
| 385 | TS[17,2]: 1999-01-08 13:24:40 | ||
| 386 | TS[17,3]: 1999-01-08 13:24:40.495 | ||
| 387 | +TS[17,4]: 1999-01-08 13:24:40.123456 | ||
| 388 | Date[18]: 1999-01-08 (N - F) | ||
| 389 | TS[18,0]: 1999-01-08 00:04:00 | ||
| 390 | TS[18,1]: 1999-01-08 01:59:00 | ||
| 391 | TS[18,2]: 1999-01-08 13:24:40 | ||
| 392 | TS[18,3]: 1999-01-08 13:24:40.495 | ||
| 393 | +TS[18,4]: 1999-01-08 13:24:40.123456 | ||
| 394 | Date[19]: 0099-01-08 BC (N - F) | ||
| 395 | TS[19,0]: 0099-01-08 00:04:00 BC | ||
| 396 | TS[19,1]: 0099-01-08 01:59:00 BC | ||
| 397 | TS[19,2]: 0099-01-08 13:24:40 BC | ||
| 398 | +TS[19,4]: 0099-01-08 13:24:40.123456 BC | ||
| 399 | +Date[20]: - (N - T) | ||
| 400 | +Date[21]: - (N - T) | ||
| 401 | interval[0]: @ 1 min | ||
| 402 | interval_copy[0]: @ 1 min | ||
| 403 | interval[1]: @ 1 day 12 hours 59 mins 10 secs | ||
| 404 | diff --git a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc | ||
| 405 | index 0edf012..a127dd9 100644 | ||
| 406 | --- a/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc | ||
| 407 | +++ b/src/interfaces/ecpg/test/pgtypeslib/dt_test2.pgc | ||
| 408 | @@ -27,6 +27,15 @@ char *dates[] = { "19990108foobar", | ||
| 409 | "1999.008", | ||
| 410 | "J2451187", | ||
| 411 | "January 8, 99 BC", | ||
| 412 | + /* | ||
| 413 | + * Maximize space usage in ParseDateTime() with 25 | ||
| 414 | + * (MAXDATEFIELDS) fields and 128 (MAXDATELEN) total length. | ||
| 415 | + */ | ||
| 416 | + "........................Xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | ||
| 417 | + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
| 418 | + /* 26 fields */ | ||
| 419 | + ".........................aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" | ||
| 420 | + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", | ||
| 421 | NULL }; | ||
| 422 | |||
| 423 | /* do not conflict with libc "times" symbol */ | ||
| 424 | @@ -34,6 +43,7 @@ static char *times[] = { "0:04", | ||
| 425 | "1:59 PDT", | ||
| 426 | "13:24:40 -8:00", | ||
| 427 | "13:24:40.495+3", | ||
| 428 | + "13:24:40.123456789+3", | ||
| 429 | NULL }; | ||
| 430 | |||
| 431 | char *intervals[] = { "1 minute", | ||
| 432 | diff --git a/src/test/regress/expected/interval.out b/src/test/regress/expected/interval.out | ||
| 433 | index 3bf2211..99fd0ca 100644 | ||
| 434 | --- a/src/test/regress/expected/interval.out | ||
| 435 | +++ b/src/test/regress/expected/interval.out | ||
| 436 | @@ -306,6 +306,13 @@ select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 | ||
| 437 | @ 4541 years 4 mons 4 days 17 mins 31 secs | ||
| 438 | (1 row) | ||
| 439 | |||
| 440 | +-- test long interval output | ||
| 441 | +select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval; | ||
| 442 | + interval | ||
| 443 | +------------------------------------------------------------------------------------------- | ||
| 444 | + @ 100000000 years 10 mons -1000000000 days -1000000000 hours -10 mins -10.000001 secs ago | ||
| 445 | +(1 row) | ||
| 446 | + | ||
| 447 | -- test justify_hours() and justify_days() | ||
| 448 | SELECT justify_hours(interval '6 months 3 days 52 hours 3 minutes 2 seconds') as "6 mons 5 days 4 hours 3 mins 2 seconds"; | ||
| 449 | 6 mons 5 days 4 hours 3 mins 2 seconds | ||
| 450 | diff --git a/src/test/regress/sql/interval.sql b/src/test/regress/sql/interval.sql | ||
| 451 | index f1da4c2..7cee286 100644 | ||
| 452 | --- a/src/test/regress/sql/interval.sql | ||
| 453 | +++ b/src/test/regress/sql/interval.sql | ||
| 454 | @@ -108,6 +108,8 @@ select avg(f1) from interval_tbl; | ||
| 455 | -- test long interval input | ||
| 456 | select '4 millenniums 5 centuries 4 decades 1 year 4 months 4 days 17 minutes 31 seconds'::interval; | ||
| 457 | |||
| 458 | +-- test long interval output | ||
| 459 | +select '100000000y 10mon -1000000000d -1000000000h -10min -10.000001s ago'::interval; | ||
| 460 | |||
| 461 | -- test justify_hours() and justify_days() | ||
| 462 | |||
| 463 | -- | ||
| 464 | 1.7.5.4 | ||
| 465 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0007-Make-pqsignal-available-to-pg_regress-of-ECPG-and-is.patch b/meta-oe/recipes-support/postgresql/files/0007-Make-pqsignal-available-to-pg_regress-of-ECPG-and-is.patch deleted file mode 100644 index 3cffc0a852..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0007-Make-pqsignal-available-to-pg_regress-of-ECPG-and-is.patch +++ /dev/null | |||
| @@ -1,75 +0,0 @@ | |||
| 1 | From 0ae841a98c21c53901d5bc9a9323a8cc800364f6 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Noah Misch <noah@leadboat.com> | ||
| 3 | Date: Sat, 14 Jun 2014 10:52:25 -0400 | ||
| 4 | Subject: [PATCH] Make pqsignal() available to pg_regress of ECPG and | ||
| 5 | isolation suites. | ||
| 6 | |||
| 7 | commit 0ae841a98c21c53901d5bc9a9323a8cc800364f6 REL9_2_STABLE | ||
| 8 | |||
| 9 | Commit 453a5d91d49e4d35054f92785d830df4067e10c1 made it available to the | ||
| 10 | src/test/regress build of pg_regress, but all pg_regress builds need the | ||
| 11 | same treatment. Patch 9.2 through 8.4; in 9.3 and later, pg_regress | ||
| 12 | gets pqsignal() via libpgport. | ||
| 13 | |||
| 14 | |||
| 15 | Upstream-Status: Backport | ||
| 16 | |||
| 17 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 18 | --- | ||
| 19 | src/interfaces/ecpg/test/Makefile | 4 ++-- | ||
| 20 | src/test/isolation/Makefile | 12 +++++++----- | ||
| 21 | 2 files changed, 9 insertions(+), 7 deletions(-) | ||
| 22 | |||
| 23 | diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile | ||
| 24 | index e9944c6..4bb9525 100644 | ||
| 25 | --- a/src/interfaces/ecpg/test/Makefile | ||
| 26 | +++ b/src/interfaces/ecpg/test/Makefile | ||
| 27 | @@ -47,10 +47,10 @@ clean distclean maintainer-clean: | ||
| 28 | |||
| 29 | all: pg_regress$(X) | ||
| 30 | |||
| 31 | -pg_regress$(X): pg_regress_ecpg.o $(top_builddir)/src/test/regress/pg_regress.o | ||
| 32 | +pg_regress$(X): pg_regress_ecpg.o $(top_builddir)/src/test/regress/pg_regress.o $(top_builddir)/src/test/regress/pqsignal.o | ||
| 33 | $(CC) $(CFLAGS) $(LDFLAGS) $(LDFLAGS_EX) $^ $(LIBS) -o $@ | ||
| 34 | |||
| 35 | -$(top_builddir)/src/test/regress/pg_regress.o: | ||
| 36 | +$(top_builddir)/src/test/regress/pg_regress.o $(top_builddir)/src/test/regress/pqsignal.o: | ||
| 37 | $(MAKE) -C $(dir $@) $(notdir $@) | ||
| 38 | |||
| 39 | # dependencies ensure that path changes propagate | ||
| 40 | diff --git a/src/test/isolation/Makefile b/src/test/isolation/Makefile | ||
| 41 | index 46ea6f0..e20ba48 100644 | ||
| 42 | --- a/src/test/isolation/Makefile | ||
| 43 | +++ b/src/test/isolation/Makefile | ||
| 44 | @@ -15,13 +15,15 @@ OBJS = specparse.o isolationtester.o | ||
| 45 | |||
| 46 | all: isolationtester$(X) pg_isolation_regress$(X) | ||
| 47 | |||
| 48 | -submake-regress: | ||
| 49 | +pg_regress.o: | ||
| 50 | $(MAKE) -C $(top_builddir)/src/test/regress pg_regress.o | ||
| 51 | - | ||
| 52 | -pg_regress.o: | submake-regress | ||
| 53 | rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/pg_regress.o . | ||
| 54 | |||
| 55 | -pg_isolation_regress$(X): isolation_main.o pg_regress.o | ||
| 56 | +pqsignal.o: | ||
| 57 | + $(MAKE) -C $(top_builddir)/src/test/regress pqsignal.o | ||
| 58 | + rm -f $@ && $(LN_S) $(top_builddir)/src/test/regress/pqsignal.o . | ||
| 59 | + | ||
| 60 | +pg_isolation_regress$(X): isolation_main.o pg_regress.o pqsignal.o | ||
| 61 | $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@ | ||
| 62 | |||
| 63 | isolationtester$(X): $(OBJS) | submake-libpq submake-libpgport | ||
| 64 | @@ -59,7 +61,7 @@ endif | ||
| 65 | # so do not clean them here | ||
| 66 | clean distclean: | ||
| 67 | rm -f isolationtester$(X) pg_isolation_regress$(X) $(OBJS) isolation_main.o | ||
| 68 | - rm -f pg_regress.o | ||
| 69 | + rm -f pg_regress.o pqsignal.o | ||
| 70 | rm -rf $(pg_regress_clean_files) | ||
| 71 | |||
| 72 | maintainer-clean: distclean | ||
| 73 | -- | ||
| 74 | 1.7.5.4 | ||
| 75 | |||
diff --git a/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch b/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch deleted file mode 100644 index 62ec9354d4..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0008-Prevent-potential-overruns-of-fixed-size-buffers.patch +++ /dev/null | |||
| @@ -1,393 +0,0 @@ | |||
| 1 | From 655b665f745e2e07cf6936c6063b0250f5caa98f Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Tom Lane <tgl@sss.pgh.pa.us> | ||
| 3 | Date: Mon, 17 Feb 2014 11:20:27 -0500 | ||
| 4 | Subject: [PATCH] Prevent potential overruns of fixed-size buffers. | ||
| 5 | |||
| 6 | commit 655b665f745e2e07cf6936c6063b0250f5caa98f REL9_2_STABLE | ||
| 7 | |||
| 8 | Coverity identified a number of places in which it couldn't prove that a | ||
| 9 | string being copied into a fixed-size buffer would fit. We believe that | ||
| 10 | most, perhaps all of these are in fact safe, or are copying data that is | ||
| 11 | coming from a trusted source so that any overrun is not really a security | ||
| 12 | issue. Nonetheless it seems prudent to forestall any risk by using | ||
| 13 | strlcpy() and similar functions. | ||
| 14 | |||
| 15 | Fixes by Peter Eisentraut and Jozef Mlich based on Coverity reports. | ||
| 16 | |||
| 17 | In addition, fix a potential null-pointer-dereference crash in | ||
| 18 | contrib/chkpass. The crypt(3) function is defined to return NULL on | ||
| 19 | failure, but chkpass.c didn't check for that before using the result. | ||
| 20 | The main practical case in which this could be an issue is if libc is | ||
| 21 | configured to refuse to execute unapproved hashing algorithms (e.g., | ||
| 22 | "FIPS mode"). This ideally should've been a separate commit, but | ||
| 23 | since it touches code adjacent to one of the buffer overrun changes, | ||
| 24 | I included it in this commit to avoid last-minute merge issues. | ||
| 25 | This issue was reported by Honza Horak. | ||
| 26 | |||
| 27 | Security: CVE-2014-0065 for buffer overruns, CVE-2014-0066 for crypt() | ||
| 28 | |||
| 29 | Upsteam-Status: Backport | ||
| 30 | |||
| 31 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 32 | --- | ||
| 33 | contrib/chkpass/chkpass.c | 29 ++++++++++++++++++++++++++--- | ||
| 34 | contrib/pg_standby/pg_standby.c | 2 +- | ||
| 35 | src/backend/access/transam/xlog.c | 10 +++++----- | ||
| 36 | src/backend/tsearch/spell.c | 2 +- | ||
| 37 | src/backend/utils/adt/datetime.c | 11 ++++++----- | ||
| 38 | src/bin/initdb/findtimezone.c | 4 ++-- | ||
| 39 | src/bin/pg_basebackup/pg_basebackup.c | 8 ++++---- | ||
| 40 | src/interfaces/ecpg/preproc/pgc.l | 2 +- | ||
| 41 | src/interfaces/libpq/fe-protocol2.c | 2 +- | ||
| 42 | src/interfaces/libpq/fe-protocol3.c | 2 +- | ||
| 43 | src/port/exec.c | 4 ++-- | ||
| 44 | src/test/regress/pg_regress.c | 6 +++--- | ||
| 45 | src/timezone/pgtz.c | 2 +- | ||
| 46 | 13 files changed, 54 insertions(+), 30 deletions(-) | ||
| 47 | |||
| 48 | diff --git a/contrib/chkpass/chkpass.c b/contrib/chkpass/chkpass.c | ||
| 49 | index 0c9fec0..1795b8c 100644 | ||
| 50 | --- a/contrib/chkpass/chkpass.c | ||
| 51 | +++ b/contrib/chkpass/chkpass.c | ||
| 52 | @@ -70,6 +70,7 @@ chkpass_in(PG_FUNCTION_ARGS) | ||
| 53 | char *str = PG_GETARG_CSTRING(0); | ||
| 54 | chkpass *result; | ||
| 55 | char mysalt[4]; | ||
| 56 | + char *crypt_output; | ||
| 57 | static char salt_chars[] = | ||
| 58 | "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | ||
| 59 | |||
| 60 | @@ -92,7 +93,15 @@ chkpass_in(PG_FUNCTION_ARGS) | ||
| 61 | mysalt[1] = salt_chars[random() & 0x3f]; | ||
| 62 | mysalt[2] = 0; /* technically the terminator is not necessary | ||
| 63 | * but I like to play safe */ | ||
| 64 | - strcpy(result->password, crypt(str, mysalt)); | ||
| 65 | + | ||
| 66 | + crypt_output = crypt(str, mysalt); | ||
| 67 | + if (crypt_output == NULL) | ||
| 68 | + ereport(ERROR, | ||
| 69 | + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
| 70 | + errmsg("crypt() failed"))); | ||
| 71 | + | ||
| 72 | + strlcpy(result->password, crypt_output, sizeof(result->password)); | ||
| 73 | + | ||
| 74 | PG_RETURN_POINTER(result); | ||
| 75 | } | ||
| 76 | |||
| 77 | @@ -141,9 +150,16 @@ chkpass_eq(PG_FUNCTION_ARGS) | ||
| 78 | chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0); | ||
| 79 | text *a2 = PG_GETARG_TEXT_PP(1); | ||
| 80 | char str[9]; | ||
| 81 | + char *crypt_output; | ||
| 82 | |||
| 83 | text_to_cstring_buffer(a2, str, sizeof(str)); | ||
| 84 | - PG_RETURN_BOOL(strcmp(a1->password, crypt(str, a1->password)) == 0); | ||
| 85 | + crypt_output = crypt(str, a1->password); | ||
| 86 | + if (crypt_output == NULL) | ||
| 87 | + ereport(ERROR, | ||
| 88 | + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
| 89 | + errmsg("crypt() failed"))); | ||
| 90 | + | ||
| 91 | + PG_RETURN_BOOL(strcmp(a1->password, crypt_output) == 0); | ||
| 92 | } | ||
| 93 | |||
| 94 | PG_FUNCTION_INFO_V1(chkpass_ne); | ||
| 95 | @@ -153,7 +169,14 @@ chkpass_ne(PG_FUNCTION_ARGS) | ||
| 96 | chkpass *a1 = (chkpass *) PG_GETARG_POINTER(0); | ||
| 97 | text *a2 = PG_GETARG_TEXT_PP(1); | ||
| 98 | char str[9]; | ||
| 99 | + char *crypt_output; | ||
| 100 | |||
| 101 | text_to_cstring_buffer(a2, str, sizeof(str)); | ||
| 102 | - PG_RETURN_BOOL(strcmp(a1->password, crypt(str, a1->password)) != 0); | ||
| 103 | + crypt_output = crypt(str, a1->password); | ||
| 104 | + if (crypt_output == NULL) | ||
| 105 | + ereport(ERROR, | ||
| 106 | + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||
| 107 | + errmsg("crypt() failed"))); | ||
| 108 | + | ||
| 109 | + PG_RETURN_BOOL(strcmp(a1->password, crypt_output) != 0); | ||
| 110 | } | ||
| 111 | diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c | ||
| 112 | index 84941ed..0f1e0c1 100644 | ||
| 113 | --- a/contrib/pg_standby/pg_standby.c | ||
| 114 | +++ b/contrib/pg_standby/pg_standby.c | ||
| 115 | @@ -338,7 +338,7 @@ SetWALFileNameForCleanup(void) | ||
| 116 | if (strcmp(restartWALFileName, nextWALFileName) > 0) | ||
| 117 | return false; | ||
| 118 | |||
| 119 | - strcpy(exclusiveCleanupFileName, restartWALFileName); | ||
| 120 | + strlcpy(exclusiveCleanupFileName, restartWALFileName, sizeof(exclusiveCleanupFileName)); | ||
| 121 | return true; | ||
| 122 | } | ||
| 123 | |||
| 124 | diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c | ||
| 125 | index d639c4a..49bb453 100644 | ||
| 126 | --- a/src/backend/access/transam/xlog.c | ||
| 127 | +++ b/src/backend/access/transam/xlog.c | ||
| 128 | @@ -3017,7 +3017,7 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname) | ||
| 129 | xlogfpath, oldpath))); | ||
| 130 | } | ||
| 131 | #else | ||
| 132 | - strncpy(oldpath, xlogfpath, MAXPGPATH); | ||
| 133 | + strlcpy(oldpath, xlogfpath, MAXPGPATH); | ||
| 134 | #endif | ||
| 135 | if (unlink(oldpath) != 0) | ||
| 136 | ereport(FATAL, | ||
| 137 | @@ -5913,7 +5913,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) | ||
| 138 | |||
| 139 | recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); | ||
| 140 | recordXtime = recordRestorePointData->rp_time; | ||
| 141 | - strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); | ||
| 142 | + strlcpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); | ||
| 143 | } | ||
| 144 | else | ||
| 145 | return false; | ||
| 146 | @@ -6008,7 +6008,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) | ||
| 147 | } | ||
| 148 | else | ||
| 149 | { | ||
| 150 | - strncpy(recoveryStopName, recordRPName, MAXFNAMELEN); | ||
| 151 | + strlcpy(recoveryStopName, recordRPName, MAXFNAMELEN); | ||
| 152 | |||
| 153 | ereport(LOG, | ||
| 154 | (errmsg("recovery stopping at restore point \"%s\", time %s", | ||
| 155 | @@ -6348,7 +6348,7 @@ StartupXLOG(void) | ||
| 156 | * see them | ||
| 157 | */ | ||
| 158 | XLogCtl->RecoveryTargetTLI = recoveryTargetTLI; | ||
| 159 | - strncpy(XLogCtl->archiveCleanupCommand, | ||
| 160 | + strlcpy(XLogCtl->archiveCleanupCommand, | ||
| 161 | archiveCleanupCommand ? archiveCleanupCommand : "", | ||
| 162 | sizeof(XLogCtl->archiveCleanupCommand)); | ||
| 163 | |||
| 164 | @@ -8760,7 +8760,7 @@ XLogRestorePoint(const char *rpName) | ||
| 165 | xl_restore_point xlrec; | ||
| 166 | |||
| 167 | xlrec.rp_time = GetCurrentTimestamp(); | ||
| 168 | - strncpy(xlrec.rp_name, rpName, MAXFNAMELEN); | ||
| 169 | + strlcpy(xlrec.rp_name, rpName, MAXFNAMELEN); | ||
| 170 | |||
| 171 | rdata.buffer = InvalidBuffer; | ||
| 172 | rdata.data = (char *) &xlrec; | ||
| 173 | diff --git a/src/backend/tsearch/spell.c b/src/backend/tsearch/spell.c | ||
| 174 | index 449aa6a..4acc33e 100644 | ||
| 175 | --- a/src/backend/tsearch/spell.c | ||
| 176 | +++ b/src/backend/tsearch/spell.c | ||
| 177 | @@ -255,7 +255,7 @@ NIAddSpell(IspellDict *Conf, const char *word, const char *flag) | ||
| 178 | } | ||
| 179 | Conf->Spell[Conf->nspell] = (SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1); | ||
| 180 | strcpy(Conf->Spell[Conf->nspell]->word, word); | ||
| 181 | - strncpy(Conf->Spell[Conf->nspell]->p.flag, flag, MAXFLAGLEN); | ||
| 182 | + strlcpy(Conf->Spell[Conf->nspell]->p.flag, flag, MAXFLAGLEN); | ||
| 183 | Conf->nspell++; | ||
| 184 | } | ||
| 185 | |||
| 186 | diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c | ||
| 187 | index 4763a6f..4105f17 100644 | ||
| 188 | --- a/src/backend/utils/adt/datetime.c | ||
| 189 | +++ b/src/backend/utils/adt/datetime.c | ||
| 190 | @@ -90,10 +90,10 @@ char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", | ||
| 191 | * Note that this table must be strictly alphabetically ordered to allow an | ||
| 192 | * O(ln(N)) search algorithm to be used. | ||
| 193 | * | ||
| 194 | - * The text field is NOT guaranteed to be NULL-terminated. | ||
| 195 | + * The token field is NOT guaranteed to be NULL-terminated. | ||
| 196 | * | ||
| 197 | - * To keep this table reasonably small, we divide the lexval for TZ and DTZ | ||
| 198 | - * entries by 15 (so they are on 15 minute boundaries) and truncate the text | ||
| 199 | + * To keep this table reasonably small, we divide the value for TZ and DTZ | ||
| 200 | + * entries by 15 (so they are on 15 minute boundaries) and truncate the token | ||
| 201 | * field at TOKMAXLEN characters. | ||
| 202 | * Formerly, we divided by 10 rather than 15 but there are a few time zones | ||
| 203 | * which are 30 or 45 minutes away from an even hour, most are on an hour | ||
| 204 | @@ -108,7 +108,7 @@ static datetkn *timezonetktbl = NULL; | ||
| 205 | static int sztimezonetktbl = 0; | ||
| 206 | |||
| 207 | static const datetkn datetktbl[] = { | ||
| 208 | -/* text, token, lexval */ | ||
| 209 | + /* token, type, value */ | ||
| 210 | {EARLY, RESERV, DTK_EARLY}, /* "-infinity" reserved for "early time" */ | ||
| 211 | {DA_D, ADBC, AD}, /* "ad" for years > 0 */ | ||
| 212 | {"allballs", RESERV, DTK_ZULU}, /* 00:00:00 */ | ||
| 213 | @@ -188,7 +188,7 @@ static const datetkn datetktbl[] = { | ||
| 214 | static int szdatetktbl = sizeof datetktbl / sizeof datetktbl[0]; | ||
| 215 | |||
| 216 | static datetkn deltatktbl[] = { | ||
| 217 | - /* text, token, lexval */ | ||
| 218 | + /* token, type, value */ | ||
| 219 | {"@", IGNORE_DTF, 0}, /* postgres relative prefix */ | ||
| 220 | {DAGO, AGO, 0}, /* "ago" indicates negative time offset */ | ||
| 221 | {"c", UNITS, DTK_CENTURY}, /* "century" relative */ | ||
| 222 | @@ -4201,6 +4201,7 @@ ConvertTimeZoneAbbrevs(TimeZoneAbbrevTable *tbl, | ||
| 223 | tbl->numabbrevs = n; | ||
| 224 | for (i = 0; i < n; i++) | ||
| 225 | { | ||
| 226 | + /* do NOT use strlcpy here; token field need not be null-terminated */ | ||
| 227 | strncpy(newtbl[i].token, abbrevs[i].abbrev, TOKMAXLEN); | ||
| 228 | newtbl[i].type = abbrevs[i].is_dst ? DTZ : TZ; | ||
| 229 | TOVAL(&newtbl[i], abbrevs[i].offset / MINS_PER_HOUR); | ||
| 230 | diff --git a/src/bin/initdb/findtimezone.c b/src/bin/initdb/findtimezone.c | ||
| 231 | index 6d6f96a..6d38151 100644 | ||
| 232 | --- a/src/bin/initdb/findtimezone.c | ||
| 233 | +++ b/src/bin/initdb/findtimezone.c | ||
| 234 | @@ -68,7 +68,7 @@ pg_open_tzfile(const char *name, char *canonname) | ||
| 235 | if (canonname) | ||
| 236 | strlcpy(canonname, name, TZ_STRLEN_MAX + 1); | ||
| 237 | |||
| 238 | - strcpy(fullname, pg_TZDIR()); | ||
| 239 | + strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); | ||
| 240 | if (strlen(fullname) + 1 + strlen(name) >= MAXPGPATH) | ||
| 241 | return -1; /* not gonna fit */ | ||
| 242 | strcat(fullname, "/"); | ||
| 243 | @@ -375,7 +375,7 @@ identify_system_timezone(void) | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Search for the best-matching timezone file */ | ||
| 247 | - strcpy(tmptzdir, pg_TZDIR()); | ||
| 248 | + strlcpy(tmptzdir, pg_TZDIR(), sizeof(tmptzdir)); | ||
| 249 | bestscore = -1; | ||
| 250 | resultbuf[0] = '\0'; | ||
| 251 | scan_available_timezones(tmptzdir, tmptzdir + strlen(tmptzdir) + 1, | ||
| 252 | diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c | ||
| 253 | index 9d840a1..26cc758 100644 | ||
| 254 | --- a/src/bin/pg_basebackup/pg_basebackup.c | ||
| 255 | +++ b/src/bin/pg_basebackup/pg_basebackup.c | ||
| 256 | @@ -735,9 +735,9 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) | ||
| 257 | FILE *file = NULL; | ||
| 258 | |||
| 259 | if (PQgetisnull(res, rownum, 0)) | ||
| 260 | - strcpy(current_path, basedir); | ||
| 261 | + strlcpy(current_path, basedir, sizeof(current_path)); | ||
| 262 | else | ||
| 263 | - strcpy(current_path, PQgetvalue(res, rownum, 1)); | ||
| 264 | + strlcpy(current_path, PQgetvalue(res, rownum, 1), sizeof(current_path)); | ||
| 265 | |||
| 266 | /* | ||
| 267 | * Get the COPY data | ||
| 268 | @@ -1053,7 +1053,7 @@ BaseBackup(void) | ||
| 269 | progname); | ||
| 270 | disconnect_and_exit(1); | ||
| 271 | } | ||
| 272 | - strcpy(xlogstart, PQgetvalue(res, 0, 0)); | ||
| 273 | + strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart)); | ||
| 274 | if (verbose && includewal) | ||
| 275 | fprintf(stderr, "transaction log start point: %s\n", xlogstart); | ||
| 276 | PQclear(res); | ||
| 277 | @@ -1153,7 +1153,7 @@ BaseBackup(void) | ||
| 278 | progname); | ||
| 279 | disconnect_and_exit(1); | ||
| 280 | } | ||
| 281 | - strcpy(xlogend, PQgetvalue(res, 0, 0)); | ||
| 282 | + strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend)); | ||
| 283 | if (verbose && includewal) | ||
| 284 | fprintf(stderr, "transaction log end point: %s\n", xlogend); | ||
| 285 | PQclear(res); | ||
| 286 | diff --git a/src/interfaces/ecpg/preproc/pgc.l b/src/interfaces/ecpg/preproc/pgc.l | ||
| 287 | index f2e7edd..7ae8556 100644 | ||
| 288 | --- a/src/interfaces/ecpg/preproc/pgc.l | ||
| 289 | +++ b/src/interfaces/ecpg/preproc/pgc.l | ||
| 290 | @@ -1315,7 +1315,7 @@ parse_include(void) | ||
| 291 | yytext[i] = '\0'; | ||
| 292 | memmove(yytext, yytext+1, strlen(yytext)); | ||
| 293 | |||
| 294 | - strncpy(inc_file, yytext, sizeof(inc_file)); | ||
| 295 | + strlcpy(inc_file, yytext, sizeof(inc_file)); | ||
| 296 | yyin = fopen(inc_file, "r"); | ||
| 297 | if (!yyin) | ||
| 298 | { | ||
| 299 | diff --git a/src/interfaces/libpq/fe-protocol2.c b/src/interfaces/libpq/fe-protocol2.c | ||
| 300 | index 1ba5885..af4c412 100644 | ||
| 301 | --- a/src/interfaces/libpq/fe-protocol2.c | ||
| 302 | +++ b/src/interfaces/libpq/fe-protocol2.c | ||
| 303 | @@ -500,7 +500,7 @@ pqParseInput2(PGconn *conn) | ||
| 304 | if (!conn->result) | ||
| 305 | return; | ||
| 306 | } | ||
| 307 | - strncpy(conn->result->cmdStatus, conn->workBuffer.data, | ||
| 308 | + strlcpy(conn->result->cmdStatus, conn->workBuffer.data, | ||
| 309 | CMDSTATUS_LEN); | ||
| 310 | checkXactStatus(conn, conn->workBuffer.data); | ||
| 311 | conn->asyncStatus = PGASYNC_READY; | ||
| 312 | diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c | ||
| 313 | index d289f82..6f8a470 100644 | ||
| 314 | --- a/src/interfaces/libpq/fe-protocol3.c | ||
| 315 | +++ b/src/interfaces/libpq/fe-protocol3.c | ||
| 316 | @@ -206,7 +206,7 @@ pqParseInput3(PGconn *conn) | ||
| 317 | if (!conn->result) | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | - strncpy(conn->result->cmdStatus, conn->workBuffer.data, | ||
| 321 | + strlcpy(conn->result->cmdStatus, conn->workBuffer.data, | ||
| 322 | CMDSTATUS_LEN); | ||
| 323 | conn->asyncStatus = PGASYNC_READY; | ||
| 324 | break; | ||
| 325 | diff --git a/src/port/exec.c b/src/port/exec.c | ||
| 326 | index c79e8ba..0726dbe 100644 | ||
| 327 | --- a/src/port/exec.c | ||
| 328 | +++ b/src/port/exec.c | ||
| 329 | @@ -66,7 +66,7 @@ validate_exec(const char *path) | ||
| 330 | if (strlen(path) >= strlen(".exe") && | ||
| 331 | pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0) | ||
| 332 | { | ||
| 333 | - strcpy(path_exe, path); | ||
| 334 | + strlcpy(path_exe, path, sizeof(path_exe) - 4); | ||
| 335 | strcat(path_exe, ".exe"); | ||
| 336 | path = path_exe; | ||
| 337 | } | ||
| 338 | @@ -275,7 +275,7 @@ resolve_symlinks(char *path) | ||
| 339 | } | ||
| 340 | |||
| 341 | /* must copy final component out of 'path' temporarily */ | ||
| 342 | - strcpy(link_buf, fname); | ||
| 343 | + strlcpy(link_buf, fname, sizeof(link_buf)); | ||
| 344 | |||
| 345 | if (!getcwd(path, MAXPGPATH)) | ||
| 346 | { | ||
| 347 | diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c | ||
| 348 | index d991a5c..a6466eb 100644 | ||
| 349 | --- a/src/test/regress/pg_regress.c | ||
| 350 | +++ b/src/test/regress/pg_regress.c | ||
| 351 | @@ -1233,7 +1233,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul | ||
| 352 | */ | ||
| 353 | platform_expectfile = get_expectfile(testname, resultsfile); | ||
| 354 | |||
| 355 | - strcpy(expectfile, default_expectfile); | ||
| 356 | + strlcpy(expectfile, default_expectfile, sizeof(expectfile)); | ||
| 357 | if (platform_expectfile) | ||
| 358 | { | ||
| 359 | /* | ||
| 360 | @@ -1288,7 +1288,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul | ||
| 361 | { | ||
| 362 | /* This diff was a better match than the last one */ | ||
| 363 | best_line_count = l; | ||
| 364 | - strcpy(best_expect_file, alt_expectfile); | ||
| 365 | + strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file)); | ||
| 366 | } | ||
| 367 | free(alt_expectfile); | ||
| 368 | } | ||
| 369 | @@ -1316,7 +1316,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul | ||
| 370 | { | ||
| 371 | /* This diff was a better match than the last one */ | ||
| 372 | best_line_count = l; | ||
| 373 | - strcpy(best_expect_file, default_expectfile); | ||
| 374 | + strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file)); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c | ||
| 379 | index d5bc83e..80c5635 100644 | ||
| 380 | --- a/src/timezone/pgtz.c | ||
| 381 | +++ b/src/timezone/pgtz.c | ||
| 382 | @@ -83,7 +83,7 @@ pg_open_tzfile(const char *name, char *canonname) | ||
| 383 | * Loop to split the given name into directory levels; for each level, | ||
| 384 | * search using scan_directory_ci(). | ||
| 385 | */ | ||
| 386 | - strcpy(fullname, pg_TZDIR()); | ||
| 387 | + strlcpy(fullname, pg_TZDIR(), sizeof(fullname)); | ||
| 388 | orignamelen = fullnamelen = strlen(fullname); | ||
| 389 | fname = name; | ||
| 390 | for (;;) | ||
| 391 | -- | ||
| 392 | 1.7.5.4 | ||
| 393 | |||
diff --git a/meta-oe/recipes-support/postgresql/postgresql-9.2.4/ecpg-parallel-make-fix.patch b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/ecpg-parallel-make-fix.patch index 63615cd517..63615cd517 100644 --- a/meta-oe/recipes-support/postgresql/postgresql-9.2.4/ecpg-parallel-make-fix.patch +++ b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/ecpg-parallel-make-fix.patch | |||
diff --git a/meta-oe/recipes-support/postgresql/postgresql-9.4.2/not-check-libperl.patch b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/not-check-libperl.patch new file mode 100644 index 0000000000..e199f39143 --- /dev/null +++ b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/not-check-libperl.patch | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | [PATCH] not check libperl under cross compiling | ||
| 2 | |||
| 3 | Upstream-Status: Inappropriate [configuration] | ||
| 4 | |||
| 5 | libperl ldflags returned by PGAC_CHECK_PERL_EMBED_LDFLAGS are native, | ||
| 6 | can not be used to check target library. | ||
| 7 | |||
| 8 | postpresql has the dependency on perl, so not need to check libperl | ||
| 9 | again, like in postgresql-9.2.4 | ||
| 10 | |||
| 11 | Signed-off-by: Roy Li <rongqing.li@windriver.com> | ||
| 12 | --- | ||
| 13 | configure.in | 2 +- | ||
| 14 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 15 | |||
| 16 | diff --git a/configure.in b/configure.in | ||
| 17 | index ae1a5a0..9a0970d 100644 | ||
| 18 | --- a/configure.in | ||
| 19 | +++ b/configure.in | ||
| 20 | @@ -1877,7 +1877,7 @@ if test "$with_tcl" = yes; then | ||
| 21 | fi | ||
| 22 | |||
| 23 | # check for <perl.h> | ||
| 24 | -if test "$with_perl" = yes; then | ||
| 25 | +if test "$with_perl" = yes && test "$cross_compiling" = no; then | ||
| 26 | ac_save_CPPFLAGS=$CPPFLAGS | ||
| 27 | CPPFLAGS="$CPPFLAGS -I$perl_archlibexp/CORE" | ||
| 28 | AC_CHECK_HEADER(perl.h, [], [AC_MSG_ERROR([header file <perl.h> is required for Perl])], | ||
| 29 | -- | ||
| 30 | 1.9.1 | ||
| 31 | |||
diff --git a/meta-oe/recipes-support/postgresql/postgresql-9.2.4/remove.autoconf.version.check.patch b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/remove.autoconf.version.check.patch index 022aa3d760..be23fd419d 100644 --- a/meta-oe/recipes-support/postgresql/postgresql-9.2.4/remove.autoconf.version.check.patch +++ b/meta-oe/recipes-support/postgresql/postgresql-9.4.2/remove.autoconf.version.check.patch | |||
| @@ -4,12 +4,13 @@ Index: postgresql-9.2.4/configure.in | |||
| 4 | +++ postgresql-9.2.4/configure.in | 4 | +++ postgresql-9.2.4/configure.in |
| 5 | @@ -19,10 +19,6 @@ m4_pattern_forbid(^PGAC_)dnl to catch un | 5 | @@ -19,10 +19,6 @@ m4_pattern_forbid(^PGAC_)dnl to catch un |
| 6 | 6 | ||
| 7 | AC_INIT([PostgreSQL], [9.2.4], [pgsql-bugs@postgresql.org]) | 7 | AC_INIT([PostgreSQL], [9.4.2], [pgsql-bugs@postgresql.org]) |
| 8 | 8 | ||
| 9 | -m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.63], [], [m4_fatal([Autoconf version 2.63 is required. | 9 | -m4_if(m4_defn([m4_PACKAGE_VERSION]), [2.69], [], [m4_fatal([Autoconf version 2.69 is required. |
| 10 | -Untested combinations of 'autoconf' and PostgreSQL versions are not | 10 | -Untested combinations of 'autoconf' and PostgreSQL versions are not |
| 11 | -recommended. You can remove the check from 'configure.in' but it is then | 11 | -recommended. You can remove the check from 'configure.in' but it is then |
| 12 | -your responsibility whether the result works or not.])]) | 12 | -your responsibility whether the result works or not.])]) |
| 13 | AC_COPYRIGHT([Copyright (c) 1996-2012, PostgreSQL Global Development Group]) | 13 | AC_COPYRIGHT([Copyright (c) 1996-2014, PostgreSQL Global Development Group]) |
| 14 | AC_CONFIG_SRCDIR([src/backend/access/common/heaptuple.c]) | 14 | AC_CONFIG_SRCDIR([src/backend/access/common/heaptuple.c]) |
| 15 | AC_CONFIG_AUX_DIR(config) | 15 | AC_CONFIG_AUX_DIR(config) |
| 16 | |||
diff --git a/meta-oe/recipes-support/postgresql/postgresql.inc b/meta-oe/recipes-support/postgresql/postgresql.inc index 1397f564de..c7aed9ec4e 100644 --- a/meta-oe/recipes-support/postgresql/postgresql.inc +++ b/meta-oe/recipes-support/postgresql/postgresql.inc | |||
| @@ -31,13 +31,6 @@ SRC_URI = "http://ftp.postgresql.org/pub/source/v${PV}/${BP}.tar.bz2 \ | |||
| 31 | file://postgresql-setup \ | 31 | file://postgresql-setup \ |
| 32 | file://postgresql.service \ | 32 | file://postgresql.service \ |
| 33 | file://0001-Use-pkg-config-for-libxml2-detection.patch \ | 33 | file://0001-Use-pkg-config-for-libxml2-detection.patch \ |
| 34 | file://0002-Predict-integer-overflow-to-avoid-buffer-overruns.patch \ | ||
| 35 | file://0003-Shore-up-ADMIN-OPTION-restrictions.patch \ | ||
| 36 | file://0004-Prevent-privilege-escalation-in-explicit-calls-to-PL.patch \ | ||
| 37 | file://0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch \ | ||
| 38 | file://0006-Fix-handling-of-wide-datetime-input-output.patch \ | ||
| 39 | file://0007-Make-pqsignal-available-to-pg_regress-of-ECPG-and-is.patch \ | ||
| 40 | file://0008-Prevent-potential-overruns-of-fixed-size-buffers.patch \ | ||
| 41 | " | 34 | " |
| 42 | 35 | ||
| 43 | LEAD_SONAME = "libpq.so" | 36 | LEAD_SONAME = "libpq.so" |
| @@ -76,7 +69,6 @@ PACKAGECONFIG[perl] = "--with-perl,--without-perl,perl,perl" | |||
| 76 | EXTRA_OECONF += "--enable-thread-safety --disable-rpath \ | 69 | EXTRA_OECONF += "--enable-thread-safety --disable-rpath \ |
| 77 | --datadir=${datadir}/${BPN} \ | 70 | --datadir=${datadir}/${BPN} \ |
| 78 | --sysconfdir=${sysconfdir}/${BPN} \ | 71 | --sysconfdir=${sysconfdir}/${BPN} \ |
| 79 | --without-krb5 \ | ||
| 80 | " | 72 | " |
| 81 | EXTRA_OECONF_sh4 += "--disable-spinlocks" | 73 | EXTRA_OECONF_sh4 += "--disable-spinlocks" |
| 82 | EXTRA_OECONF_aarch64 += "--disable-spinlocks" | 74 | EXTRA_OECONF_aarch64 += "--disable-spinlocks" |
diff --git a/meta-oe/recipes-support/postgresql/postgresql_9.2.4.bb b/meta-oe/recipes-support/postgresql/postgresql_9.2.4.bb deleted file mode 100644 index 49ca53fae3..0000000000 --- a/meta-oe/recipes-support/postgresql/postgresql_9.2.4.bb +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | require postgresql.inc | ||
| 2 | |||
| 3 | LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=ab55a5887d3f8ba77d0fd7db787e4bab" | ||
| 4 | |||
| 5 | PR = "${INC_PR}.0" | ||
| 6 | |||
| 7 | SRC_URI += "\ | ||
| 8 | file://remove.autoconf.version.check.patch \ | ||
| 9 | file://ecpg-parallel-make-fix.patch \ | ||
| 10 | " | ||
| 11 | |||
| 12 | SRC_URI[md5sum] = "6ee5bb53b97da7c6ad9cb0825d3300dd" | ||
| 13 | SRC_URI[sha256sum] = "d97dd918a88a4449225998f46aafa85216a3f89163a3411830d6890507ffae93" | ||
diff --git a/meta-oe/recipes-support/postgresql/postgresql_9.4.2.bb b/meta-oe/recipes-support/postgresql/postgresql_9.4.2.bb new file mode 100644 index 0000000000..1dee1624b5 --- /dev/null +++ b/meta-oe/recipes-support/postgresql/postgresql_9.4.2.bb | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | require postgresql.inc | ||
| 2 | |||
| 3 | LIC_FILES_CHKSUM = "file://COPYRIGHT;md5=7d847a9b446ddfe187acfac664189672" | ||
| 4 | |||
| 5 | PR = "${INC_PR}.0" | ||
| 6 | |||
| 7 | SRC_URI += "\ | ||
| 8 | file://remove.autoconf.version.check.patch \ | ||
| 9 | file://not-check-libperl.patch \ | ||
| 10 | " | ||
| 11 | |||
| 12 | SRC_URI[md5sum] = "b6369156607a4fd88f21af6fec0f30b9" | ||
| 13 | SRC_URI[sha256sum] = "81fda191c165ba1d25d75cd0166ece5abdcb4a7f5eca01b349371e279ebb4d11" | ||
