From 70f635140fbc1659e8ec49cd85e6984a8836ee13 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 29 Sep 2026 07:39:53 +0200 Subject: [PATCH] Silence -fsanitize=function where we cast function pointers on purpose In clang, -fsanitize=undefined includes -fsanitize=function, which reports every call made through a function pointer whose type does not exactly match the called function, so it fires all over the place on expression tree walkers and mutators, as well as a few other places. So -fsanitize=undefined hasn't been working cleanly under clang for a while. (Before clang 17, it only applied to C++.) This is the same issue that caused us to use -Wno-cast-function-type-strict with clang. That warning applies at the place where the mismatching function pointer is passed, so there are potentially hundreds of sites. Therefore, a global disabling is appropriate. The sanitizer, on the other hand, triggers where the function is called, which are only about two dozen places, so it seems possible to silence these checks individually and still main the check for accidental violations elsewhere. Add pg_attribute_no_sanitize_function() and place it on the functions that make such calls. --- contrib/pgcrypto/pgcrypto.c | 1 + src/backend/access/heap/heapam.c | 1 + src/backend/nodes/nodeFuncs.c | 12 ++++++++++++ src/backend/parser/analyze.c | 1 + src/backend/utils/hash/dynahash.c | 1 + src/backend/utils/mmgr/mcxt.c | 1 + src/include/c.h | 16 ++++++++++++++++ src/include/executor/execScan.h | 1 + src/include/lib/sort_template.h | 2 ++ src/pl/plpgsql/src/pl_funcs.c | 1 + 10 files changed, 37 insertions(+) diff --git a/contrib/pgcrypto/pgcrypto.c b/contrib/pgcrypto/pgcrypto.c index 24019f82893..2756900077c 100644 --- a/contrib/pgcrypto/pgcrypto.c +++ b/contrib/pgcrypto/pgcrypto.c @@ -492,6 +492,7 @@ pg_check_fipsmode(PG_FUNCTION_ARGS) PG_RETURN_BOOL(CheckFIPSMode()); } +pg_attribute_no_sanitize_function() static void * find_provider(text *name, PFN provider_lookup, diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 4207f0e0e08..5c1eaadd442 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6574,6 +6574,7 @@ heap_abort_speculative(Relation relation, const ItemPointerData *tid) * consequence that the table's next VACUUM could see the table's relfrozenxid * move forward between vacuum_get_cutoffs() and finishing. */ +pg_attribute_no_sanitize_function() bool heap_inplace_lock(Relation relation, HeapTuple oldtup_ptr, Buffer buffer, diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 9512a6a824a..678b9a046ce 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -2099,6 +2099,7 @@ check_functions_in_node(Node *node, check_function_callback checker, * uses, but may need to be revisited in future. */ +pg_attribute_no_sanitize_function() bool expression_tree_walker_impl(Node *node, tree_walker_callback walker, @@ -2704,6 +2705,7 @@ expression_tree_walker_impl(Node *node, * which is the bitwise OR of flag values to add or suppress visitation of * indicated items. (More flag bits may be added as needed.) */ +pg_attribute_no_sanitize_function() bool query_tree_walker_impl(Query *query, tree_walker_callback walker, @@ -2828,6 +2830,7 @@ range_table_walker_impl(List *rtable, /* * Some callers even want to scan the expressions in individual RTEs. */ +pg_attribute_no_sanitize_function() bool range_table_entry_walker_impl(RangeTblEntry *rte, tree_walker_callback walker, @@ -2956,6 +2959,7 @@ range_table_entry_walker_impl(RangeTblEntry *rte, * and doing the right thing. */ +pg_attribute_no_sanitize_function() Node * expression_tree_mutator_impl(Node *node, tree_mutator_callback mutator, @@ -3783,6 +3787,7 @@ expression_tree_mutator_impl(Node *node, * it to be modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. * All modified substructure is safely copied in any case. */ +pg_attribute_no_sanitize_function() Query * query_tree_mutator_impl(Query *query, tree_mutator_callback mutator, @@ -3874,6 +3879,7 @@ query_tree_mutator_impl(Query *query, * a query's rangetable. This is split out since it can be useful on * its own. */ +pg_attribute_no_sanitize_function() List * range_table_mutator_impl(List *rtable, tree_mutator_callback mutator, @@ -3952,6 +3958,7 @@ range_table_mutator_impl(List *rtable, * the recursion when the walker's normal change of state is not appropriate * for the outermost Query node. */ +pg_attribute_no_sanitize_function() bool query_or_expression_tree_walker_impl(Node *node, tree_walker_callback walker, @@ -3975,6 +3982,7 @@ query_or_expression_tree_walker_impl(Node *node, * the recursion when the mutator's normal change of state is not appropriate * for the outermost Query node. */ +pg_attribute_no_sanitize_function() Node * query_or_expression_tree_mutator_impl(Node *node, tree_mutator_callback mutator, @@ -4006,6 +4014,7 @@ query_or_expression_tree_mutator_impl(Node *node, * because this is used mainly during analysis of CTEs, and only DML * statements can appear in CTEs. */ +pg_attribute_no_sanitize_function() bool raw_expression_tree_walker_impl(Node *node, tree_walker_callback walker, @@ -4729,6 +4738,7 @@ raw_expression_tree_walker_impl(Node *node, * The walker has already visited the current node, and so we need only * recurse into any sub-nodes it has. */ +pg_attribute_no_sanitize_function() bool planstate_tree_walker_impl(PlanState *planstate, planstate_tree_walker_callback walker, @@ -4813,6 +4823,7 @@ planstate_tree_walker_impl(PlanState *planstate, /* * Walk a list of SubPlans (or initPlans, which also use SubPlan nodes). */ +pg_attribute_no_sanitize_function() static bool planstate_walk_subplans(List *plans, planstate_tree_walker_callback walker, @@ -4835,6 +4846,7 @@ planstate_walk_subplans(List *plans, * Walk the constituent plans of a ModifyTable, Append, MergeAppend, * BitmapAnd, or BitmapOr node. */ +pg_attribute_no_sanitize_function() static bool planstate_walk_members(PlanState **planstates, int nplans, planstate_tree_walker_callback walker, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 08f99dff711..2dc81a8d3b2 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -197,6 +197,7 @@ parse_analyze_varparams(RawStmt *parseTree, const char *sourceText, * This variant is used when the caller supplies their own parser callback to * resolve parameters and possibly other things. */ +pg_attribute_no_sanitize_function() Query * parse_analyze_withcb(RawStmt *parseTree, const char *sourceText, ParserSetupHook parserSetup, diff --git a/src/backend/utils/hash/dynahash.c b/src/backend/utils/hash/dynahash.c index f6fc6271a2e..3aeb5e09c7f 100644 --- a/src/backend/utils/hash/dynahash.c +++ b/src/backend/utils/hash/dynahash.c @@ -898,6 +898,7 @@ hash_search(HTAB *hashp, foundPtr); } +pg_attribute_no_sanitize_function() void * hash_search_with_hash_value(HTAB *hashp, const void *keyPtr, diff --git a/src/backend/utils/mmgr/mcxt.c b/src/backend/utils/mmgr/mcxt.c index 594c7a93bba..917cc0ac771 100644 --- a/src/backend/utils/mmgr/mcxt.c +++ b/src/backend/utils/mmgr/mcxt.c @@ -633,6 +633,7 @@ MemoryContextUnregisterResetCallback(MemoryContext context, * MemoryContextCallResetCallbacks * Internal function to call all registered callbacks for context. */ +pg_attribute_no_sanitize_function() static void MemoryContextCallResetCallbacks(MemoryContext context) { diff --git a/src/include/c.h b/src/include/c.h index 57d891e52a7..5f716251558 100644 --- a/src/include/c.h +++ b/src/include/c.h @@ -274,6 +274,22 @@ extern "C++" #define pg_attribute_no_sanitize_alignment() #endif +/* + * Place this macro before functions that intentionally call through a + * function pointer whose type does not exactly match the called function. + * The prime examples are the expression tree walkers and mutators, which are + * declared with their own concrete node and context types and cast to a + * generic signature. That is, strictly speaking, undefined behavior, but it + * is a convenient convention that works in practice. See also + * -Wno-cast-function-type-strict, which disables the corresponding + * compile-time warning. + */ +#ifdef __clang__ +#define pg_attribute_no_sanitize_function() __attribute__((no_sanitize("function"))) +#else +#define pg_attribute_no_sanitize_function() +#endif + /* * pg_attribute_nonnull means the compiler should warn if the function is * called with the listed arguments set to NULL. If no arguments are diff --git a/src/include/executor/execScan.h b/src/include/executor/execScan.h index 25efc622f88..a5902645543 100644 --- a/src/include/executor/execScan.h +++ b/src/include/executor/execScan.h @@ -29,6 +29,7 @@ * eliminated at compile time, avoiding unnecessary run-time checks and code * for cases where EPQ is not required. */ +pg_attribute_no_sanitize_function() static pg_always_inline TupleTableSlot * ExecScanFetch(ScanState *node, EPQState *epqstate, diff --git a/src/include/lib/sort_template.h b/src/include/lib/sort_template.h index 22b2092d03b..960b831d919 100644 --- a/src/include/lib/sort_template.h +++ b/src/include/lib/sort_template.h @@ -257,6 +257,7 @@ ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * first, size_t n * Refer to the comment at the top of this file for known caveats to consider * when writing inlined comparator functions. */ +pg_attribute_no_sanitize_function() static pg_noinline ST_ELEMENT_TYPE * ST_MED3(ST_ELEMENT_TYPE * a, ST_ELEMENT_TYPE * b, @@ -288,6 +289,7 @@ ST_SWAPN(ST_POINTER_TYPE * a, ST_POINTER_TYPE * b, size_t n) /* * Sort an array. */ +pg_attribute_no_sanitize_function() ST_SCOPE void ST_SORT(ST_ELEMENT_TYPE * data, size_t n ST_SORT_PROTO_ELEMENT_SIZE diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index 92cd9116c0e..e15962563f5 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -360,6 +360,7 @@ typedef void (*plpgsql_expr_walker_callback) (PLpgSQL_expr *expr, plpgsql_statement_tree_walker_impl(s, (plpgsql_stmt_walker_callback) (sw), \ (plpgsql_expr_walker_callback) (ew), c) +pg_attribute_no_sanitize_function() static void plpgsql_statement_tree_walker_impl(PLpgSQL_stmt *stmt, plpgsql_stmt_walker_callback stmt_callback, -- 2.55.0