Skip to main content
Redhat Developers  Logo
  • Products

    Featured

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat OpenShift AI
      Red Hat OpenShift AI
    • Red Hat Enterprise Linux AI
      Linux icon inside of a brain
    • Image mode for Red Hat Enterprise Linux
      RHEL image mode
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • Red Hat Developer Hub
      Developer Hub
    • View All Red Hat Products
    • Linux

      • Red Hat Enterprise Linux
      • Image mode for Red Hat Enterprise Linux
      • Red Hat Universal Base Images (UBI)
    • Java runtimes & frameworks

      • JBoss Enterprise Application Platform
      • Red Hat build of OpenJDK
    • Kubernetes

      • Red Hat OpenShift
      • Microsoft Azure Red Hat OpenShift
      • Red Hat OpenShift Virtualization
      • Red Hat OpenShift Lightspeed
    • Integration & App Connectivity

      • Red Hat Build of Apache Camel
      • Red Hat Service Interconnect
      • Red Hat Connectivity Link
    • AI/ML

    • Automation

      • Red Hat Ansible Automation Platform
      • Red Hat Ansible Lightspeed
    • Developer tools

      • Red Hat Trusted Software Supply Chain
      • Podman Desktop
      • Red Hat OpenShift Dev Spaces
    • Developer Sandbox

      Developer Sandbox
      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Secure Development & Architectures

      • Security
      • Secure coding
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
      • View All Technologies
    • Start exploring in the Developer Sandbox for free

      sandbox graphic
      Try Red Hat's products and technologies without setup or configuration.
    • Try at no cost
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • Java
      Java icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • API Catalog
    • Product Documentation
    • Legacy Documentation
    • Red Hat Learning

      Learning image
      Boost your technical skills to expert-level with the help of interactive lessons offered by various Red Hat Learning programs.
    • Explore Red Hat Learning
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

Securing malloc in glibc: Why malloc hooks had to go

August 25, 2021
Siddhesh Poyarekar
Related topics:
C, C#, C++LinuxSecuritySecure Coding
Related products:
Red Hat Enterprise Linux

Share:

Memory access is one of the most basic operations in computer programs. It is also an unending source of program errors in C programs, because memory safety was never really a programming language goal in C. Memory-related issues also comprise a significant part of the top 25 security weaknesses that result in program vulnerabilities.

Memory access also plays an important role in performance, which makes memory management a prime target for performance tuning. It is natural, then, that dynamic memory management in the C runtime should have capabilities that allow fine-grained tracking and customizable actions on allocation events. These features allow users to diagnose memory issues in their programs and if necessary, override the C runtime allocator with their own to improve performance or memory utilization.

This article describes the clash between the quest for flexibility and introspection, on the one hand, and performance and security protections on the other. You'll learn why this clash ultimately led to a major change in how memory allocation (malloc) is implemented in the GNU C Library, or glibc. We'll also discuss how to adapt applications that depended on the old way of doing things, as well as the implications for future versions of Fedora and Red Hat Enterprise Linux (RHEL).

Debugging malloc in glibc

Until recently, the GNU C Library, which is the core runtime library for RHEL, provided diagnostic functionality in the form of function pointers that users could overwrite to implement their own allocation functions. These function pointers were collectively called malloc hooks. If a hook was set to a function address, glibc allocator functions would call the function instead of the internal implementations, allowing programmers to perform arbitrary actions. A programmer could even run a custom function and then, if necessary, call the glibc memory allocator function again (by momentarily setting the hook to NULL) to get the actual block of memory.

All of the malloc debugging features in glibc (i.e., mtrace, mcheck, and the MALLOC_CHECK_ environment variable) were implemented using these hooks. These debugging features, and the hooks in general, were very useful because they provided checking on a more lightweight basis than the memory checking done by full-fledged memory debugging programs such as Valgrind and sanitizers.

Malloc hooks in multi-threaded applications

As applications became increasingly multi-threaded, it was discovered that manipulating malloc hooks in such environments was fraught with risks. All of the debugging features in glibc malloc except MALLOC_CHECK_ were, and continue to be, unsafe in multi-threaded environments. Malloc hooks, the basis of the debugging features, were not the only way to override malloc, either; glibc always supported the interposition of malloc functions by preloading a shared library with those functions. Glibc itself always calls malloc functions through its procedure linkage table (PLT) so that it can invoke the interposed functions.

