[SCM] PostGIS branch stable-3.3 updated. 3.3.10-3-gf8009dd50

git at osgeo.org git at osgeo.org
Wed May 27 16:22:36 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.3 has been updated
       via  f8009dd50554c9ab6fe2767ee5f5dccae8964ec3 (commit)
       via  8e326d9d9fd7cb4175e9565a7816518a752f7cad (commit)
      from  e7e8de1c250fffd31041201fbf31f7b0c77a0f57 (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 f8009dd50554c9ab6fe2767ee5f5dccae8964ec3
Author: Paul Ramsey <pramsey at cleverelephant.ca>
Date:   Wed May 27 16:22:30 2026 -0700

    News entry for flatgeobuf schema fix

diff --git a/NEWS b/NEWS
index 2dbea618c..b3c009bdf 100644
--- a/NEWS
+++ b/NEWS
@@ -3,7 +3,7 @@ PostGIS 3.3.11
 
 * Bug Fixes and Enhancements *
 
- -
+ - Flatgeobuf schema mismatch vulnerability (NeuroWinter)
 
 
 PostGIS 3.3.10

commit 8e326d9d9fd7cb4175e9565a7816518a752f7cad
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 b4e2f3340..b4047693d 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;
 		}
@@ -468,7 +469,9 @@ void flatgeobuf_decode_row(struct flatgeobuf_decode_ctx *ctx)
 	uint32_t natts = ctx->tupdesc->natts;
 
 	Datum *values = palloc0(natts * sizeof(Datum));
-	bool *isnull = palloc0(natts * sizeof(bool));
+	bool *isnull = palloc(natts * sizeof(bool));
+	for (uint32_t j = 0; j < natts; j++) isnull[j] = true;
+	isnull[0] = false;
 
 	values[0] = Int32GetDatum(ctx->fid);
 
@@ -477,6 +480,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                           |  2 +-
 postgis/flatgeobuf.c           | 12 ++++---
 postgis/lwgeom_in_flatgeobuf.c | 78 +++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 86 insertions(+), 6 deletions(-)


hooks/post-receive
-- 
PostGIS


More information about the postgis-tickets mailing list