5 min read

How the hardened_malloc library protects processes from security exploitation on Rocky Linux from CIQ - Hardened

August 5, 2025
How the hardened_malloc library protects processes from security exploitation on Rocky Linux from CIQ - Hardened

Table of contents

IntroductionBackground on process memory allocation.What is the danger?How does hardened_malloc help?What is the cost of this protection?Where can I find out more about libhardened_malloc?

Contributors

Jeremy Allison

Subscribe to our newsletter

Subscribe

Introduction

Rocky Linux from CIQ - Hardened (RLC-H) is a hardened version of Rocky Linux that includes a security-focused general purpose memory allocator which implements secure heap allocation strategies and strengthens resistance against heap exploitation techniques. This white paper covers the technical details on how this is implemented. CIQ would like to thank GrapheneOS for creating and the Rocky Special Interest Group - Security community for introducing this technology to Rocky Linux.

Background on process memory allocation.

Random Access Memory (RAM) is the working space to store running code, variables, and the call stack for all processes running on a computer. The kernel provides memory isolation between different processes to maintain security, but managing the memory within a process is the job of the memory allocation functions, or the malloc code for short.

Malloc and associated functions live within the C library. The C library is what most processes on a Linux box use to provide an interface between userspace and the kernel via system calls. On Rocky Linux this is the GNU C library, or glibc.

When a process calls into the C library malloc() function, the C library requests chunks of memory from the kernel via system calls, brk() or mmap(). These chunks form what is called the heap, or heaps (a program can have more than one). When a process requests a specific-sized memory allocation (usually using the malloc() library function) glibc uses internal bookkeeping to split these heaps into separate memory areas that are then returned to the calling function to use. For large size requests a separate mmap() system call is made to allocate memory directly.

What is the danger?

The executable code and read-only data in a process is loaded into a read-only protected memory space. The rest of the memory space has to be set as read/write in order for any useful work to be done by the process. As by default there is no protection between allocated memory areas within a process a bug in a program that allows memory to be overwritten can potentially cause a catastrophic security failure. Depending on the credentials of the process being exploited this can compromise the entire machine. Software security flaws including ones such as these are tracked by a “Common Vulnerabilities and Exposures” (CVE) number.

How big a memory overwrite is needed to cause a security breach ? One might think that the size of a pointer (8 bytes) is needed in order to break into a program. After all, modifying an entire pointer has the potential to cause execution to be redirected elsewhere in the program, and most running processes already contain the necessary code to cause a security compromise, possibly even a remote code execution compromise - where a network attacker can gain complete control over a machine.

So it may shock you to learn that even a ONE byte memory overwrite is enough to cause a security compromise. Here is an extremely well-written explanation (although very technical) of how being able to write one byte out of bounds is enough to break the security of a system:

CVE-2020-3837: https://googleprojectzero.blogspot.com/2020/07/one-byte-to-rule-them-all.html

Although the above exploit is for the Apple iPhone iOS kernel, please don’t feel reassured.

There are plenty of exploits to go around, many of which exist in userspace code that runs on Rocky Linux. Here is one that exploits a one-byte overwrite which can potentially lead to arbitrary code execution:

CVE-2014-5119: https://googleprojectzero.blogspot.com/2014/08/the-poisoned-nul-byte-2014-edition.html

Code written in memory-safe languages such as Python, Go or Rust provides protection against memory overwriting bugs. Unfortunately, most of the programs in most Linux distributions are still written in memory unsafe languages such as C or C++, which provide ample opportunities for an attacker to find memory bugs that cause security problems. Moreover, the runtimes and system libraries in use by memory-safe language implementations are commonly written in C and may contain or expose memory corruption bugs.

How does hardened_malloc help?

libhardened_malloc is a shared library shipped on RLC-H that provides protection against many of the problems with bugs that allow memory overwriting in C and C++ programs. As it provides an application binary interface (ABI) that is compatible with the malloc interfaces in glibc, it can be transparently loaded into most applications without the need for changes in the application source code. On detection of errors in the memory allocations in a process it writes an error to STDERR_FILENO (file descriptor 2) and calls abort(), immediately terminating the process.

Libhardened_malloc has a host of mitigations for memory access errors built into the library. A (non-complete) list follows:

  • Zero out small allocations on free(), preventing leaks of sensitive data.
  • Check small allocations are zero-filled before use, this allows detection of write-after-free errors.
  • Add 8-byte “canary” values to the end of requested allocations. The canary starts with a zero byte to prevent code reading a C string running off the end of an allocation.
  • Large allocations are protected with guard pages.
  • The allocator state (book-keeping information) is stored in a dedicated memory area, separate from the allocation regions. This prevents overwrites in the allocated memory from modifying the book-keeping information.
  • The space for the allocator state is entirely reserved during initialization and never reused for anything else.

libhardened_malloc also protects allocated areas with either guard pages (for large allocations) or canary values (for smaller allocations).

Guard pages are memory pages mapped as unavailable for reading or writing that are mapped before and after the address range of the memory being returned to the application for use. That means that if a memory overwrite or underwrite (accessing memory before the returned address) occurs and access the guard page, the kernel sends a SIGSEGV signal to the process, terminating it.

image2

Canary values are random 56 bit numbers (with a leading zero byte making them 64 bits long). Every allocation generates a different random canary value. These canary values are copied to the end of any allocated memory regions in addition to the requested size. When an allocated memory region is freed the contents of the stored canary after the region is compared to the value stored in the metadata pointing to the allocated memory. If this differs, an overwrite has occurred and the process is terminated. As the first byte of the canary starts with a zero, this also protects against C code that erroneously reads beyond the end of an unterminated string.

image1

This list of features means libhardened_malloc provides a robust series of protections against the common memory errors in most C and C++ applications running on RLC-H.

What is the cost of this protection?

These extra security features do come with some costs affecting the performance of code using libhardened_malloc. It depends on the application use of malloc, but typically slowdowns in the tens of percent can be expected.

For this reason RLC-H only enables libhardened_malloc by default for processes started as root. This can be changed to enable libhardened_malloc for all processes on an RLC-H system by changing the permissions on the configuration file /etc/ld.so.preload to allow read-only access to all processes, instead of only root. For a bastion host or a machine exposed to the Internet this might be a good change to make.

Where can I find out more about libhardened_malloc?

This paper is only a brief introduction to the details of libhardened_malloc. Like most code CIQ ships, libhardened_malloc is fully open source and can be downloaded either as a source RPM or directly from the community who created and maintains it. Links to the source code and comprehensive technical details are listed below.

Rocky Special Interest Group - Security:

https://sig-security.rocky.page/packages/hardened_malloc/

Source code:

https://github.com/GrapheneOS/hardened_malloc/

Full technical description:

https://github.com/GrapheneOS/hardened_malloc/blob/main/README.md

Built for Scale. Chosen by the World’s Best.

1.4M+

Rocky Linux instances

Being used world wide

90%

Of fortune 100 companies

Use CIQ supported technologies

250k

Avg. monthly downloads

Rocky Linux

Related posts

Available Now: A Security focused Linux… and pre-configured compliance options

Available Now: A Security focused Linux… and pre-configured compliance options

How the hardened_malloc library protects processes from security exploitation on Rocky Linux from CIQ - Hardened

How the hardened_malloc library protects processes from security exploitation on Rocky Linux from CIQ - Hardened

Why Choose Rocky Linux from CIQ?

Why Choose Rocky Linux from CIQ?

Announcing Rocky Linux from CIQ - Hardened

Announcing Rocky Linux from CIQ - Hardened