From: | Andres Freund <andres(at)anarazel(dot)de> |
---|---|
To: | Kyotaro Horiguchi <horikyota(dot)ntt(at)gmail(dot)com> |
Cc: | masao(dot)fujii(at)oss(dot)nttdata(dot)com, gkokolatos(at)protonmail(dot)com, pgsql-hackers(at)lists(dot)postgresql(dot)org |
Subject: | Re: shared-memory based stats collector |
Date: | 2021-03-19 21:27:38 |
Message-ID: | 20210319212738.bxabibx6sybzadrj@alap3.anarazel.de |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
Hi,
On 2021-03-10 20:26:56 -0800, Andres Freund wrote:
> > + shhashent = dshash_find_extended(pgStatSharedHash, &key,
> > + create, nowait, create, &shfound);
> > + if (shhashent)
> > + {
> > + if (create && !shfound)
> > + {
> > + /* Create new stats entry. */
> > + dsa_pointer chunk = dsa_allocate0(area,
> > + pgstat_sharedentsize[type]);
> > +
> > + shheader = dsa_get_address(area, chunk);
> > + LWLockInitialize(&shheader->lock, LWTRANCHE_STATS);
> > + pg_atomic_init_u32(&shheader->refcount, 0);
> > +
> > + /* Link the new entry from the hash entry. */
> > + shhashent->body = chunk;
> > + }
> > + else
> > + shheader = dsa_get_address(area, shhashent->body);
> > +
> > + /*
> > + * We expose this shared entry now. You might think that the entry
> > + * can be removed by a concurrent backend, but since we are creating
> > + * an stats entry, the object actually exists and used in the upper
> > + * layer. Such an object cannot be dropped until the first vacuum
> > + * after the current transaction ends.
> > + */
> > + dshash_release_lock(pgStatSharedHash, shhashent);
>
> I don't think you can safely release the lock before you incremented the
> refcount? What if, once the lock is released, somebody looks up that
> entry, increments the refcount, and decrements it again? It'll see a
> refcount of 0 at the end and decide to free the memory. Then the code
> below will access already freed / reused memory, no?
Yep, it's not even particularly hard to hit:
S0: CREATE TABLE a_table();
S0: INSERT INTO a_table();
S0: disconnect
S1: set a breakpoint to just after the dshash_release_lock(), with an
if objid == a_table_oid
S1: SELECT pg_stat_get_live_tuples('a_table'::regclass);
(will break at above breakpoint, without having incremented the
refcount yet)
S2: DROP TABLE a_table;
S2: VACUUM pg_class;
At that point S2's call to pgstat_vacuum_stat() will find the shared
stats entry for a_table, delete the entry from the shared hash table,
see that the stats data has a zero refcount, and free it. Once S1 wakes
up it'll use already freed (and potentially since reused) memory.
Greetings,
Andres Freund
From | Date | Subject | |
---|---|---|---|
Next Message | Andres Freund | 2021-03-19 21:29:46 | Re: [HACKERS] Custom compression methods |
Previous Message | Tomas Vondra | 2021-03-19 21:19:49 | Re: [HACKERS] Custom compression methods |