Re: BUG #19524: In `contrib/btree_gist` float4/float8 GiST index operations, handling NaN values with raw C operator

王跃林 <violin0613@tju.edu.cn>

From: 王跃林 <violin0613@tju.edu.cn>
To: Ayush Tiwari <ayushtiwari.slg01@gmail.com>
Cc: pgsql-bugs <pgsql-bugs@lists.postgresql.org>
Date: 2026-06-18T13:43:23Z
Lists: pgsql-bugs
I apologize for the overlap. The root cause is identical and my analysis additionally identifies EXCLUDE constraint bypass and RLS bypass as security impacts with the same fix.










 王跃林
3020001251@tju.edu.cn











Original:
From:Ayush Tiwari <ayushtiwari.slg01@gmail.com>Date:2026-06-18 21:36:52(中国 (GMT+08:00))To:3020001251<3020001251@tju.edu.cn> , pgsql-bugs<pgsql-bugs@lists.postgresql.org>Cc:Subject:Re: BUG #19524: In `contrib/btree_gist` float4/float8 GiST index operations, handling NaN values with raw C operatorHi,

On Thu, 18 Jun 2026 at 18:54, PG Bug reporting form <noreply@postgresql.org> wrote:

The following bug has been logged on the website:

 Bug reference:      19524
 Logged by:          Yuelin Wang
 Email address:      3020001251@tju.edu.cn
 PostgreSQL version: 19beta1
 Operating system:   Linux (Ubuntu 24.04, x86_64)
 Description:       

 **Component**: `contrib/btree_gist/btree_float4.c`, `btree_float8.c`,
 `btree_utils_num.c`

 ```sql
 CREATE EXTENSION btree_gist;

 -- Effect 1: EXCLUDE constraint bypass (float4)
 CREATE TABLE reservations (
     room   float4,
     during tsrange,
     EXCLUDE USING gist (room WITH =, during WITH &&)
 );
 INSERT INTO reservations VALUES ('NaN'::float4, '[2025-01-01,2025-01-02)');
 INSERT INTO reservations VALUES ('NaN'::float4, '[2025-01-01,2025-01-02)');
 SELECT COUNT(*) FROM reservations;

 -- Effect 2: RLS bypass (float8)
 CREATE TABLE measurements (id int, val float8);
 CREATE INDEX ON measurements USING gist (val);
 INSERT INTO measurements VALUES (1, 'NaN'), (2, 1.5);
 ALTER TABLE measurements ENABLE ROW LEVEL SECURITY;
 ALTER TABLE measurements FORCE ROW LEVEL SECURITY;
 CREATE POLICY hide_nan ON measurements FOR SELECT USING (val !=
 'NaN'::float8);
 CREATE ROLE lowpriv LOGIN;
 GRANT SELECT ON measurements TO lowpriv;
 SET ROLE lowpriv;
 SET enable_seqscan = off;
 SET enable_bitmapscan = off;
 SELECT * FROM measurements ORDER BY id;
 RESET ROLE;

 -- Effect 3: index corruption after page split (float8)
 CREATE TABLE t (val float8);
 CREATE INDEX ON t USING gist (val);
 INSERT INTO t SELECT 'NaN'::float8 FROM generate_series(1, 2000);
 SET enable_indexscan = off; SET enable_bitmapscan = off;
 SELECT COUNT(*) AS seqscan_count FROM t WHERE val = 'NaN';
 RESET ALL;
 SET enable_seqscan = off; SET enable_bitmapscan = off;
 SELECT COUNT(*) AS indexscan_count FROM t WHERE val = 'NaN';
 RESET ALL;
 ```

 Expected vs actual output:

 | Query | Expected | Actual |
 |---|---|---|
 | `SELECT COUNT(*) FROM reservations` | `1` (second insert blocked by
 EXCLUDE) | `2` |
 | `SELECT * FROM measurements ORDER BY id` (lowpriv, index scan) | `(2,
 1.5)` only | `(1, NaN)` and `(2, 1.5)` |
 | `seqscan_count` (Effect 3, seq scan forced) | `2000` | `2000` |
 | `indexscan_count` (Effect 3, index scan forced) | `2000` | `0` |


This looks the same as: https://www.postgresql.org/message-id/19501-3bff3bbc97f1e7c9%40postgresql.org
Could you please check.

Regards,
Ayush 




Commits

  1. btree_gist: fix NaN handling in float4/float8 opclasses.