I was updating my qlsc project with the updated v2.0.5 q3c when I thought I'd run Claude Code against it to look for bugs. A few simple ones turned up. The rest of the message below is authored by Claude. -- Demitri
Summary
q3c_in_poly() / q3c_poly() (via q3c_check_sphere_point_in_poly) writes past the end of two stack/struct buffers when given a polygon large enough that its projected bounding box overflows 3 or 4 sides of a cube face. This is an out-of-bounds write, reproducible under AddressSanitizer, on version 2.0.5.
Details
Two coupled buffers are undersized, both on the point-in-polygon code path:
-
points[4] is too small — q3c_poly.c:323
q3c_coord_t points[4]; /* in q3c_check_sphere_point_in_poly() */
q3c_multi_face_check() (q3cube.c:434) writes 2 coordinates per overflowing face, up to 4 faces = 8 values. The other three callers (q3c_get_nearby, q3c_radial_query, q3c_poly_query) all declare points[8]; this one uses points[4]. ASan reports the OOB write at q3cube.c:470 (points[6]/points[7]).
-
xpj/ypj/axpj/aypj are dimensioned [3][…] — q3c.c:1028
q3c_coord_t xpj[3][Q3C_MAX_N_POLY_VERTEX], ypj[3][...], axpj[3][...], aypj[3][...];
The face loop at q3c_poly.c:365 runs face_count = 0 … multi_flag and indexes xpj[face_count], but multi_flag can be 3 or 4, so it indexes row 3/4 of a 3-row array. copy_q3c_poly_info_type() (q3c.c:1055) carries the same j < 3 limit.
Root cause
The "polygon too large" guard is both too permissive and checked too late:
q3c_project_poly only sets large_flag when a vertex projects with cos(ra1) < Q3C_MINDISCR, i.e. a vertex roughly 90° from the face center — far looser than the documented ~23°-diameter limit.
- In
pgq3c_in_poly the too_large error is raised at q3c.c:1304, after the vulnerable q3c_check_sphere_point_in_poly call at q3c.c:1301 has already run and overflowed.
So a polygon that should be rejected as too large instead reaches the projection loop with multi_flag = 3 or 4 and corrupts memory before it can be rejected.
(The range-query path q3c_poly_query is not affected — it reprojects per-face into a single buffer and uses a local points[8].)
Reproduction
SQL:
SELECT q3c_in_poly(10, 10, ARRAY[80,0, 0,80, 280,0, 0,-80]::double precision[]);
The vertices are each <90° from the face center (so large_flag does not fire in time) but the polygon spans enough sky to overflow all four face sides. Built with -fsanitize=address (standalone Q3C_STANDALONE build), this trips a stack-buffer-overflow; I observed multi_flag == 3 for this input.
ASan (with points[4]):
ERROR: AddressSanitizer: stack-buffer-overflow ... WRITE of size 8
#0 q3c_multi_face_check q3cube.c:470
#1 q3c_check_sphere_point_in_poly q3c_poly.c:361
After sizing points[8], ASan then reports the second overflow in the xpj[3][…] arrays.
Suggested fix
q3c_poly.c:323: points[4] → points[8].
q3c.c:1028: dimension the four projection arrays [5][Q3C_MAX_N_POLY_VERTEX] (main face + up to 4 secondary faces), and bump the copy_q3c_poly_info_type loop at q3c.c:1055 from j < 3 to j < 5.
Both changes together make the ASan report disappear. Ideally also tighten/relocate the size guard so oversized polygons are rejected before projection.
I was updating my qlsc project with the updated v2.0.5 q3c when I thought I'd run Claude Code against it to look for bugs. A few simple ones turned up. The rest of the message below is authored by Claude. -- Demitri
Summary
q3c_in_poly()/q3c_poly()(viaq3c_check_sphere_point_in_poly) writes past the end of two stack/struct buffers when given a polygon large enough that its projected bounding box overflows 3 or 4 sides of a cube face. This is an out-of-bounds write, reproducible under AddressSanitizer, on version 2.0.5.Details
Two coupled buffers are undersized, both on the point-in-polygon code path:
points[4]is too small —q3c_poly.c:323q3c_multi_face_check()(q3cube.c:434) writes 2 coordinates per overflowing face, up to 4 faces = 8 values. The other three callers (q3c_get_nearby,q3c_radial_query,q3c_poly_query) all declarepoints[8]; this one usespoints[4]. ASan reports the OOB write atq3cube.c:470(points[6]/points[7]).xpj/ypj/axpj/aypjare dimensioned[3][…]—q3c.c:1028The face loop at
q3c_poly.c:365runsface_count = 0 … multi_flagand indexesxpj[face_count], butmulti_flagcan be 3 or 4, so it indexes row 3/4 of a 3-row array.copy_q3c_poly_info_type()(q3c.c:1055) carries the samej < 3limit.Root cause
The "polygon too large" guard is both too permissive and checked too late:
q3c_project_polyonly setslarge_flagwhen a vertex projects withcos(ra1) < Q3C_MINDISCR, i.e. a vertex roughly 90° from the face center — far looser than the documented ~23°-diameter limit.pgq3c_in_polythetoo_largeerror is raised atq3c.c:1304, after the vulnerableq3c_check_sphere_point_in_polycall atq3c.c:1301has already run and overflowed.So a polygon that should be rejected as too large instead reaches the projection loop with
multi_flag= 3 or 4 and corrupts memory before it can be rejected.(The range-query path
q3c_poly_queryis not affected — it reprojects per-face into a single buffer and uses a localpoints[8].)Reproduction
SQL:
The vertices are each <90° from the face center (so
large_flagdoes not fire in time) but the polygon spans enough sky to overflow all four face sides. Built with-fsanitize=address(standaloneQ3C_STANDALONEbuild), this trips a stack-buffer-overflow; I observedmulti_flag == 3for this input.ASan (with
points[4]):After sizing
points[8], ASan then reports the second overflow in thexpj[3][…]arrays.Suggested fix
q3c_poly.c:323:points[4]→points[8].q3c.c:1028: dimension the four projection arrays[5][Q3C_MAX_N_POLY_VERTEX](main face + up to 4 secondary faces), and bump thecopy_q3c_poly_info_typeloop atq3c.c:1055fromj < 3toj < 5.Both changes together make the ASan report disappear. Ideally also tighten/relocate the size guard so oversized polygons are rejected before projection.