[LINUX] Flux dans lequel les paramètres malloc sont définis à partir de variables d'environnement

introduction

Les paramètres liés à malloc peuvent être modifiés avec mallopt (3). Par exemple, vous pouvez modifier le seuil d'utilisation de mMap dans malloc à 1 Mo en procédant comme suit:

mallopt(M_MMAP_THRESHOLD, 1024*1024);

Certains des paramètres qui peuvent être définis avec mallopt peuvent également être modifiés avec des variables d'environnement. Par exemple, le «M_MMAP_THRESHOLD» mentionné ci-dessus peut également être défini avec la variable d'environnement «MALLOC_MMAP_THRESHOLD_».

$ export MALLOC_MMAP_THRESHOLD_=1048576
$ ./a.out

Les variables d'environnement peuvent être appliquées sans changer le programme, ce qui est utile lors de la modification des valeurs par défaut du système. Le changement de mallopt par cette variable d'environnement utilise le mécanisme appelé Tunables de la glibc.

Dans cet article, le déroulement de la configuration des paramètres réglables de la glibc Prenons la variable d'environnement MALLOC_MMAP_THRESHOLD_ comme exemple et laissons-la comme mémorandum. Si vous avez des erreurs, je vous serais reconnaissant de bien vouloir les signaler.

L'environnement est le suivant.

$ arch
x86_64
$ uname -r
5.4.0-42-generic
$ lsb_release -d
Description:	Ubuntu 18.04.4 LTS
$ dpkg -l | grep libc-bin
ii  libc-bin             2.27-3ubuntu1.2 amd64           GNU C Library: Binaries

MALLOC_MMAP_THRESHOLD_ Flux qui reflète les variables d'environnement

1. Vérifiez le vecteur auxiliaire avec le chargeur (ld-linux.so)

Le vecteur auxiliaire peut être obtenu avec getauxval (3), Informations auxiliaires pour le fichier exécutable transmises depuis le chargeur ELF du noyau.

Dans le chargeur, récupérez les informations ʻAT_SECURE du vecteur auxiliaire et ʻSi AT_SECURE est vrai, la variable globale __libc_enable_secure est définie.

Ici, ʻAT_SECURE est un nom un peu trompeur, Cela signifie que le processus actuel doit s'exécuter en toute sécurité. Plus spécifiquement, ʻAT_SECURE est défini dans l'un des cas suivants.

--Programmes avec set-user-ID ou set-group-ID valide --Programmes avec capacités

glibc-2.27/elf/dl-sysdep.c


ElfW(Addr)
_dl_sysdep_start (void **start_argptr,
		  void (*dl_main) (const ElfW(Phdr) *phdr, ElfW(Word) phnum,
				   ElfW(Addr) *user_entry, ElfW(auxv_t) *auxv))
{
...

  for (av = GLRO(dl_auxv); av->a_type != AT_NULL; set_seen (av++))
    switch (av->a_type)
      {
...
      case AT_SECURE:
        __libc_enable_secure = av->a_un.a_val;
        break;
...
      }

  __tunables_init (_environ);

...
  (*dl_main) (phdr, phnum, &user_entry, GLRO(dl_auxv));
  return user_entry;
}

2. Dans le chargeur, mettez à jour tunable_list [] à partir de la valeur de la variable d'environnement

Dans __tunables_init, si l'élément danstunnelable_list [] ʻest inclus dans la variable d'environnement, La valeur de la variable d'environnement est reflétée partunable_initialize ()`.