To make things worse, much of the debugging infrastructure was tightly integrated into system allocator functionality. This made the task of enhancing the allocator unnecessarily complex. Furthermore, there was always the possibility of corner cases inducing unexpected behavior. Finally, implementing debugging features in the system allocator created a minor but unnecessary performance overhead.

The key misfeature of the debugging hooks, though, was their presence as unprotected function pointers that were guaranteed to be executed at specific events. This made the hooks an easy exploit primitive in practically every program that ran on Linux distributions. A trivial search for __malloc_hook "house of" turns up a long list of exploit methods that use the hooks as either an intermediate step or the final goal for the exploit.

Malloc hooks had to go.

Excising malloc hooks from the main library

The last of the malloc hook variables were deprecated in glibc 2.32 and new applications were encouraged to use malloc interposition instead. The effect of deprecation was mild: Newer applications just got a warning during the build. In the interest of maintaining backward compatibility, the memory allocator continued to look for hooks and, if available, execute them. In glibc version 2.34 (August 2021), we finally bit the bullet and took support for malloc hooks out of the mainstream library.

The upstream glibc community agreed that malloc debugging features have no place in production. So we moved all debugging features into a separate library named libc_malloc_debug.so.0 that overrides system malloc behavior to enable debugging. Most importantly, we either moved unprotected hook function pointers into libc_malloc_debug.so.0 or removed them completely. Doing this eliminated a key exploit primitive from the library.

Debugging and hardening in a post-hook world

The new glibc without the problematic hooks will be available in future versions of Fedora and RHEL. With this glibc, malloc debugging features such as MALLOC_CHECK_, mtrace(), and mcheck() will no longer work by default. Users will need to preload libc_malloc_debug.so.0 to enable these debugging features. Additionally, the __after_morecore_hook, __default_morecore_hook, and __morecore function pointers are no longer read, and the system malloc uses the brk() and mmap() system calls to request memory from the kernel.

System administrators may also remove the library from the system and effectively disable malloc debugging and malloc hooks. This is useful hardening for production systems that have strong controls on what files are available on the system.

Conclusion

Separating malloc debugging from the main library is a significant security hardening improvement in glibc. It eliminates an exploit primitive from Linux distributions and adds an opportunity for hardening in both RHEL and Fedora. Simplifying system allocator code also sets the stage for improvements to malloc that may result in better security and performance. Watch out for more interesting changes to the malloc subsystem in future releases of glibc.

Last updated: October 8, 2024

Related Posts

  • Malloc Internals and You

  • Malloc systemtap probes: an example

  • Memory error checking in C and C++: Comparing Sanitizers and Valgrind

  • Valgrind Memcheck: Different ways to lose your memory

Recent Posts

  • LLM Compressor: Optimize LLMs for low-latency deployments

  • How to set up NVIDIA NIM on Red Hat OpenShift AI

  • Leveraging Ansible Event-Driven Automation for Automatic CPU Scaling in OpenShift Virtualization

  • Python packaging for RHEL 9 & 10 using pyproject RPM macros

  • Kafka Monthly Digest: April 2025

Red Hat Developers logo LinkedIn YouTube Twitter Facebook

Products

  • Red Hat Enterprise Linux
  • Red Hat OpenShift
  • Red Hat Ansible Automation Platform

Build

  • Developer Sandbox
  • Developer Tools
  • Interactive Tutorials
  • API Catalog

Quicklinks

  • Learning Resources
  • E-books
  • Cheat Sheets
  • Blog
  • Events
  • Newsletter

Communicate

  • About us
  • Contact sales
  • Find a partner
  • Report a website issue
  • Site Status Dashboard
  • Report a security problem

RED HAT DEVELOPER

Build here. Go anywhere.

We serve the builders. The problem solvers who create careers with code.

Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

Sign me up

Red Hat legal and privacy links

  • About Red Hat
  • Jobs
  • Events
  • Locations
  • Contact Red Hat
  • Red Hat Blog
  • Inclusion at Red Hat
  • Cool Stuff Store
  • Red Hat Summit

Red Hat legal and privacy links

  • Privacy statement
  • Terms of use
  • All policies and guidelines
  • Digital accessibility

Report a website issue

OSZAR »