mirror of
				https://sourceware.org/git/glibc.git
				synced 2025-10-30 10:45:40 +03:00 
			
		
		
		
	On Linux/i386, there are 3 ways to make a system call: 1. call *%gs:SYSINFO_OFFSET. This requires TLS initialization. 2. call *_dl_sysinfo. This requires relocation of _dl_sysinfo. 3. int $0x80. This is slower than #2 and #3, but works everywhere. When an object file is compiled with PIC, #1 is prefered since it is faster than #3 and doesn't require relocation of _dl_sysinfo. For dynamic executables, ld.so initializes TLS. However, for static executables, before TLS is initialized by __libc_setup_tls, #3 should be used for system calls. This patch adds <startup.h> which defines _startup_fatal and defaults it to __libc_fatal. It replaces __libc_fatal with _startup_fatal in static executables where it is called before __libc_setup_tls is called. This header file is included in all files containing functions which are called before __libc_setup_tls is called. On Linux/i386, when PIE is enabled by default, _startup_fatal is turned into ABORT_INSTRUCTION and I386_USE_SYSENTER is defined to 0 so that "int $0x80" is used for system calls before __libc_setup_tls is called. Tested on i686 and x86-64. Without this patch, all statically-linked tests will fail on i686 when the compiler defaults to -fPIE. [BZ #21913] * csu/libc-tls.c: Include <startup.h> first. (__libc_setup_tls): Call _startup_fatal instead of __libc_fatal. * elf/dl-tunables.c: Include <startup.h> first. * include/libc-symbols.h (BUILD_PIE_DEFAULT): New. * sysdeps/generic/startup.h: New file. * sysdeps/unix/sysv/linux/i386/startup.h: Likewise. * sysdeps/unix/sysv/linux/i386/brk.c [BUILD_PIE_DEFAULT != 0] (I386_USE_SYSENTER): New. Defined to 0.
		
			
				
	
	
		
			411 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			411 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* The tunable framework.  See the README.tunables to know how to use the
 | |
|    tunable in a glibc module.
 | |
| 
 | |
|    Copyright (C) 2016-2017 Free Software Foundation, Inc.
 | |
|    This file is part of the GNU C Library.
 | |
| 
 | |
|    The GNU C Library is free software; you can redistribute it and/or
 | |
|    modify it under the terms of the GNU Lesser General Public
 | |
|    License as published by the Free Software Foundation; either
 | |
|    version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|    The GNU C Library is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|    Lesser General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU Lesser General Public
 | |
|    License along with the GNU C Library; if not, see
 | |
|    <http://www.gnu.org/licenses/>.  */
 | |
| 
 | |
| #include <startup.h>
 | |
| #include <stdint.h>
 | |
| #include <stdbool.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include <sysdep.h>
 | |
| #include <fcntl.h>
 | |
| #include <ldsodefs.h>
 | |
| 
 | |
| #define TUNABLES_INTERNAL 1
 | |
| #include "dl-tunables.h"
 | |
| 
 | |
| #include <not-errno.h>
 | |
| 
 | |
| #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 | |
| # define GLIBC_TUNABLES "GLIBC_TUNABLES"
 | |
| #endif
 | |
| 
 | |
| #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 | |
| static char *
 | |
| tunables_strdup (const char *in)
 | |
