[LINUX] Ablauf, in dem Malloc-Parameter aus Umgebungsvariablen festgelegt werden

Einführung

malloc-bezogene Parameter können mit mallopt (3) geändert werden. Sie können beispielsweise den Schwellenwert für die Verwendung von mMap in Malloc auf 1 MB ändern, indem Sie folgende Schritte ausführen:

mallopt(M_MMAP_THRESHOLD, 1024*1024);

Einige der Parameter, die mit mallopt festgelegt werden können, können auch mit Umgebungsvariablen geändert werden. Beispielsweise kann der oben erwähnte M_MMAP_THRESHOLD auch mit der Umgebungsvariablen MALLOC_MMAP_THRESHOLD_ festgelegt werden.

$ export MALLOC_MMAP_THRESHOLD_=1048576
$ ./a.out

Umgebungsvariablen können angewendet werden, ohne das Programm zu ändern. Dies ist hilfreich, wenn Sie die Systemstandards ändern. Die Änderung von mallopt durch diese Umgebungsvariable verwendet den Mechanismus Tunables von glibc.

In diesem Artikel wird der Ablauf der Einstellung von Glibc-Tunables beschrieben Nehmen wir als Beispiel die Umgebungsvariable MALLOC_MMAP_THRESHOLD_ und belassen Sie sie als Memorandum. Wenn Sie Fehler haben, würde ich mich freuen, wenn Sie darauf hinweisen könnten.

Die Umgebung ist wie folgt.

$ 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_ Fluss, der Umgebungsvariablen widerspiegelt

1. Überprüfen Sie den Hilfsvektor mit dem Lader (ld-linux.so).

Der Hilfsvektor kann mit getauxval (3) erhalten werden. Zusatzinformationen der ausführbaren Datei, die vom ELF-Loader des Kernels übergeben wurden.

Holen Sie sich im Loader die Informationen zu AT_SECURE aus dem Hilfsvektor und Wenn "AT_SECURE" wahr ist, wird die globale Variable "__libc_enable_secure" gesetzt.

Hier ist "AT_SECURE" ein Name, der etwas irreführend ist. Dies bedeutet, dass der aktuelle Prozess sicher ausgeführt werden muss. Insbesondere wird "AT_SECURE" in einem der folgenden Fälle festgelegt.

--Programme mit Set-User-ID oder Set-Group-ID gültig

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. Aktualisieren Sie im Loader "tunable_list []" vom Wert der Umgebungsvariablen

Wenn in __tunables_init das Element in tunnelable_list [] in der Umgebungsvariablen enthalten ist, Der Wert der Umgebungsvariablen wird durch "tunable_initialize ()" wiedergegeben.

Wenn jedoch "__libc_enable_secure" gesetzt ist (= "AT_SECURE" gesetzt ist), Die Verarbeitung wird gemäß den folgenden drei Arten von "Sicherheitsstufe" durchgeführt.

--SXID_ERASE: Bei AT_SECURE löschen Sie es aus der Umgebungsvariablen, damit der untergeordnete Prozess es nicht lesen kann. --SXID_IGNORE: Ignorieren, wenn AT_SECURE (bleibt in der Umgebungsvariablen) --NONE: Immer nachdenken

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;
            }
        }
    }
}

Die Sicherheitsstufe jedes Elements in der einstellbaren Datei wird in der Datei ".list" zusammen mit dem Namen und dem Typ der Umgebungsvariablen definiert.

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 generiert automatisch dl-tunable-list.h aus dl-tunables.list Dort wird tunable_list [] definiert.

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. Reflektiert von tunable_list [] zu mp_ beim ersten Malloc

Ab hier geht es nicht mehr um Tunables, sondern um Malloc-Parameter. Außerdem ist der Ausführungszeitpunkt nicht der Loader, sondern das erste Mal, dass malloc nach Eingabe der normalen Hauptfunktion aufgerufen wird.

Wenn das Programm zum ersten Mal die Speicherzuweisungsfunktion aufruft, wird "malloc_hook_ini" aufgerufen und Rufen Sie von dort aus ptmalloc_init auf.

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);
}

Holen Sie sich in ptmalloc_init``mmap_threshold aus der in 2 festgelegten tunable_list. Stellen Sie die Struktur mp_ ein, die die Malloc-Parameter zusammenhält.

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);
}

Die eigentliche Einstellung ist die Funktion "do_set_mmap_threshold". Wenn Sie hier schauen, gibt es tatsächlich eine Obergrenze für den eingestellten Wert. Sie können sehen, dass die dynamische Schwellenwertsteuerung deaktiviert ist, wenn Sie "mmap_threshold" einstellen.

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;
}

Jetzt ist es sonnig und der Wert der Umgebungsvariablen "MALLOC_MMAP_THRESHOLD_" wird verwendet, um zu bestimmen, ob "mmap" verwendet werden soll. Ich bin glücklich.

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));
    }
}

abschließend

Wenn Sie den malloc-Parameter mit einer Umgebungsvariablen festlegen, sollten Sie überprüfen, ob die Einstellung vom Debugger ordnungsgemäß wiedergegeben wird.

Referenz

Nach dem Betrieb von malloc (Umgebungsvariablen) - Qiita mallopt(3) - Linux manual page The GNU C Library getauxval(3) - Linux manual page

Recommended Posts

Ablauf, in dem Malloc-Parameter aus Umgebungsvariablen festgelegt werden
Ruft Umgebungsvariablen in Python ab, andernfalls legen Sie Standardwerte fest
Was sind Umgebungsvariablen? (Linux)
Behandeln Sie Umgebungsvariablen in Python
HTTP-Umgebungsvariablen in Flask
Umgebungsvariablen mit Lambda-Uploader einstellen
Lesen von Umgebungsvariablen aus einer ENV-Datei mit PyCharm (für Mac)
Richten Sie Pipenv auf Pycharm in einer Windows-Umgebung ein
Verweisen auf Umgebungsvariablen in Python in Blender