LCOV - code coverage report
Current view: top level - src/backend/utils/misc - stack_depth.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 31 42 73.8 %
Date: 2025-01-18 03:14:54 Functions: 6 7 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * stack_depth.c
       4             :  *    Functions for monitoring and limiting process stack depth
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/misc/stack_depth.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include <limits.h>
      19             : #include <sys/resource.h>
      20             : 
      21             : #include "miscadmin.h"
      22             : #include "utils/guc_hooks.h"
      23             : 
      24             : 
      25             : /* GUC variable for maximum stack depth (measured in kilobytes) */
      26             : int         max_stack_depth = 100;
      27             : 
      28             : /* max_stack_depth converted to bytes for speed of checking */
      29             : static long max_stack_depth_bytes = 100 * 1024L;
      30             : 
      31             : /*
      32             :  * Stack base pointer -- initialized by set_stack_base(), which
      33             :  * should be called from main().
      34             :  */
      35             : static char *stack_base_ptr = NULL;
      36             : 
      37             : 
      38             : /*
      39             :  * set_stack_base: set up reference point for stack depth checking
      40             :  *
      41             :  * Returns the old reference point, if any.
      42             :  */
      43             : pg_stack_base_t
      44        3308 : set_stack_base(void)
      45             : {
      46             : #ifndef HAVE__BUILTIN_FRAME_ADDRESS
      47             :     char        stack_base;
      48             : #endif
      49             :     pg_stack_base_t old;
      50             : 
      51        3308 :     old = stack_base_ptr;
      52             : 
      53             :     /*
      54             :      * Set up reference point for stack depth checking.  On recent gcc we use
      55             :      * __builtin_frame_address() to avoid a warning about storing a local
      56             :      * variable's address in a long-lived variable.
      57             :      */
      58             : #ifdef HAVE__BUILTIN_FRAME_ADDRESS
      59        3308 :     stack_base_ptr = __builtin_frame_address(0);
      60             : #else
      61             :     stack_base_ptr = &stack_base;
      62             : #endif
      63             : 
      64        3308 :     return old;
      65             : }
      66             : 
      67             : /*
      68             :  * restore_stack_base: restore reference point for stack depth checking
      69             :  *
      70             :  * This can be used after set_stack_base() to restore the old value. This
      71             :  * is currently only used in PL/Java. When PL/Java calls a backend function
      72             :  * from different thread, the thread's stack is at a different location than
      73             :  * the main thread's stack, so it sets the base pointer before the call, and
      74             :  * restores it afterwards.
      75             :  */
      76             : void
      77           0 : restore_stack_base(pg_stack_base_t base)
      78             : {
      79           0 :     stack_base_ptr = base;
      80           0 : }
      81             : 
      82             : 
      83             : /*
      84             :  * check_stack_depth/stack_is_too_deep: check for excessively deep recursion
      85             :  *
      86             :  * This should be called someplace in any recursive routine that might possibly
      87             :  * recurse deep enough to overflow the stack.  Most Unixen treat stack
      88             :  * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves
      89             :  * before hitting the hardware limit.
      90             :  *
      91             :  * check_stack_depth() just throws an error summarily.  stack_is_too_deep()
      92             :  * can be used by code that wants to handle the error condition itself.
      93             :  */
      94             : void
      95   564008798 : check_stack_depth(void)
      96             : {
      97   564008798 :     if (stack_is_too_deep())
      98             :     {
      99          30 :         ereport(ERROR,
     100             :                 (errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
     101             :                  errmsg("stack depth limit exceeded"),
     102             :                  errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
     103             :                          "after ensuring the platform's stack depth limit is adequate.",
     104             :                          max_stack_depth)));
     105             :     }
     106   564008768 : }
     107             : 
     108             : bool
     109   585748390 : stack_is_too_deep(void)
     110             : {
     111             :     char        stack_top_loc;
     112             :     long        stack_depth;
     113             : 
     114             :     /*
     115             :      * Compute distance from reference point to my local variables
     116             :      */
     117   585748390 :     stack_depth = (long) (stack_base_ptr - &stack_top_loc);
     118             : 
     119             :     /*
     120             :      * Take abs value, since stacks grow up on some machines, down on others
     121             :      */
     122   585748390 :     if (stack_depth < 0)
     123           0 :         stack_depth = -stack_depth;
     124             : 
     125             :     /*
     126             :      * Trouble?
     127             :      *
     128             :      * The test on stack_base_ptr prevents us from erroring out if called
     129             :      * before that's been set.  Logically it should be done first, but putting
     130             :      * it last avoids wasting cycles during normal cases.
     131             :      */
     132   585748390 :     if (stack_depth > max_stack_depth_bytes &&
     133          30 :         stack_base_ptr != NULL)
     134          30 :         return true;
     135             : 
     136   585748360 :     return false;
     137             : }
     138             : 
     139             : 
     140             : /* GUC check hook for max_stack_depth */
     141             : bool
     142       10226 : check_max_stack_depth(int *newval, void **extra, GucSource source)
     143             : {
     144       10226 :     long        newval_bytes = *newval * 1024L;
     145       10226 :     long        stack_rlimit = get_stack_depth_rlimit();
     146             : 
     147       10226 :     if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP)
     148             :     {
     149           0 :         GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.",
     150           0 :                             (stack_rlimit - STACK_DEPTH_SLOP) / 1024L);
     151           0 :         GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent.");
     152           0 :         return false;
     153             :     }
     154       10226 :     return true;
     155             : }
     156             : 
     157             : /* GUC assign hook for max_stack_depth */
     158             : void
     159       10234 : assign_max_stack_depth(int newval, void *extra)
     160             : {
     161       10234 :     long        newval_bytes = newval * 1024L;
     162             : 
     163       10234 :     max_stack_depth_bytes = newval_bytes;
     164       10234 : }
     165             : 
     166             : /*
     167             :  * Obtain platform stack depth limit (in bytes)
     168             :  *
     169             :  * Return -1 if unknown
     170             :  */
     171             : long
     172       13022 : get_stack_depth_rlimit(void)
     173             : {
     174             : #if defined(HAVE_GETRLIMIT)
     175             :     static long val = 0;
     176             : 
     177             :     /* This won't change after process launch, so check just once */
     178       13022 :     if (val == 0)
     179             :     {
     180             :         struct rlimit rlim;
     181             : 
     182        1982 :         if (getrlimit(RLIMIT_STACK, &rlim) < 0)
     183           0 :             val = -1;
     184        1982 :         else if (rlim.rlim_cur == RLIM_INFINITY)
     185           0 :             val = LONG_MAX;
     186             :         /* rlim_cur is probably of an unsigned type, so check for overflow */
     187        1982 :         else if (rlim.rlim_cur >= LONG_MAX)
     188           0 :             val = LONG_MAX;
     189             :         else
     190        1982 :             val = rlim.rlim_cur;
     191             :     }
     192       13022 :     return val;
     193             : #else
     194             :     /* On Windows we set the backend stack size in src/backend/Makefile */
     195             :     return WIN32_STACK_RLIMIT;
     196             : #endif
     197             : }

Generated by: LCOV version 1.14