Re: SIGSEGV in dynahash

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: Rahila Syed <rahilasyed90(at)gmail(dot)com>, Konstantin Knizhnik <knizhnik(at)garret(dot)ru>
Cc: PostgreSQL mailing lists <pgsql-bugs(at)lists(dot)postgresql(dot)org>
Subject: Re: SIGSEGV in dynahash
Date: 2026-08-25 11:14:45
Message-ID: e3ae5aa3-a051-4e07-9fac-c0a24c361e22@iki.fi
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

On 25/08/2026 07:30, Rahila Syed wrote:
>> On PG19, ShmemInitHash always builds a fixed-size shared hash with a bump allocator (ShmemHashAlloc) whose alloc_arg is a stack-local region used only during hash_create. After init, that pointer is dead.
>>
>> In hash_search, for every HASH_ENTER / HASH_ENTER_NULL, dynahash does this before lookup:
>>
>> dynahash.cLines 927-937
>> if (action == HASH_ENTER || action == HASH_ENTER_NULL)
>> {
>> if (hctl->freeList[0].nentries > (int64) hctl->max_bucket &&
>> !IS_PARTITIONED(hctl) && !hashp->frozen &&
>> !has_seq_scans(hashp))
>> (void) expand_table(hashp);
>> }
>>
>>
>> It may cause SIGSEGV in case of using HASH_ENTER_NULL:
>>
>>
>> hash_search(HASH_ENTER_NULL)
>> → expand_table → seg_alloc → SIGSEGV in libc (MemSet/alloc)
>
> The same thing cannot be triggered by
> hash_search_with_hash_value()->get_hash_entry()->element_alloc()
> because element_alloc()'s
> if (hctl->isfixed) return false; guard trips before ever touching the allocator.
>
> Similarly for dir_realloc() which has a check if
> (hashp->hctl->max_dsize != NO_MAX_DSIZE) return false.
> Since shared hash tables set a fixed max_dsize (not NO_MAX_DSIZE),
> dir_realloc() returns false immediately instead of growing the
> directory.
>
> This makes me think we should add a check for hctl->isfixed in
> seg_alloc() instead of before
> expand_table() like it is done in the proposed patch.

Hmm, yeah, I see what you mean, although I don't feel too bad about just
assuming that they're not called from elsewhere. I added an
Assert(!hashp->hctl->isfixed) in both seg_alloc() and dir_realloc(), to
document that you shouldn't get there for fixed-size hash tables.

Committed the fix with those extra Asserts. Thanks!

> It would also be valuable to add this SIGSEGV as a regression test. It
> can't be triggered through any of the existing shared hash tables,
> though — LOCK and PROCLOCK are the only fixed-size ones in core, and
> both are partitioned, which makes expand_table() unreachable for them
> (!IS_PARTITIONED(hctl) at dynahash.c:934/:1497). A dedicated
> non-partitioned, fixed-size test hash table would be needed.

All shmem hash tables are fixed-size. But you need one that has just the
right number of elements, is not partitioned, and you then need to fill
it up to trigger the bug.

I came up with the attached, but now that we've fixed the bug it doesn't
feel very interesting to test for exactly that, so I didn't feel it's
worth committing. Maybe as part of a larger unit test of various hash
table operations, but even then the requirement that the hash table is
just the right size to exercise this case feels fragile.

- Heikki

Attachment Content-Type Size
test-fixed-hash-expand-bug.patch text/x-patch 4.2 KB

In response to

Browse pgsql-bugs by date

  From Date Subject
Next Message Zsolt Parragi 2026-08-25 11:16:53 Re: autovacuum: automatically propagate updated parameters
Previous Message Fujii Masao 2026-08-25 05:14:25 Re: BUG #19629: pg_restore_relation_stats reports XX000 instead of proper SQLSTATE for input validation errors