(root)/
glibc-2.38/
elf/
dl-tunables.c
       1  /* The tunable framework.  See the README.tunables to know how to use the
       2     tunable in a glibc module.
       3  
       4     Copyright (C) 2016-2023 Free Software Foundation, Inc.
       5     This file is part of the GNU C Library.
       6  
       7     The GNU C Library is free software; you can redistribute it and/or
       8     modify it under the terms of the GNU Lesser General Public
       9     License as published by the Free Software Foundation; either
      10     version 2.1 of the License, or (at your option) any later version.
      11  
      12     The GNU C Library is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15     Lesser General Public License for more details.
      16  
      17     You should have received a copy of the GNU Lesser General Public
      18     License along with the GNU C Library; if not, see
      19     <https://www.gnu.org/licenses/>.  */
      20  
      21  /* Mark symbols hidden in static PIE for early self relocation to work.  */
      22  #if BUILD_PIE_DEFAULT
      23  # pragma GCC visibility push(hidden)
      24  #endif
      25  #include <startup.h>
      26  #include <stdint.h>
      27  #include <stdbool.h>
      28  #include <unistd.h>
      29  #include <stdlib.h>
      30  #include <sysdep.h>
      31  #include <fcntl.h>
      32  #include <ldsodefs.h>
      33  #include <array_length.h>
      34  #include <dl-minimal-malloc.h>
      35  
      36  #define TUNABLES_INTERNAL 1
      37  #include "dl-tunables.h"
      38  
      39  #include <not-errno.h>
      40  
      41  static char *
      42  tunables_strdup (const char *in)
      43  {
      44    size_t i = 0;
      45  
      46    while (in[i++] != '\0');
      47    char *out = __minimal_malloc (i + 1);
      48  
      49    /* For most of the tunables code, we ignore user errors.  However,
      50       this is a system error - and running out of memory at program
      51       startup should be reported, so we do.  */
      52    if (out == NULL)
      53      _dl_fatal_printf ("failed to allocate memory to process tunables\n");
      54  
      55    while (i-- > 0)
      56      out[i] = in[i];
      57  
      58    return out;
      59  }
      60  
      61  static char **
      62  get_next_env (char **envp, char **name, size_t *namelen, char **val,
      63  	      char ***prev_envp)
      64  {
      65    while (envp != NULL && *envp != NULL)
      66      {
      67        char **prev = envp;
      68        char *envline = *envp++;
      69        int len = 0;
      70  
      71        while (envline[len] != '\0' && envline[len] != '=')
      72  	len++;
      73  
      74        /* Just the name and no value, go to the next one.  */
      75        if (envline[len] == '\0')
      76  	continue;
      77  
      78        *name = envline;
      79        *namelen = len;
      80        *val = &envline[len + 1];
      81        *prev_envp = prev;
      82  
      83        return envp;
      84      }
      85  
      86    return NULL;
      87  }
      88  
      89  static void
      90  do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
      91  		       const tunable_num_t *minp,
      92  		       const tunable_num_t *maxp)
      93  {
      94    tunable_num_t val, min, max;
      95  
      96    if (cur->type.type_code == TUNABLE_TYPE_STRING)
      97      {
      98        cur->val.strval = valp->strval;
      99        cur->initialized = true;
     100        return;
     101      }
     102  
     103    bool unsigned_cmp = unsigned_tunable_type (cur->type.type_code);
     104  
     105    val = valp->numval;
     106    min = minp != NULL ? *minp : cur->type.min;
     107    max = maxp != NULL ? *maxp : cur->type.max;
     108  
     109    /* We allow only increasingly restrictive bounds.  */
     110    if (tunable_val_lt (min, cur->type.min, unsigned_cmp))
     111      min = cur->type.min;
     112  
     113    if (tunable_val_gt (max, cur->type.max, unsigned_cmp))
     114      max = cur->type.max;
     115  
     116    /* Skip both bounds if they're inconsistent.  */
     117    if (tunable_val_gt (min, max, unsigned_cmp))
     118      {
     119        min = cur->type.min;
     120        max = cur->type.max;
     121      }
     122  
     123    /* Bail out if the bounds are not valid.  */
     124    if (tunable_val_lt (val, min, unsigned_cmp)
     125        || tunable_val_lt (max, val, unsigned_cmp))
     126      return;
     127  
     128    cur->val.numval = val;
     129    cur->type.min = min;
     130    cur->type.max = max;
     131    cur->initialized = true;
     132  }
     133  
     134  /* Validate range of the input value and initialize the tunable CUR if it looks
     135     good.  */
     136  static void
     137  tunable_initialize (tunable_t *cur, const char *strval)
     138  {
     139    tunable_val_t val;
     140  
     141    if (cur->type.type_code != TUNABLE_TYPE_STRING)
     142      val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
     143    else
     144      val.strval = strval;
     145    do_tunable_update_val (cur, &val, NULL, NULL);
     146  }
     147  
     148  void
     149  __tunable_set_val (tunable_id_t id, tunable_val_t *valp, tunable_num_t *minp,
     150  		   tunable_num_t *maxp)
     151  {
     152    tunable_t *cur = &tunable_list[id];
     153  
     154    do_tunable_update_val (cur, valp, minp, maxp);
     155  }
     156  
     157  /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
     158     be unsafe for AT_SECURE processes so that it can be used as the new
     159     environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
     160     environment variable string which we use to make NULL terminated values so
     161     that we don't have to allocate memory again for it.  */
     162  static void
     163  parse_tunables (char *tunestr, char *valstring)
     164  {
     165    if (tunestr == NULL || *tunestr == '\0')
     166      return;
     167  
     168    char *p = tunestr;
     169    size_t off = 0;
     170  
     171    while (true)
     172      {
     173        char *name = p;
     174        size_t len = 0;
     175  
     176        /* First, find where the name ends.  */
     177        while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
     178  	len++;
     179  
     180        /* If we reach the end of the string before getting a valid name-value
     181  	 pair, bail out.  */
     182        if (p[len] == '\0')
     183  	{
     184  	  if (__libc_enable_secure)
     185  	    tunestr[off] = '\0';
     186  	  return;
     187  	}
     188  
     189        /* We did not find a valid name-value pair before encountering the
     190  	 colon.  */
     191        if (p[len]== ':')
     192  	{
     193  	  p += len + 1;
     194  	  continue;
     195  	}
     196  
     197        p += len + 1;
     198  
     199        /* Take the value from the valstring since we need to NULL terminate it.  */
     200        char *value = &valstring[p - tunestr];
     201        len = 0;
     202  
     203        while (p[len] != ':' && p[len] != '\0')
     204  	len++;
     205  
     206        /* Add the tunable if it exists.  */
     207        for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
     208  	{
     209  	  tunable_t *cur = &tunable_list[i];
     210  
     211  	  if (tunable_is_name (cur->name, name))
     212  	    {
     213  	      /* If we are in a secure context (AT_SECURE) then ignore the
     214  		 tunable unless it is explicitly marked as secure.  Tunable
     215  		 values take precedence over their envvar aliases.  We write
     216  		 the tunables that are not SXID_ERASE back to TUNESTR, thus
     217  		 dropping all SXID_ERASE tunables and any invalid or
     218  		 unrecognized tunables.  */
     219  	      if (__libc_enable_secure)
     220  		{
     221  		  if (cur->security_level != TUNABLE_SECLEVEL_SXID_ERASE)
     222  		    {
     223  		      if (off > 0)
     224  			tunestr[off++] = ':';
     225  
     226  		      const char *n = cur->name;
     227  
     228  		      while (*n != '\0')
     229  			tunestr[off++] = *n++;
     230  
     231  		      tunestr[off++] = '=';
     232  
     233  		      for (size_t j = 0; j < len; j++)
     234  			tunestr[off++] = value[j];
     235  		    }
     236  
     237  		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
     238  		    break;
     239  		}
     240  
     241  	      value[len] = '\0';
     242  	      tunable_initialize (cur, value);
     243  	      break;
     244  	    }
     245  	}
     246  
     247        if (p[len] != '\0')
     248  	p += len + 1;
     249      }
     250  }
     251  
     252  /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
     253     the system administrator has created the /etc/suid-debug file.  This is a
     254     special case where we want to conditionally enable/disable a tunable even
     255     for setuid binaries.  We use the special version of access() to avoid
     256     setting ERRNO, which is a TLS variable since TLS has not yet been set
     257     up.  */
     258  static __always_inline void
     259  maybe_enable_malloc_check (void)
     260  {
     261    tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
     262    if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
     263      tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
     264  }
     265  
     266  /* Initialize the tunables list from the environment.  For now we only use the
     267     ENV_ALIAS to find values.  Later we will also use the tunable names to find
     268     values.  */
     269  void
     270  __tunables_init (char **envp)
     271  {
     272    char *envname = NULL;
     273    char *envval = NULL;
     274    size_t len = 0;
     275    char **prev_envp = envp;
     276  
     277    maybe_enable_malloc_check ();
     278  
     279    while ((envp = get_next_env (envp, &envname, &len, &envval,
     280  			       &prev_envp)) != NULL)
     281      {
     282        if (tunable_is_name ("GLIBC_TUNABLES", envname))
     283  	{
     284  	  char *new_env = tunables_strdup (envname);
     285  	  if (new_env != NULL)
     286  	    parse_tunables (new_env + len + 1, envval);
     287  	  /* Put in the updated envval.  */
     288  	  *prev_envp = new_env;
     289  	  continue;
     290  	}
     291  
     292        for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
     293  	{
     294  	  tunable_t *cur = &tunable_list[i];
     295  
     296  	  /* Skip over tunables that have either been set already or should be
     297  	     skipped.  */
     298  	  if (cur->initialized || cur->env_alias[0] == '\0')
     299  	    continue;
     300  
     301  	  const char *name = cur->env_alias;
     302  
     303  	  /* We have a match.  Initialize and move on to the next line.  */
     304  	  if (tunable_is_name (name, envname))
     305  	    {
     306  	      /* For AT_SECURE binaries, we need to check the security settings of
     307  		 the tunable and decide whether we read the value and also whether
     308  		 we erase the value so that child processes don't inherit them in
     309  		 the environment.  */
     310  	      if (__libc_enable_secure)
     311  		{
     312  		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
     313  		    {
     314  		      /* Erase the environment variable.  */
     315  		      char **ep = prev_envp;
     316  
     317  		      while (*ep != NULL)
     318  			{
     319  			  if (tunable_is_name (name, *ep))
     320  			    {
     321  			      char **dp = ep;
     322  
     323  			      do
     324  				dp[0] = dp[1];
     325  			      while (*dp++);
     326  			    }
     327  			  else
     328  			    ++ep;
     329  			}
     330  		      /* Reset the iterator so that we read the environment again
     331  			 from the point we erased.  */
     332  		      envp = prev_envp;
     333  		    }
     334  
     335  		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
     336  		    continue;
     337  		}
     338  
     339  	      tunable_initialize (cur, envval);
     340  	      break;
     341  	    }
     342  	}
     343      }
     344  }
     345  
     346  void
     347  __tunables_print (void)
     348  {
     349    for (int i = 0; i < array_length (tunable_list); i++)
     350      {
     351        const tunable_t *cur = &tunable_list[i];
     352        if (cur->type.type_code == TUNABLE_TYPE_STRING
     353  	  && cur->val.strval == NULL)
     354  	_dl_printf ("%s:\n", cur->name);
     355        else
     356  	{
     357  	  _dl_printf ("%s: ", cur->name);
     358  	  switch (cur->type.type_code)
     359  	    {
     360  	    case TUNABLE_TYPE_INT_32:
     361  	      _dl_printf ("%d (min: %d, max: %d)\n",
     362  			  (int) cur->val.numval,
     363  			  (int) cur->type.min,
     364  			  (int) cur->type.max);
     365  	      break;
     366  	    case TUNABLE_TYPE_UINT_64:
     367  	      _dl_printf ("0x%lx (min: 0x%lx, max: 0x%lx)\n",
     368  			  (long int) cur->val.numval,
     369  			  (long int) cur->type.min,
     370  			  (long int) cur->type.max);
     371  	      break;
     372  	    case TUNABLE_TYPE_SIZE_T:
     373  	      _dl_printf ("0x%zx (min: 0x%zx, max: 0x%zx)\n",
     374  			  (size_t) cur->val.numval,
     375  			  (size_t) cur->type.min,
     376  			  (size_t) cur->type.max);
     377  	      break;
     378  	    case TUNABLE_TYPE_STRING:
     379  	      _dl_printf ("%s\n", cur->val.strval);
     380  	      break;
     381  	    default:
     382  	      __builtin_unreachable ();
     383  	    }
     384  	}
     385      }
     386  }
     387  
     388  /* Set the tunable value.  This is called by the module that the tunable exists
     389     in. */
     390  void
     391  __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
     392  {
     393    tunable_t *cur = &tunable_list[id];
     394  
     395    switch (cur->type.type_code)
     396      {
     397      case TUNABLE_TYPE_UINT_64:
     398  	{
     399  	  *((uint64_t *) valp) = (uint64_t) cur->val.numval;
     400  	  break;
     401  	}
     402      case TUNABLE_TYPE_INT_32:
     403  	{
     404  	  *((int32_t *) valp) = (int32_t) cur->val.numval;
     405  	  break;
     406  	}
     407      case TUNABLE_TYPE_SIZE_T:
     408  	{
     409  	  *((size_t *) valp) = (size_t) cur->val.numval;
     410  	  break;
     411  	}
     412      case TUNABLE_TYPE_STRING:
     413  	{
     414  	  *((const char **)valp) = cur->val.strval;
     415  	  break;
     416  	}
     417      default:
     418        __builtin_unreachable ();
     419      }
     420  
     421    if (cur->initialized && callback != NULL)
     422      callback (&cur->val);
     423  }
     424  
     425  rtld_hidden_def (__tunable_get_val)