[SCM] PostGIS branch stable-3.2 updated. 3.2.10-6-g69525adbe

git at osgeo.org git at osgeo.org
Mon Jun 8 16:45:45 PDT 2026


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "PostGIS".

The branch, stable-3.2 has been updated
       via  69525adbe50c6f70eb7198d8aad88e4e64c9d111 (commit)
       via  c800a006fe796d913ec8463c693a3c11655bbdab (commit)
      from  94610531f03c30c733853704569ba1bd963151d8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 69525adbe50c6f70eb7198d8aad88e4e64c9d111
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Mon Jun 8 15:55:39 2026 -0700

    NEWS item for flatgeobuf fix

diff --git a/NEWS b/NEWS
index 91cb13bd6..01e2752ca 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,7 @@ PostGIS 3.2.11
 * Bug Fixes *
 
  - #5899, pg_upgrade issue for non-standard geography SRID (Paul Ramsey)
+ - Flatgeobuf schema mismatch vulnerability (NeuroWinter)
 
 
 PostGIS 3.2.10

commit c800a006fe796d913ec8463c693a3c11655bbdab
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed May 27 21:52:25 2026 +0000

    Check that user-provided flatbuffer schema matches
    
    The flatbuffer import maps an incoming file to a provided
    table schema, and the consistency of those two schemes
    needs to be checked before data are copied across.

diff --git a/postgis/flatgeobuf.c b/postgis/flatgeobuf.c
index 545442f31..d8eea1562 100644
--- a/postgis/flatgeobuf.c
+++ b/postgis/flatgeobuf.c
@@ -288,8 +288,6 @@ static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values,
 
 	POSTGIS_DEBUGF(3, "flatgeobuf: decode_properties from byte array with length %d at offset %d", size, offset);
 
