This is an automated email from the ASF dual-hosted git repository.

MuhammadTahaNaveed pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/age.git


The following commit(s) were added to refs/heads/master by this push:
     new 4d9b2506 Fix backend crash in age_create_barbell_graph with a null 
node label (#2521)
4d9b2506 is described below

commit 4d9b250663424ad9c5e2405f6e2d821182eaaef4
Author: Himmel <[email protected]>
AuthorDate: Thu Aug 20 17:26:46 2026 +0800

    Fix backend crash in age_create_barbell_graph with a null node label (#2521)
    
    node_label is declared "name = NULL" in the SQL signature, so leaving it
    out - or passing NULL explicitly - is a supported call. Both crashed the
    backend with SIGSEGV, which makes the postmaster reinitialize and drops
    every other session on the instance.
    
    There were two separate faults on that path.
    
    First, the default label was copied into a null pointer:
    
        Name node_label_name = NULL;
        ...
        if (PG_ARGISNULL(3))
            namestrcpy(node_label_name, AG_DEFAULT_LABEL_VERTEX);
    
    namestrcpy() writes through the pointer it is given and does not
    allocate, so give the default its own NameData. This also makes the
    default actually take effect, which it never did.
    
    Second, the node label was forwarded to create_complete_graph() as
    args[3].value. DirectFunctionCall4() marks every argument as not null, so
    a null node label arrived there as a non-null NULL pointer and was
    dereferenced by the vertex/edge label comparison. Forward the resolved
    label instead.
    
    create_complete_graph() already handles its own null node label correctly;
    follow the same approach here.
    
    Extend the graph_generation test with the previously untested cases. The
    existing barbell tests always passed a node label, except for the
    all-arguments-null case which errors out on the graph name before ever
    reaching this code.
---
 regress/expected/graph_generation.out | 65 +++++++++++++++++++++++++++++++++++
 regress/sql/graph_generation.sql      | 15 ++++++++
 src/backend/utils/graph_generation.c  | 23 ++++++++++---
 3 files changed, 98 insertions(+), 5 deletions(-)

diff --git a/regress/expected/graph_generation.out 
b/regress/expected/graph_generation.out
index ca511eaf..c038f9d9 100644
--- a/regress/expected/graph_generation.out
+++ b/regress/expected/graph_generation.out
@@ -203,6 +203,60 @@ ERROR:  edge label can not be NULL
 -- Should error out because same labels are used for both vertices and edges
 SELECT * FROM age_create_barbell_graph('gp6',5,10,'label',NULL,'label',NULL);
 ERROR:  vertex and edge label can not be same
+/*
+ * node_label is declared with a NULL default, so passing NULL for it - either
+ * explicitly or by leaving the trailing arguments out - has to fall back to
+ * the default vertex label rather than crash the backend.
+ */
+SELECT * FROM age_create_barbell_graph('gp7',5,0,NULL,NULL,'edges',NULL);
+NOTICE:  graph "gp7" has been created
+NOTICE:  ELabel "edges" has been created
+ age_create_barbell_graph 
+--------------------------
+ 
+(1 row)
+
+SELECT COUNT(*) FROM gp7."_ag_label_vertex";
+ count 
+-------
+    10
+(1 row)
+
+SELECT COUNT(*) FROM gp7."edges";
+ count 
+-------
+    21
+(1 row)
+
+SELECT * FROM cypher('gp7', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
+                                                             n                 
                                            
+---------------------------------------------------------------------------------------------------------------------------
+ {"id": 844424930131969, "label": "edges", "end_id": 281474976710658, 
"start_id": 281474976710657, "properties": {}}::edge
+ {"id": 844424930131970, "label": "edges", "end_id": 281474976710659, 
"start_id": 281474976710657, "properties": {}}::edge
+ {"id": 844424930131971, "label": "edges", "end_id": 281474976710660, 
"start_id": 281474976710657, "properties": {}}::edge
+ {"id": 844424930131972, "label": "edges", "end_id": 281474976710661, 
"start_id": 281474976710657, "properties": {}}::edge
+ {"id": 844424930131973, "label": "edges", "end_id": 281474976710659, 
"start_id": 281474976710658, "properties": {}}::edge
+ {"id": 844424930131974, "label": "edges", "end_id": 281474976710660, 
"start_id": 281474976710658, "properties": {}}::edge
+ {"id": 844424930131975, "label": "edges", "end_id": 281474976710661, 
"start_id": 281474976710658, "properties": {}}::edge
+ {"id": 844424930131976, "label": "edges", "end_id": 281474976710660, 
"start_id": 281474976710659, "properties": {}}::edge
+ {"id": 844424930131977, "label": "edges", "end_id": 281474976710661, 
"start_id": 281474976710659, "properties": {}}::edge
+ {"id": 844424930131978, "label": "edges", "end_id": 281474976710661, 
"start_id": 281474976710660, "properties": {}}::edge
+ {"id": 844424930131979, "label": "edges", "end_id": 281474976710663, 
"start_id": 281474976710662, "properties": {}}::edge
+ {"id": 844424930131980, "label": "edges", "end_id": 281474976710664, 
"start_id": 281474976710662, "properties": {}}::edge
+ {"id": 844424930131981, "label": "edges", "end_id": 281474976710665, 
"start_id": 281474976710662, "properties": {}}::edge
+ {"id": 844424930131982, "label": "edges", "end_id": 281474976710666, 
"start_id": 281474976710662, "properties": {}}::edge
+ {"id": 844424930131983, "label": "edges", "end_id": 281474976710664, 
"start_id": 281474976710663, "properties": {}}::edge
+ {"id": 844424930131984, "label": "edges", "end_id": 281474976710665, 
"start_id": 281474976710663, "properties": {}}::edge
+ {"id": 844424930131985, "label": "edges", "end_id": 281474976710666, 
"start_id": 281474976710663, "properties": {}}::edge
+ {"id": 844424930131986, "label": "edges", "end_id": 281474976710665, 
"start_id": 281474976710664, "properties": {}}::edge
+ {"id": 844424930131987, "label": "edges", "end_id": 281474976710666, 
"start_id": 281474976710664, "properties": {}}::edge
+ {"id": 844424930131988, "label": "edges", "end_id": 281474976710666, 
"start_id": 281474976710665, "properties": {}}::edge
+ {"id": 844424930131989, "label": "edges", "end_id": 281474976710666, 
"start_id": 281474976710657, "properties": {}}::edge
+(21 rows)
+
+-- SHOULD FAIL, but with an error rather than a crash
+SELECT * FROM age_create_barbell_graph('gp8',5,0);
+ERROR:  edge label can not be NULL
 -- DROPPING GRAPHS
 SELECT drop_graph('gp1', true);
 NOTICE:  drop cascades to 4 other objects
@@ -228,3 +282,14 @@ NOTICE:  graph "gp2" has been dropped
  
 (1 row)
 
+SELECT drop_graph('gp7', true);
+NOTICE:  drop cascades to 3 other objects
+DETAIL:  drop cascades to table gp7._ag_label_vertex
+drop cascades to table gp7._ag_label_edge
+drop cascades to table gp7.edges
+NOTICE:  graph "gp7" has been dropped
+ drop_graph 
+------------
+ 
+(1 row)
+
diff --git a/regress/sql/graph_generation.sql b/regress/sql/graph_generation.sql
index e9ee8ea8..ee359ce9 100644
--- a/regress/sql/graph_generation.sql
+++ b/regress/sql/graph_generation.sql
@@ -76,7 +76,22 @@ SELECT * FROM 
age_create_barbell_graph('gp5',5,0,'vertices',NULL,NULL,NULL);
 -- Should error out because same labels are used for both vertices and edges
 SELECT * FROM age_create_barbell_graph('gp6',5,10,'label',NULL,'label',NULL);
 
+/*
+ * node_label is declared with a NULL default, so passing NULL for it - either
+ * explicitly or by leaving the trailing arguments out - has to fall back to
+ * the default vertex label rather than crash the backend.
+ */
+SELECT * FROM age_create_barbell_graph('gp7',5,0,NULL,NULL,'edges',NULL);
+
+SELECT COUNT(*) FROM gp7."_ag_label_vertex";
+SELECT COUNT(*) FROM gp7."edges";
+SELECT * FROM cypher('gp7', $$MATCH (a)-[e]->(b) RETURN e$$) as (n agtype);
+
+-- SHOULD FAIL, but with an error rather than a crash
+SELECT * FROM age_create_barbell_graph('gp8',5,0);
+
 -- DROPPING GRAPHS
 SELECT drop_graph('gp1', true);
 SELECT drop_graph('gp2', true);
+SELECT drop_graph('gp7', true);
 
diff --git a/src/backend/utils/graph_generation.c 
b/src/backend/utils/graph_generation.c
index ea8e1bd5..4dea0426 100644
--- a/src/backend/utils/graph_generation.c
+++ b/src/backend/utils/graph_generation.c
@@ -238,6 +238,8 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
     int64 start_node_index, end_node_index, nextval;
 
     Name node_label_name = NULL;
+    NameData default_node_label;
+    Datum node_label_datum;
     int32 node_label_id;
     char* node_label_str;
 
@@ -284,16 +286,22 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
                 errmsg("Bridge size cannot be NULL or lower than 0")));
     }
 
