array_in() and array_recv() need to be more paranoid about validating
authorTom Lane <[email protected]>
Mon, 15 Aug 2005 19:40:43 +0000 (19:40 +0000)
committerTom Lane <[email protected]>
Mon, 15 Aug 2005 19:40:43 +0000 (19:40 +0000)
their OID parameter.  It was possible to crash the backend with
select array_in('{123}',0,0); because that would bypass the needed step
of initializing the workspace.  These seem to be the only two places
with a problem, though (record_in and record_recv don't have the issue,
and the other array functions aren't depending on user-supplied input).
Back-patch as far as 7.4; 7.3 does not have the bug.

src/backend/utils/adt/arrayfuncs.c

index f22c3a46002a4e88d27f19e76976abc35727503c..aeaa4084faa336ecc4d4565a7aabed22a28c3e4a 100644 (file)
@@ -160,7 +160,7 @@ array_in(PG_FUNCTION_ARGS)
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                                                                                 sizeof(ArrayMetaState));
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
-               my_extra->element_type = InvalidOid;
+               my_extra->element_type = ~element_type;
        }
 
        if (my_extra->element_type != element_type)
@@ -1175,15 +1175,6 @@ array_recv(PG_FUNCTION_ARGS)
        }
        nitems = ArrayGetNItems(ndim, dim);
 
-       if (nitems == 0)
-       {
-               /* Return empty array */
-               retval = (ArrayType *) palloc0(sizeof(ArrayType));
-               retval->size = sizeof(ArrayType);
-               retval->elemtype = element_type;
-               PG_RETURN_ARRAYTYPE_P(retval);
-       }
-
        /*
         * We arrange to look up info about element type, including its
         * receive conversion proc, only once per series of calls, assuming
@@ -1195,7 +1186,7 @@ array_recv(PG_FUNCTION_ARGS)
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
                                                                                                 sizeof(ArrayMetaState));
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
-               my_extra->element_type = InvalidOid;
+               my_extra->element_type = ~element_type;
        }
 
        if (my_extra->element_type != element_type)
@@ -1214,6 +1205,16 @@ array_recv(PG_FUNCTION_ARGS)
                                          fcinfo->flinfo->fn_mcxt);
                my_extra->element_type = element_type;
        }
+
+       if (nitems == 0)
+       {
+               /* Return empty array ... but not till we've validated element_type */
+               retval = (ArrayType *) palloc0(sizeof(ArrayType));
+               retval->size = sizeof(ArrayType);
+               retval->elemtype = element_type;
+               PG_RETURN_ARRAYTYPE_P(retval);
+       }
+
        typlen = my_extra->typlen;
        typbyval = my_extra->typbyval;
        typalign = my_extra->typalign;
@@ -2193,13 +2194,20 @@ array_set_slice(ArrayType *array,
  *      or binary-compatible with, the first argument type of fn().
  * * retType: OID of element type of output array.     This must be the same as,
  *      or binary-compatible with, the result type of fn().
+ * * amstate: workspace for array_map.  Must be zeroed by caller before
+ *      first call, and not touched after that.
+ *
+ * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
+ * but better performance can be had if the state can be preserved across
+ * a series of calls.
  *
  * NB: caller must assure that input array is not NULL.  Currently,
  * any additional parameters passed to fn() may not be specified as NULL
  * either.
  */
 Datum
-array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
+array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
+                 ArrayMapState *amstate)
 {
        ArrayType  *v;
        ArrayType  *result;
@@ -2217,12 +2225,6 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
        bool            typbyval;
        char            typalign;
        char       *s;
-       typedef struct
-       {
-               ArrayMetaState inp_extra;
-               ArrayMetaState ret_extra;
-       } am_extra;
-       am_extra   *my_extra;
        ArrayMetaState *inp_extra;
        ArrayMetaState *ret_extra;
 
@@ -2254,22 +2256,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType)
         * only once per series of calls, assuming the element type doesn't
         * change underneath us.
         */
-       my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
-       if (my_extra == NULL)
-       {
-               fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-                                                                                                         sizeof(am_extra));
-               my_extra = (am_extra *) fcinfo->flinfo->fn_extra;
-               inp_extra = &my_extra->inp_extra;
-               inp_extra->element_type = InvalidOid;
-               ret_extra = &my_extra->ret_extra;
-               ret_extra->element_type = InvalidOid;
-       }
-       else
-       {
-               inp_extra = &my_extra->inp_extra;
-               ret_extra = &my_extra->ret_extra;
-       }
+       inp_extra = &amstate->inp_extra;
+       ret_extra = &amstate->ret_extra;
 
        if (inp_extra->element_type != inpType)
        {
@@ -3101,6 +3089,7 @@ array_type_length_coerce_internal(ArrayType *src,
                Oid                     srctype;
                Oid                     desttype;
                FmgrInfo        coerce_finfo;
+               ArrayMapState amstate;
        } atc_extra;
        atc_extra  *my_extra;
        FunctionCallInfoData locfcinfo;
@@ -3113,10 +3102,9 @@ array_type_length_coerce_internal(ArrayType *src,
        my_extra = (atc_extra *) fmgr_info->fn_extra;
        if (my_extra == NULL)
        {
-               fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
-                                                                                                sizeof(atc_extra));
+               fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
+                                                                                                        sizeof(atc_extra));
                my_extra = (atc_extra *) fmgr_info->fn_extra;
-               my_extra->srctype = InvalidOid;
        }
 
        if (my_extra->srctype != src_elem_type)
@@ -3192,7 +3180,8 @@ array_type_length_coerce_internal(ArrayType *src,
        locfcinfo.arg[1] = Int32GetDatum(desttypmod);
        locfcinfo.arg[2] = BoolGetDatum(isExplicit);
 
-       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
+       return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype,
+                                        &my_extra->amstate);
 }
 
 /*
@@ -3210,6 +3199,7 @@ array_length_coerce(PG_FUNCTION_ARGS)
        {
                Oid                     elemtype;
                FmgrInfo        coerce_finfo;
+               ArrayMapState amstate;
        } alc_extra;
        alc_extra  *my_extra;
        FunctionCallInfoData locfcinfo;
@@ -3226,10 +3216,9 @@ array_length_coerce(PG_FUNCTION_ARGS)
        my_extra = (alc_extra *) fmgr_info->fn_extra;
        if (my_extra == NULL)
        {
-               fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
-                                                                                                sizeof(alc_extra));
+               fmgr_info->fn_extra = MemoryContextAllocZero(fmgr_info->fn_mcxt,
+                                                                                                        sizeof(alc_extra));
                my_extra = (alc_extra *) fmgr_info->fn_extra;
-               my_extra->elemtype = InvalidOid;
        }
 
        if (my_extra->elemtype != ARR_ELEMTYPE(v))
@@ -3265,7 +3254,8 @@ array_length_coerce(PG_FUNCTION_ARGS)
        locfcinfo.arg[1] = Int32GetDatum(desttypmod);
        locfcinfo.arg[2] = BoolGetDatum(isExplicit);
 
-       return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v));
+       return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v),
+                                        &my_extra->amstate);
 }
 
 /*