• Tom Lane's avatar
    Fix mishandling of resjunk columns in ON CONFLICT ... UPDATE tlists. · 049e1e2e
    Tom Lane authored
    It's unusual to have any resjunk columns in an ON CONFLICT ... UPDATE
    list, but it can happen when MULTIEXPR_SUBLINK SubPlans are present.
    If it happens, the ON CONFLICT UPDATE code path would end up storing
    tuples that include the values of the extra resjunk columns.  That's
    fairly harmless in the short run, but if new columns are added to
    the table then the values would become accessible, possibly leading
    to malfunctions if they don't match the datatypes of the new columns.
    
    This had escaped notice through a confluence of missing sanity checks,
    including
    
    * There's no cross-check that a tuple presented to heap_insert or
    heap_update matches the table rowtype.  While it's difficult to
    check that fully at reasonable cost, we can easily add assertions
    that there aren't too many columns.
    
    * The output-column-assignment cases in execExprInterp.c lacked
    any sanity checks on the output column numbers, which seems like
    an oversight considering there are plenty of assertion checks on
    input column numbers.  Add assertions there too.
    
    * We failed to apply nodeModifyTable's ExecCheckPlanOutput() to
    the ON CONFLICT UPDATE tlist.  That wouldn't have caught this
    specific error, since that function is chartered to ignore resjunk
    columns; but it sure seems like a bad omission now that we've seen
    this bug.
    
    In HEAD, the right way to fix this is to make the processing of
    ON CONFLICT UPDATE tlists work the same as regular UPDATE tlists
    now do, that is don't add "SET x = x" entries, and use
    ExecBuildUpdateProjection to evaluate the tlist and combine it with
    old values of the not-set columns.  This adds a little complication
    to ExecBuildUpdateProjection, but allows removal of a comparable
    amount of now-dead code from the planner.
    
    In the back branches, the most expedient solution seems to be to
    (a) use an output slot for the ON CONFLICT UPDATE projection that
    actually matches the target table, and then (b) invent a variant of
    ExecBuildProjectionInfo that can be told to not store values resulting
    from resjunk columns, so it doesn't try to store into nonexistent
    columns of the output slot.  (We can't simply ignore the resjunk columns
    altogether; they have to be evaluated for MULTIEXPR_SUBLINK to work.)
    This works back to v10.  In 9.6, projections work much differently and
    we can't cheaply give them such an option.  The 9.6 version of this
    patch works by inserting a JunkFilter when it's necessary to get rid
    of resjunk columns.
    
    In addition, v11 and up have the reverse problem when trying to
    perform ON CONFLICT UPDATE on a partitioned table.  Through a
    further oversight, adjust_partition_tlist() discarded resjunk columns
    when re-ordering the ON CONFLICT UPDATE tlist to match a partition.
    This accidentally prevented the storing-bogus-tuples problem, but
    at the cost that MULTIEXPR_SUBLINK cases didn't work, typically
    crashing if more than one row has to be updated.  Fix by preserving
    resjunk columns in that routine.  (I failed to resist the temptation
    to add more assertions there too, and to do some minor code
    beautification.)
    
    Per report from Andres Freund.  Back-patch to all supported branches.
    
    Security: CVE-2021-32028
    049e1e2e
outfuncs.c 106 KB