Cependant, si __libc_enable_secure est défini (= ʻAT_SECUREest défini), Le traitement est effectué selon les trois types suivants desecurity_level`.

--SXID_ERASE: ʻAT_SECURE, supprimez-le de la variable d'environnement afin qu'il ne soit pas lu par les processus enfants. --Ignorer si SXID_IGNORE: ʻAT_SECURE (reste dans la variable d'environnement) --NONE: toujours refléter

glibc-2.27/elf/dl-tunables.c


void
__tunables_init (char **envp)
{
...
  while ((envp = get_next_env (envp, &envname, &len, &envval,
                               &prev_envp)) != NULL)
    {
...
      for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
        {
          tunable_t *cur = &tunable_list[i];
...
              if (__libc_enable_secure)
                {
                  if (cur->security_level == TUNABLE_SECLEVEL_SXID_ERASE)
                    {
                      /* Erase the environment variable.  */
                      ...
                    }

                  if (cur->security_level != TUNABLE_SECLEVEL_NONE)
                    continue;
                }

              tunable_initialize (cur, envval);
              break;
            }
        }
    }
}

Le security_level de chaque élément dans le paramétrable est défini dans le fichier .list avec le nom et le type de la variable d'environnement.

glibc-2.27/elf/dl-tunables.list


glibc {
  malloc {
    ...
    mmap_threshold {
      type: SIZE_T
      env_alias: MALLOC_MMAP_THRESHOLD_
      security_level: SXID_IGNORE
    }
    ...
  }
}

scripts / gen-tunables.awk génère automatiquement dl-tunable-list.h à partir de dl-tunables.list tunable_list [] y est défini.

dl-tunable-list.h


static tunable_t tunable_list[] attribute_relro = {
  ...
  {TUNABLE_NAME_S(glibc, malloc, mmap_threshold), {TUNABLE_TYPE_SIZE_T, 0, SIZE_MAX}, {}, NULL, TUNABLE_SECLEVEL_SXID_IGNORE, "MALLOC_MMAP_THRESHOLD_"},
  ...
};

3. Reflété de tunable_list [] à mp_ au premier malloc

À partir de maintenant, il ne s'agit pas de paramètres réglables, mais de paramètres malloc. En outre, la synchronisation d'exécution n'est pas le chargeur, mais la première fois que malloc est appelé après avoir entré la fonction principale normale.

La première fois que le programme appelle la fonction d'allocation de mémoire, malloc_hook_ini est appelé et Appelez ptmalloc_init à partir de là.

glibc-2.27/malloc/hooks.c


static void *
malloc_hook_ini (size_t sz, const void *caller)
{
  __malloc_hook = NULL;
  ptmalloc_init ();
  return __libc_malloc (sz);
}

Dans ptmalloc_init, récupérezmmap_threshold à partir de la liste tunable_list définie en 2. Réglez sur la structure mp_ qui contient les paramètres malloc ensemble.

glibc-2.27/malloc/arena.c


static void
ptmalloc_init (void)
{
...
  TUNABLE_GET (mmap_threshold, size_t, TUNABLE_CALLBACK (set_mmap_threshold));
...
}

glibc-2.27/elf/dl-tunables.c


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_SIZE_T:
	{
	  *((size_t *) valp) = (size_t) cur->val.numval;
	  break;
	}
...

  if (cur->initialized && callback != NULL)
    callback (&cur->val);
}

Le paramètre actuel est la fonction do_set_mmap_threshold. Si vous regardez ici, il y a en fait une limite supérieure pour la valeur définie, Vous pouvez voir que le contrôle de seuil dynamique est désactivé lors de la définition de mmap_threshold.

glibc-2.27/malloc/malloc.c


static inline int
__always_inline
do_set_mmap_threshold (size_t value)
{
  /* Forbid setting the threshold too high.  */
  if (value <= HEAP_MAX_SIZE / 2)
    {
      LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value, mp_.mmap_threshold,
		  mp_.no_dyn_threshold);
      mp_.mmap_threshold = value;
      mp_.no_dyn_threshold = 1;
      return 1;
    }
  return 0;
}

Maintenant, il fait beau et la valeur de la variable d'environnement MALLOC_MMAP_THRESHOLD_ sera utilisée pour déterminer si mmap Je suis heureux.

glibc-2.27/malloc/malloc.c


static void *
sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
...
  if (av == NULL
      || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)
	  && (mp_.n_mmaps < mp_.n_mmaps_max)))
    {
...
          mm = (char *) (MMAP (0, size, PROT_READ | PROT_WRITE, 0));
    }
}

en conclusion

Lors de la définition du paramètre malloc avec une variable d'environnement, il est préférable de vérifier si le paramètre est correctement reflété par le débogueur.

référence

Suite au fonctionnement de malloc (variables environnementales) --Qiita mallopt(3) - Linux manual page The GNU C Library getauxval(3) - Linux manual page

Recommended Posts

Flux dans lequel les paramètres malloc sont définis à partir de variables d'environnement
Obtenez des variables d'environnement en Python, sinon définissez les valeurs par défaut
Que sont les variables d'environnement? (Linux)
Gérer les variables d'environnement en Python
Variables d'environnement HTTP dans Flask
Définir des variables d'environnement avec lambda-uploader
Comment lire les variables d'environnement à partir d'un fichier .env avec PyCharm (pour Mac)
Configurer Pipenv sur Pycharm dans un environnement Windows
Pour référencer des variables d'environnement en Python dans Blender