Toward safer GNU C Library tunable handling
The tunables mechanism allows various aspects of the library's behavior to be adjusted at run time. Many of the provided knobs relate to memory-management algorithms hidden behind malloc(), but there are others as well. The list of available tunables on any given system can be seen by running:
/lib64/ld-linux-x86-64.so.2 --list-tunables
The library provides no public functions for setting or querying tunable values; instead, users can set them, before running a program, by setting GLIBC_TUNABLES. This variable is processed early in the startup routine, well before a program's main() function is called. So a program has little control over which tunables are applied to it; in a sense, glibc tunables allow the library's behavior to be tweaked behind a program's back.
The ability to do such tweaking raises some obvious red flags when the program involved has the setuid or setgid permissions set. Those programs constitute a security boundary within the system, so allowing users on the unprivileged side of the boundary to adjust behavior on the privileged side is going to be fraught. Glibc has long recognized that some tunables should cease to be tunable when a program is setuid or setgid; to enforce that restriction, each tunable is marked, within glibc, with a "security level". If a tunable is marked SXID_ERASE, it will be ignored in privileged programs and erased from GLIBC_TUNABLES entirely, with the result that it will not be present in any child processes created by that program. SXID_IGNORE, instead, causes the tunable to be ignored but left in place. Without one of those settings, the tunable will be honored in privileged programs.
Even with this mechanism in place, the intersection of tunables and privileged programs has proved to be a reliable source of security bugs. So the glibc developers are looking at ways of eliminating that intersection entirely. Siddhesh Poyarekar posted a patch set setting all tunables to SXID_ERASE, essentially disabling the tunables mechanism for privileged programs entirely, shortly after the latest vulnerability disclosure. Some discussion followed and, one week later, Adhemerval Zanella proposed a deeper set of changes aimed at eliminating this class of problems.
Step one in Zanella's series is to eliminate support for the special /etc/suid-debug file. If that file exists (it can be empty), then the glibc.malloc.check tunable can turn on malloc() checking, even in privileged programs. Normally this feature is disabled in such programs because it can generate output to the standard error stream, which could be abused by an attacker. This debugging can be useful, so the check for this file was added as a way of giving the system administrator a way to make it available for privileged programs. It turns out, though, that the /etc/suid-debug check hasn't worked since the 2.34 release, when memory allocation was pushed out into a separate library; rather than fix this feature, this series just removes that support.
The next step is to ignore all tunables in privileged programs, essentially setting them all to SXID_IGNORE. A later patch then adds GLIBC_TUNABLES to the list of environment variables that are not passed through to child processes, effectively changing that setting to SXID_ERASE via a different means. There is another set of environment variables that can change many of the malloc() tunables as well; for example, MALLOC_MMAP_THRESHOLD can be used to change glibc.malloc.mmap_threshold. All of those variables are added to the "insecure variables" list as well.
That eliminates the handling of tunables from privileged programs, but the patch set makes another set of changes as well. Current glibc versions are relatively forgiving about invalid GLIBC_TUNABLES settings; a number of patches in this series put an end to that, aborting tunable parsing and emitting a warning when badly formatted tunable settings are found. Finally, there is a patch that causes GLIBC_TUNABLES to be parsed in place rather than copied to a separate string. That change is intended to avoid any problems that might come about as the result of memory-allocation failures; this processing happens before glibc's memory management is initialized, so allocation is trickier at this stage.
The end result should be a distinct reduction in tunable-related bugs and
vulnerabilities in future glibc releases — if the series is accepted, of
course. This change could come at a cost for users who have a reason to
set these tunables for privileged programs, should any such users exist.
But the glibc documentation is explicit in its statement that tunables are
not considered part of the ABI and can change at any time. In this case,
most users may well conclude that these changes are overdue and the cost is
well worth paying if it results in fewer vulnerabilities in the future.
