Check stack depth in new recursive functions
authorAlvaro Herrera <[email protected]>
Wed, 8 Nov 2023 17:44:54 +0000 (18:44 +0100)
committerAlvaro Herrera <[email protected]>
Wed, 8 Nov 2023 17:44:54 +0000 (18:44 +0100)
Commit b0e96f311985 introduced a bunch of recursive functions, but
failed to make them check for stack depth.  This can cause the backend
to crash when operating on inheritance hierarchies several thousands
deep.  Protect the code by adding the missing stack depth checks.

Reported-by: Alexander Lakhin <[email protected]>
Discussion: https://postgr.es/m/b2ac2392-9727-5f76-e890-721ac80c1615@gmail.com

src/backend/commands/tablecmds.c

index 416a98e7cef017156fbcfb07701b0d94b636ed4f..e456ccd767e1554c52f6161d8c7c597f7124c085 100644 (file)
@@ -7630,6 +7630,9 @@ set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
        Form_pg_attribute attForm;
        bool            retval = false;
 
+       /* Guard against stack overflow due to overly deep inheritance tree. */
+       check_stack_depth();
+
        tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for attribute %d of relation %u",
@@ -7716,6 +7719,9 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
        bool            is_no_inherit = false;
        List       *ready = NIL;
 
+       /* Guard against stack overflow due to overly deep inheritance tree. */
+       check_stack_depth();
+
        /*
         * In cases of multiple inheritance, we might visit the same child more
         * than once.  In the topmost call, set up a list that we fill with all
@@ -9359,6 +9365,9 @@ ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
        ListCell   *child;
        ObjectAddress address = InvalidObjectAddress;
 
+       /* Guard against stack overflow due to overly deep inheritance tree. */
+       check_stack_depth();
+
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
                ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
@@ -12428,6 +12437,9 @@ dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior beha
                return InvalidObjectAddress;
        *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
 
+       /* Guard against stack overflow due to overly deep inheritance tree. */
+       check_stack_depth();
+
        /* At top level, permission check was done in ATPrepCmd, else do it */
        if (recursing)
                ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);