From 8cc6745fc479748883e1d22b360e8509cf4b59a3 Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Thu, 5 Nov 2026 13:11:54 +0900 Subject: [PATCH] Fix use-after-free in COPY (ON_ERROR) When ON_ERROR handled a soft error, some of the contents set for the error context callback could remain set and pointing at areas that could be reset, leading to some use-after-frees depending on the input data provided through COPY. These contents are now reset at the beginning of NextCopyFrom(), before processing the next tuple. SET_NULL is affected down to v19, IGNORE down to v17. Author: Gowdham M Reviewed-by: Shihao Zhong Discussion: https://postgr.es/m/1a181ff6be8.6d14feb261368.4036709598649280477@zohocorp.com Backpatch-through: 17 --- src/backend/commands/copyfromparse.c | 4 ++++ src/test/regress/expected/copy2.out | 8 ++++++++ src/test/regress/sql/copy2.sql | 11 +++++++++++ 3 files changed, 23 insertions(+) diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c index 06450535779b..e6cf907ad280 100644 --- a/src/backend/commands/copyfromparse.c +++ b/src/backend/commands/copyfromparse.c @@ -927,6 +927,10 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext, MemSet(nulls, true, num_phys_attrs * sizeof(bool)); MemSet(cstate->defaults, false, num_phys_attrs * sizeof(bool)); + /* Clear the current attribute for error messages. */ + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + /* Get one row from source */ if (!cstate->routine->CopyFromOneRow(cstate, econtext, values, nulls)) return false; diff --git a/src/test/regress/expected/copy2.out b/src/test/regress/expected/copy2.out index df6fcd91799a..690568db4be8 100644 --- a/src/test/regress/expected/copy2.out +++ b/src/test/regress/expected/copy2.out @@ -830,6 +830,10 @@ CONTEXT: COPY t_on_error_null, line 1: "1,1" COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null); ERROR: extra data after last expected column CONTEXT: COPY t_on_error_null, line 1: "1,2,3,4" +-- fail, extra data after a row whose last column was set to null +COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null); +ERROR: extra data after last expected column +CONTEXT: COPY t_on_error_null, line 2: "1,2,3,4" COPY t_on_error_null FROM STDIN WITH (on_error set_null, log_verbosity verbose); -- ok NOTICE: setting to null due to data type incompatibility at line 1 for column "b": "x1" CONTEXT: COPY t_on_error_null @@ -883,6 +887,10 @@ CONTEXT: COPY hard_err, line 1, column foo: "1" COPY check_ign_err FROM STDIN WITH (on_error ignore); ERROR: missing data for column "k" CONTEXT: COPY check_ign_err, line 1: "1 {1}" +-- test extra data after a skipped row: should fail, reporting the line +COPY check_ign_err FROM STDIN WITH (on_error ignore); +ERROR: extra data after last expected column +CONTEXT: COPY check_ign_err, line 2: "2 {2} 2 extra" -- test extra data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); ERROR: extra data after last expected column diff --git a/src/test/regress/sql/copy2.sql b/src/test/regress/sql/copy2.sql index ec6f5e8cfdc1..71b7e31dac56 100644 --- a/src/test/regress/sql/copy2.sql +++ b/src/test/regress/sql/copy2.sql @@ -628,6 +628,11 @@ COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null); COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null); 1,2,3,4 \. +-- fail, extra data after a row whose last column was set to null +COPY t_on_error_null FROM STDIN WITH (delimiter ',', on_error set_null); +1,2,x +1,2,3,4 +\. COPY t_on_error_null FROM STDIN WITH (on_error set_null, log_verbosity verbose); -- ok 10 x1 yx @@ -669,6 +674,12 @@ COPY check_ign_err FROM STDIN WITH (on_error ignore); 1 {1} \. +-- test extra data after a skipped row: should fail, reporting the line +COPY check_ign_err FROM STDIN WITH (on_error ignore); +1 {1} a +2 {2} 2 extra +\. + -- test extra data: should fail COPY check_ign_err FROM STDIN WITH (on_error ignore); 1 {1} 3 abc -- 2.56.0