-    /* node label: if null, gets default label, which is "_ag_label_vertex" */
+    /*
+     * node label: if null, gets default label, which is "_ag_label_vertex".
+     * The default needs storage of its own - namestrcpy() writes through the
+     * pointer it is given and does not allocate.
+     */
     if (PG_ARGISNULL(3))
     {
-        namestrcpy(node_label_name, AG_DEFAULT_LABEL_VERTEX);
+        namestrcpy(&default_node_label, AG_DEFAULT_LABEL_VERTEX);
+        node_label_name = &default_node_label;
     }
     else
     {
         node_label_name = PG_GETARG_NAME(3);
     }
     node_label_str = NameStr(*node_label_name);
+    node_label_datum = NameGetDatum(node_label_name);
 
     /* Name edge_label */
     if (PG_ARGISNULL(5))
@@ -306,15 +314,20 @@ Datum age_create_barbell_graph(PG_FUNCTION_ARGS)
     edge_label_str = NameStr(*edge_label_name);
 
 
-    /* create two separate complete graphs */
+    /*
+     * Create two separate complete graphs. node_label_datum is used rather
+     * than args[3].value because DirectFunctionCall4() marks every argument
+     * as not null, so a null node label would reach create_complete_graph()
+     * as a non-null NULL pointer.
+     */
     DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
                                                arguments->args[1].value,
                                                arguments->args[5].value,
-                                               arguments->args[3].value);
+                                               node_label_datum);
     DirectFunctionCall4(create_complete_graph, arguments->args[0].value,
                                                arguments->args[1].value,
                                                arguments->args[5].value,
-                                               arguments->args[3].value);
+                                               node_label_datum);
 
     graph_oid = get_graph_oid(graph_name_str);
     node_label_id = get_label_id(node_label_str, graph_oid);

Reply via email to