| {
 | |
|   size_t i = 0;
 | |
| 
 | |
|   while (in[i++] != '\0');
 | |
|   char *out = __sbrk (i);
 | |
| 
 | |
|   /* FIXME: In reality if the allocation fails, __sbrk will crash attempting to
 | |
|      set the thread-local errno since the TCB has not yet been set up.  This
 | |
|      needs to be fixed with an __sbrk implementation that does not set
 | |
|      errno.  */
 | |
|   if (out == (void *)-1)
 | |
|     return NULL;
 | |
| 
 | |
|   i--;
 | |
| 
 | |
|   while (i-- > 0)
 | |
|     out[i] = in[i];
 | |
| 
 | |
|   return out;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static char **
 | |
| get_next_env (char **envp, char **name, size_t *namelen, char **val,
 | |
| 	      char ***prev_envp)
 | |
| {
 | |
|   while (envp != NULL && *envp != NULL)
 | |
|     {
 | |
|       char **prev = envp;
 | |
|       char *envline = *envp++;
 | |
|       int len = 0;
 | |
| 
 | |
|       while (envline[len] != '\0' && envline[len] != '=')
 | |
| 	len++;
 | |
| 
 | |
|       /* Just the name and no value, go to the next one.  */
 | |
|       if (envline[len] == '\0')
 | |
| 	continue;
 | |
| 
 | |
|       *name = envline;
 | |
|       *namelen = len;
 | |
|       *val = &envline[len + 1];
 | |
|       *prev_envp = prev;
 | |
| 
 | |
|       return envp;
 | |
|     }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| #define TUNABLE_SET_VAL_IF_VALID_RANGE(__cur, __val, __type, __default_min, \
 | |
| 				       __default_max)			      \
 | |
| ({									      \
 | |
|   __type min = (__cur)->type.min;					      \
 | |
|   __type max = (__cur)->type.max;					      \
 | |
| 									      \
 | |
|   if (min == max)							      \
 | |
|     {									      \
 | |
|       min = __default_min;						      \
 | |
|       max = __default_max;						      \
 | |
|     }									      \
 | |
| 									      \
 | |
|   if ((__type) (__val) >= min && (__type) (val) <= max)			      \
 | |
|     {									      \
 | |
|       (__cur)->val.numval = val;					      \
 | |
|       (__cur)->initialized = true;					      \
 | |
|     }									      \
 | |
| })
 | |
| 
 | |
| static void
 | |
| do_tunable_update_val (tunable_t *cur, const void *valp)
 | |
| {
 | |
|   uint64_t val;
 | |
| 
 | |
|   if (cur->type.type_code != TUNABLE_TYPE_STRING)
 | |
|     val = *((int64_t *) valp);
 | |
| 
 | |
|   switch (cur->type.type_code)
 | |
|     {
 | |
|     case TUNABLE_TYPE_INT_32:
 | |
| 	{
 | |
| 	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, int64_t, INT32_MIN, INT32_MAX);
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_UINT_64:
 | |
| 	{
 | |
| 	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, UINT64_MAX);
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_SIZE_T:
 | |
| 	{
 | |
| 	  TUNABLE_SET_VAL_IF_VALID_RANGE (cur, val, uint64_t, 0, SIZE_MAX);
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_STRING:
 | |
| 	{
 | |
| 	  cur->val.strval = valp;
 | |
| 	  break;
 | |
| 	}
 | |
|     default:
 | |
|       __builtin_unreachable ();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Validate range of the input value and initialize the tunable CUR if it looks
 | |
|    good.  */
 | |
| static void
 | |
| tunable_initialize (tunable_t *cur, const char *strval)
 | |
| {
 | |
|   uint64_t val;
 | |
|   const void *valp;
 | |
| 
 | |
|   if (cur->type.type_code != TUNABLE_TYPE_STRING)
 | |
|     {
 | |
|       val = _dl_strtoul (strval, NULL);
 | |
|       valp = &val;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       cur->initialized = true;
 | |
|       valp = strval;
 | |
|     }
 | |
|   do_tunable_update_val (cur, valp);
 | |
| }
 | |
| 
 | |
| void
 | |
| __tunable_set_val (tunable_id_t id, void *valp)
 | |
| {
 | |
|   tunable_t *cur = &tunable_list[id];
 | |
| 
 | |
|   do_tunable_update_val (cur, valp);
 | |
| }
 | |
| 
 | |
| #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 | |
| /* Parse the tunable string TUNESTR and adjust it to drop any tunables that may
 | |
|    be unsafe for AT_SECURE processes so that it can be used as the new
 | |
|    environment variable value for GLIBC_TUNABLES.  VALSTRING is the original
 | |
|    environment variable string which we use to make NULL terminated values so
 | |
|    that we don't have to allocate memory again for it.  */
 | |
| static void
 | |
| parse_tunables (char *tunestr, char *valstring)
 | |
| {
 | |
|   if (tunestr == NULL || *tunestr == '\0')
 | |
|     return;
 | |
| 
 | |
|   char *p = tunestr;
 | |
| 
 | |
|   while (true)
 | |
|     {
 | |
|       char *name = p;
 | |
|       size_t len = 0;
 | |
| 
 | |
|       /* First, find where the name ends.  */
 | |
|       while (p[len] != '=' && p[len] != ':' && p[len] != '\0')
 | |
| 	len++;
 | |
| 
 | |
|       /* If we reach the end of the string before getting a valid name-value
 | |
| 	 pair, bail out.  */
 | |
|       if (p[len] == '\0')
 | |
| 	return;
 | |
| 
 | |
|       /* We did not find a valid name-value pair before encountering the
 | |
| 	 colon.  */
 | |
|       if (p[len]== ':')
 | |
| 	{
 | |
| 	  p += len + 1;
 | |
| 	  continue;
 | |
| 	}
 | |
| 
 | |
|       p += len + 1;
 | |
| 
 | |
|       /* Take the value from the valstring since we need to NULL terminate it.  */
 | |
|       char *value = &valstring[p - tunestr];
 | |
|       len = 0;
 | |
| 
 | |
|       while (p[len] != ':' && p[len] != '\0')
 | |
| 	len++;
 | |
| 
 | |
|       /* Add the tunable if it exists.  */
 | |
|       for (size_t i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
 | |
| 	{
 | |
| 	  tunable_t *cur = &tunable_list[i];
 | |
| 
 | |
| 	  if (tunable_is_name (cur->name, name))
 | |
| 	    {
 | |
| 	      /* If we are in a secure context (AT_SECURE) then ignore the tunable
 | |
| 		 unless it is explicitly marked as secure.  Tunable values take
 | |
| 		 precendence over their envvar aliases.  */
 | |
| 	      if (__libc_enable_secure)
 | |
| 		{
 | |
| 		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
 | |
| 		    {
 | |
| 		      if (p[len] == '\0')
 | |
| 			{
 | |
| 			  /* Last tunable in the valstring.  Null-terminate and
 | |
| 			     return.  */
 | |
| 			  *name = '\0';
 | |
| 			  return;
 | |
| 			}
 | |
| 		      else
 | |
| 			{
 | |
| 			  /* Remove the current tunable from the string.  We do
 | |
| 			     this by overwriting the string starting from NAME
 | |
| 			     (which is where the current tunable begins) with
 | |
| 			     the remainder of the string.  We then have P point
 | |
| 			     to NAME so that we continue in the correct
 | |
| 			     position in the valstring.  */
 | |
| 			  char *q = &p[len + 1];
 | |
| 			  p = name;
 | |
| 			  while (*q != '\0')
 | |
| 			    *name++ = *q++;
 | |
| 			  name[0] = '\0';
 | |
| 			  len = 0;
 | |
| 			}
 | |
| 		    }
 | |
| 
 | |
| 		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
 | |
| 		    break;
 | |
| 		}
 | |
| 
 | |
| 	      value[len] = '\0';
 | |
| 	      tunable_initialize (cur, value);
 | |
| 	      break;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       if (p[len] == '\0')
 | |
| 	return;
 | |
|       else
 | |
| 	p += len + 1;
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* Enable the glibc.malloc.check tunable in SETUID/SETGID programs only when
 | |
|    the system administrator has created the /etc/suid-debug file.  This is a
 | |
|    special case where we want to conditionally enable/disable a tunable even
 | |
|    for setuid binaries.  We use the special version of access() to avoid
 | |
|    setting ERRNO, which is a TLS variable since TLS has not yet been set
 | |
|    up.  */
 | |
| static inline void
 | |
| __always_inline
 | |
| maybe_enable_malloc_check (void)
 | |
| {
 | |
|   tunable_id_t id = TUNABLE_ENUM_NAME (glibc, malloc, check);
 | |
|   if (__libc_enable_secure && __access_noerrno ("/etc/suid-debug", F_OK) == 0)
 | |
|     tunable_list[id].security_level = TUNABLE_SECLEVEL_NONE;
 | |
| }
 | |
| 
 | |
| /* Initialize the tunables list from the environment.  For now we only use the
 | |
|    ENV_ALIAS to find values.  Later we will also use the tunable names to find
 | |
|    values.  */
 | |
| void
 | |
| __tunables_init (char **envp)
 | |
| {
 | |
|   char *envname = NULL;
 | |
|   char *envval = NULL;
 | |
|   size_t len = 0;
 | |
|   char **prev_envp = envp;
 | |
| 
 | |
|   maybe_enable_malloc_check ();
 | |
| 
 | |
|   while ((envp = get_next_env (envp, &envname, &len, &envval,
 | |
| 			       &prev_envp)) != NULL)
 | |
|     {
 | |
| #if TUNABLES_FRONTEND == TUNABLES_FRONTEND_valstring
 | |
|       if (tunable_is_name (GLIBC_TUNABLES, envname))
 | |
| 	{
 | |
| 	  char *new_env = tunables_strdup (envname);
 | |
| 	  if (new_env != NULL)
 | |
| 	    parse_tunables (new_env + len + 1, envval);
 | |
| 	  /* Put in the updated envval.  */
 | |
| 	  *prev_envp = new_env;
 | |
| 	  continue;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
|       for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
 | |
| 	{
 | |
| 	  tunable_t *cur = &tunable_list[i];
 | |
| 
 | |
| 	  /* Skip over tunables that have either been set already or should be
 | |
| 	     skipped.  */
 | |
| 	  if (cur->initialized || cur->env_alias == NULL)
 | |
| 	    continue;
 | |
| 
 | |
| 	  const char *name = cur->env_alias;
 | |
| 
 | |
| 	  /* We have a match.  Initialize and move on to the next line.  */
 | |
| 	  if (tunable_is_name (name, envname))
 | |
| 	    {
 | |
| 	      /* For AT_SECURE binaries, we need to check the security settings of
 | |
| 		 the tunable and decide whether we read the value and also whether
 | |
| 		 we erase the value so that child processes don't inherit them in
 | |
| 		 the environment.  */
 | |
| 	      if (__libc_enable_secure)
 | |
| 		{
 | |
| 		  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
 | |
| 		    {
 | |
| 		      /* Erase the environment variable.  */
 | |
| 		      char **ep = prev_envp;
 | |
| 
 | |
| 		      while (*ep != NULL)
 | |
| 			{
 | |
| 			  if (tunable_is_name (name, *ep))
 | |
| 			    {
 | |
| 			      char **dp = ep;
 | |
| 
 | |
| 			      do
 | |
| 				dp[0] = dp[1];
 | |
| 			      while (*dp++);
 | |
| 			    }
 | |
| 			  else
 | |
| 			    ++ep;
 | |
| 			}
 | |
| 		      /* Reset the iterator so that we read the environment again
 | |
| 			 from the point we erased.  */
 | |
| 		      envp = prev_envp;
 | |
| 		    }
 | |
| 
 | |
| 		  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
 | |
| 		    continue;
 | |
| 		}
 | |
| 
 | |
| 	      tunable_initialize (cur, envval);
 | |
| 	      break;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Set the tunable value.  This is called by the module that the tunable exists
 | |
|    in. */
 | |
| void
 | |
| __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
 | |
| {
 | |
|   tunable_t *cur = &tunable_list[id];
 | |
| 
 | |
|   switch (cur->type.type_code)
 | |
|     {
 | |
|     case TUNABLE_TYPE_UINT_64:
 | |
| 	{
 | |
| 	  *((uint64_t *) valp) = (uint64_t) cur->val.numval;
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_INT_32:
 | |
| 	{
 | |
| 	  *((int32_t *) valp) = (int32_t) cur->val.numval;
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_SIZE_T:
 | |
| 	{
 | |
| 	  *((size_t *) valp) = (size_t) cur->val.numval;
 | |
| 	  break;
 | |
| 	}
 | |
|     case TUNABLE_TYPE_STRING:
 | |
| 	{
 | |
| 	  *((const char **)valp) = cur->val.strval;
 | |
| 	  break;
 | |
| 	}
 | |
|     default:
 | |
|       __builtin_unreachable ();
 | |
|     }
 | |
| 
 | |
|   if (cur->initialized && callback != NULL)
 | |
|     callback (&cur->val);
 | |
| }
 | |
| 
 | |
| rtld_hidden_def (__tunable_get_val)
 |