| From: | Tomas Vondra <tomas(at)vondra(dot)me> |
|---|---|
| To: | 1217816127(at)qq(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org, PG Bug reporting form <noreply(at)postgresql(dot)org> |
| Subject: | Re: BUG #19763: ManualBug2 NULL pointer dereference in make_build_data via examine_expression bypass of lookup_var_a |
| Date: | 2026-10-15 18:52:42 |
| Message-ID: | d6cc3cd3-4eea-45a1-a2e4-91423a902063@vondra.me |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
On 10/15/26 17:05, PG Bug reporting form wrote:
> The following bug has been logged on the website:
>
> Bug reference: 19763
> Logged by: Yuelin Wang
> Email address: 1217816127(at)qq(dot)com
> PostgreSQL version: 19rc1
> Operating system: Linux (Ubuntu 24.04, x86_64)
> Description:
>
> ### What happens?
>
> `make_build_data()` in `src/backend/statistics/extended_stats.c` (line 2586)
> calls `examine_expression(expr, stattarget)` for each expression in a
> statistics object without checking for a NULL return value.
> The function `examine_expression()` can return NULL when a typanalyze
> callback returns false for the given `attstattarget` value.
> The invariant violated is: every element of `data->stats[]` returned by
> `make_build_data()` must be a valid non-NULL `VacAttrStats` pointer before
> any stat builder iterates over the array.
> The upstream guard in `BuildRelationExtStatistics()` calls
> `lookup_var_attr_stats()` first (line 169) and returns early when it yields
> NULL.
> However, `lookup_var_attr_stats()` calls `examine_attribute(expr)` (line
> 776) which sets `attstattarget = -1`, whereas `make_build_data()` calls
> `examine_expression(expr, stattarget)` (line 2586) which sets `attstattarget
> = stattarget` (a positive integer).
> A typanalyze callback that behaves differently depending on `attstattarget`
> can therefore pass `examine_attribute` (returning true) but fail
> `examine_expression` (returning false, causing NULL).
> The NULL then propagates through `make_build_data()` into stat builders such
> as `statext_ndistinct_build()`, `statext_mcv_build()`, and
> `statext_dependencies_build()`, all of which dereference `data->stats[i]`
> without a NULL check.
>
> The crash path confirmed by ASAN is:
>
> ```
> make_build_data extended_stats.c:2586 examine_expression returns NULL for
> stattarget-sensitive typanalyze
> data->stats[idx] = NULL
> BuildRelationExtStatistics extended_stats.c:206 calls
> statext_ndistinct_build(data)
> statext_ndistinct_build mvdistinct.c:125 iterates over data->stats[]
> ndistinct_for_combination mvdistinct.c:459 VacAttrStats *colstat =
> data->stats[combination[i]] == NULL
> typid = colstat->attrtypid <- NULL dereference (SEGFAULT)
> ```
>
> ### To Reproduce
>
> First build and install the `test_custom_types` extension with an additional
> `int_custom_typanalyze_stattarget_sensitive` function added to
> `src/test/modules/test_custom_types/test_custom_types.c`.
> The function returns true (with valid `compute_stats` and `minrows`) when
> `stats->attstattarget < 0` (the value set by `examine_attribute`), and
> returns false when `stats->attstattarget >= 0` (the value set by
> `examine_expression` with the actual statistics target).
>
> Start a fresh PostgreSQL ASAN instance and run:
>
> ```sql
> SELECT version();
> CREATE EXTENSION test_custom_types;
> CREATE TABLE t1 (id int, data int_custom);
> INSERT INTO t1 SELECT i, (i % 100)::text::int_custom FROM generate_series(1,
> 1000) i;
> CREATE OR REPLACE FUNCTION func_ic(p int_custom) RETURNS int_custom LANGUAGE
> plpgsql AS $$ BEGIN RETURN p; END; $$;
> ALTER TYPE int_custom SET (ANALYZE =
> int_custom_typanalyze_stattarget_sensitive);
> CREATE STATISTICS st1 ON func_ic(data), (id) FROM t1;
> ANALYZE t1;
> SELECT 'survived' AS result;
> ```
>
> Trigger output:
>
> ```
> version
> --------------------------------------------------------------------
> PostgreSQL 20devel on x86_64-linux, compiled by gcc-13.3.0, 64-bit
> (1 row)
>
> CREATE EXTENSION
> CREATE TABLE
> INSERT 0 1000
> CREATE FUNCTION
> ALTER TYPE
> CREATE STATISTICS
> server closed the connection unexpectedly
> This probably means the server terminated abnormally
> before or while processing the request.
> connection to server was lost
> ```
>
> pg.log:
>
> ```
> 2026-10-15 08:38:12.423 +08 [2602104] LOG: starting PostgreSQL 20devel on
> x86_64-linux, compiled by gcc-13.3.0, 64-bit
> 2026-10-15 08:38:12.423 +08 [2602104] LOG: listening on IPv4 address
> "127.0.0.1", port 14064
> 2026-10-15 08:38:12.423 +08 [2602104] LOG: listening on Unix socket
> "/tmp/.s.PGSQL.14064"
> 2026-10-15 08:38:12.432 +08 [2602109] LOG: database system was shut down at
> 2026-10-15 08:38:11 +08
> 2026-10-15 08:38:12.440 +08 [2602104] LOG: database system is ready to
> accept connections
> AddressSanitizer:DEADLYSIGNAL
> 2026-10-15 08:38:14.788 +08 [2602104] LOG: client backend (PID 2602233) was
> terminated by signal 6: Aborted
> 2026-10-15 08:38:14.788 +08 [2602104] DETAIL: Failed process was running:
> ANALYZE t1;
> 2026-10-15 08:38:14.788 +08 [2602104] LOG: terminating any other active
> server processes
> 2026-10-15 08:38:14.793 +08 [2602104] LOG: all server processes terminated;
> reinitializing
> ```
>
> ASAN report:
>
> ```
> ==2602233==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000004
> (pc 0x594bbf1a8535 bp 0x7ffcec007d70 sp 0x7ffcec007ca0 T0)
> ==2602233==The signal is caused by a READ memory access.
> ==2602233==Hint: address points to the zero page.
> #0 0x594bbf1a8535 in ndistinct_for_combination
> ../src/backend/statistics/mvdistinct.c:459
> #1 0x594bbf1a8535 in statext_ndistinct_build
> ../src/backend/statistics/mvdistinct.c:125
> #2 0x594bbf192013 in BuildRelationExtStatistics
> ../src/backend/statistics/extended_stats.c:206
> #3 0x594bbeb807f7 in do_analyze_rel
> ../src/backend/commands/analyze.c:645
> #4 0x594bbeb83d20 in analyze_rel ../src/backend/commands/analyze.c:299
> #5 0x594bbecef696 in vacuum ../src/backend/commands/vacuum.c:653
> #6 0x594bbecf0e6c in ExecVacuum ../src/backend/commands/vacuum.c:467
> #7 0x594bbf286a15 in standard_ProcessUtility
> ../src/backend/tcop/utility.c:860
> #8 0x594bbf282d46 in PortalRunUtility ../src/backend/tcop/pquery.c:1153
> #9 0x594bbf282fa4 in PortalRunMulti ../src/backend/tcop/pquery.c:1311
> #10 0x594bbf283c64 in PortalRun ../src/backend/tcop/pquery.c:788
> #11 0x594bbf27a670 in exec_simple_query
> ../src/backend/tcop/postgres.c:1297
> #12 0x594bbf27e970 in PostgresMain ../src/backend/tcop/postgres.c:4946
> #13 0x594bbf27134c in BackendMain
> ../src/backend/tcop/backend_startup.c:124
> #14 0x594bbf092b51 in postmaster_child_launch
> ../src/backend/postmaster/launch_backend.c:268
> #15 0x594bbf099c76 in BackendStartup
> ../src/backend/postmaster/postmaster.c:3650
> #16 0x594bbf099c76 in ServerLoop
> ../src/backend/postmaster/postmaster.c:1727
> #17 0x594bbf09cb29 in PostmasterMain
> ../src/backend/postmaster/postmaster.c:1414
> #18 0x594bbe88e20d in main ../src/backend/main/main.c:227
> SUMMARY: AddressSanitizer: SEGV ../src/backend/statistics/mvdistinct.c:459
> in ndistinct_for_combination
> ==2602233==ABORTING
> ```
>
I tried reproducing it using the provided information, without much
success. Attached is the test_custom_types patch with the typanalyze
function I used, but it always ends for me with
WARNING: statistics object "public.st1" could not be computed for
relation "public.t1"
Please, provide a working reproducer, or clarify the instructions
regards
--
Tomas Vondra
| Attachment | Content-Type | Size |
|---|---|---|
| analyze-repro.patch | text/x-patch | 1.6 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Tomas Vondra | 2026-10-15 19:36:24 | Re: BUG #19764: ManualBug3 Assert failure in pgstat_setup_shared_refs crashes initdb post-bootstrap subprocess |
| Previous Message | Tomas Vondra | 2026-10-15 16:34:44 | Re: BUG #19764: ManualBug3 Assert failure in pgstat_setup_shared_refs crashes initdb post-bootstrap subprocess |