-	// TODO: init isnull
-
 	if (size > 0 && size < (sizeof(uint16_t) + sizeof(uint8_t)))
 		elog(ERROR, "flatgeobuf: decode_properties: Unexpected properties data size %d", size);
 	while (offset + 1 < size) {
@@ -390,7 +388,10 @@ static void decode_properties(struct flatgeobuf_decode_ctx *ctx, Datum *values,
 			if (offset + sizeof(float) > size)
 				elog(ERROR, "flatgeobuf: decode_properties: Invalid size for float value");
 			memcpy(&value, data + offset, sizeof(float));
-			values[ci] = Float4GetDatum(value);
+			if (getBaseType(TupleDescAttr(ctx->tupdesc, ci)->atttypid) == FLOAT8OID)
+				values[ci] = Float8GetDatum((double) value);
+			else
+				values[ci] = Float4GetDatum(value);
 			offset += sizeof(float);
 			break;
 		}
@@ -459,9 +460,10 @@ void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
 	HeapTuple heapTuple;
 	uint32_t natts = ctx->tupdesc->natts;
 
-	Datum *values = palloc0(natts * sizeof(Datum *));
-	bool *isnull = palloc0(natts * sizeof(bool *));
-
+	Datum *values = palloc0(natts * sizeof(Datum));
+	bool *isnull = palloc0(natts * sizeof(bool));
+	for (uint32_t j = 0; j < natts; j++) isnull[j] = true;
+	isnull[0] = false;
 	values[0] = Int32GetDatum(ctx->fid);
 
 	if (flatgeobuf_decode_feature(ctx->ctx))
@@ -469,6 +471,7 @@ void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
 
 	if (ctx->ctx->lwgeom != NULL) {
 		values[1] = PointerGetDatum(geometry_serialize(ctx->ctx->lwgeom));
+		isnull[1] = false;
 	} else {
 		POSTGIS_DEBUG(3, "geometry is null");
 		isnull[1] = true;
diff --git a/postgis/lwgeom_in_flatgeobuf.c b/postgis/lwgeom_in_flatgeobuf.c
index ac460ff7a..773b50f98 100644
--- a/postgis/lwgeom_in_flatgeobuf.c
+++ b/postgis/lwgeom_in_flatgeobuf.c
@@ -67,6 +67,60 @@ static char *get_pgtype(uint8_t column_type) {
 	elog(ERROR, "unknown column_type %d", column_type);
 }
 
+static const char *
+flatgeobuf_type_name(uint8_t fgb_type)
+{
+	/* Names match FlatGeobuf::EnumNamesColumnType() in header_generated.h */
+	static const char * const names[] = {
+		"Byte", "UByte", "Bool", "Short", "UShort",
+		"Int", "UInt", "Long", "ULong",
+		"Float", "Double", "String", "Json", "DateTime", "Binary"
+	};
+	if (fgb_type >= sizeof(names) / sizeof(names[0]))
+		return "unknown";
+	return names[fgb_type];
+}
+
+static bool
+flatgeobuf_type_compatible(uint8_t fgb_type, Oid pgtype)
+{
+	switch (fgb_type)
+	{
+	case flatgeobuf_column_type_bool:
+		return pgtype == BOOLOID;
+	/* small integer types: allow widening into larger signed ints */
+	case flatgeobuf_column_type_byte:
+	case flatgeobuf_column_type_ubyte:
+	case flatgeobuf_column_type_short:
+	case flatgeobuf_column_type_ushort:
+		return pgtype == INT2OID || pgtype == INT4OID || pgtype == INT8OID;
+	/* int32: allow widening to bigint */
+	case flatgeobuf_column_type_int:
+		return pgtype == INT4OID || pgtype == INT8OID;
+	/* uint32 max exceeds INT4, must land in bigint */
+	case flatgeobuf_column_type_uint:
+		return pgtype == INT8OID;
+	case flatgeobuf_column_type_long:
+	case flatgeobuf_column_type_ulong:
+		return pgtype == INT8OID;
+	/* float: allow widening to double (explicit conversion handled in decode) */
+	case flatgeobuf_column_type_float:
+		return pgtype == FLOAT4OID || pgtype == FLOAT8OID;
+	case flatgeobuf_column_type_double:
+		return pgtype == FLOAT8OID;
+	case flatgeobuf_column_type_string:
+		return pgtype == TEXTOID || pgtype == VARCHAROID;
+	case flatgeobuf_column_type_datetime:
+		return pgtype == DATEOID || pgtype == TIMEOID ||
+		       pgtype == TIMESTAMPOID || pgtype == TIMESTAMPTZOID;
+	case flatgeobuf_column_type_json:
+		return pgtype == JSONBOID;
+	case flatgeobuf_column_type_binary:
+		return pgtype == BYTEAOID;
+	}
+	return false;
+}
+
 PG_FUNCTION_INFO_V1(pgis_tablefromflatgeobuf);
 Datum pgis_tablefromflatgeobuf(PG_FUNCTION_ARGS)
 {
@@ -192,7 +246,29 @@ Datum pgis_fromflatgeobuf(PG_FUNCTION_ARGS)
 			SRF_RETURN_DONE(funcctx);
 		}
 
-		// TODO: get table and verify structure against header
+		/* Validate that the file schema matches the caller-supplied composite type.
+		 * tupdesc positions 0 (fid) and 1 (geom) are fixed; file columns start at 2. */
+		if (ctx->ctx->columns_size != (uint32_t)(tupdesc->natts - 2))
+			ereport(ERROR,
+				(errcode(ERRCODE_DATATYPE_MISMATCH),
+				 errmsg("flatgeobuf: column count mismatch: "
+				        "file has %u columns, target type has %d",
+				        ctx->ctx->columns_size, tupdesc->natts - 2)));
+
+		for (uint16_t col_i = 0; col_i < ctx->ctx->columns_size; col_i++)
+		{
+			flatgeobuf_column *col = ctx->ctx->columns[col_i];
+			Oid pgtype = getBaseType(TupleDescAttr(tupdesc, col_i + 2)->atttypid);
+			if (!flatgeobuf_type_compatible(col->type, pgtype))
+				ereport(ERROR,
+					(errcode(ERRCODE_DATATYPE_MISMATCH),
+					 errmsg("flatgeobuf: column \"%s\" type mismatch: "
+					        "file type \"%s\" is not compatible with PostgreSQL type %s",
+					        col->name,
+					        flatgeobuf_type_name(col->type),
+					        format_type_be(pgtype))));
+		}
+
 		MemoryContextSwitchTo(oldcontext);
 	}
 

-----------------------------------------------------------------------

Summary of changes:
 NEWS                           |  1 +
 postgis/flatgeobuf.c           | 15 ++++----
 postgis/lwgeom_in_flatgeobuf.c | 78 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 87 insertions(+), 7 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list