$RodHat_
MOTD

CVE-2026-86805: ld.so validated one path and opened another

Published by

This article may contain Amazon affiliate links. I may earn a commission if you buy through them. Disclosure.

CVE-2026-86805: ld.so validated one path and opened another
Photo: AI-generated — no human photographer / RodHat AI Cover
On this page · 3 sections
  1. what $ORIGIN is and why it matters for AT_SECURE
  2. the gap between validate and open
  3. the part that should embarrass the glibc team more than it does

The dynamic loader is one of the most security-sensitive pieces of code on a Linux system. It runs before your program does. It runs under whatever privileges the binary carries. When a setuid binary starts, ld.so is already running with elevated privileges before a single line of your application runs.

CVE-2026-86805 is a textbook TOCTOU race in glibc’s ld.so, in the specific path that handles $ORIGIN expansion inside DT_RPATH for binaries running under AT_SECURE mode (which covers setuid and setgid). The vulnerability affects glibc 2.14 through 2.44. That range starts around 2011 and ends with the release before this month’s patch. Fourteen years.

what $ORIGIN is and why it matters for AT_SECURE

$ORIGIN in a binary’s DT_RPATH expands to the directory containing the executable. It exists so you can ship a program with a bundled library in a relative path without hardcoding absolute paths. That is useful and mostly works.

The problem is that setuid binaries cannot be allowed to load libraries from attacker-controlled directories. If an attacker can influence where ld.so looks for libraries, they can inject a malicious .so and get code execution with the privileges of the target binary.

Glibc knows this. When AT_SECURE is set, ld.so validates the expanded path against a set of trusted directories before proceeding. That validation is the part that is supposed to make $ORIGIN safe for privileged executables.

the gap between validate and open

The bug is simple once you see it. Glibc validates a lexically normalized version of the path, checks it against the trusted-directory whitelist, and marks it safe. Then it opens the raw, un-normalized version of the same path.

Those two views of the string can point to different things when an attacker wins a race.

An attacker who can hard-link a setuid binary into a directory they control, and who can win a race by replacing an intermediate path component with a symlink at the right moment, can cause ld.so to load a shared object from outside the trusted directory. The validation passed. The open is somewhere else.

The result is arbitrary code execution at the privilege level of the target setuid binary. On most systems, that means root.

the part that should embarrass the glibc team more than it does

The kernel has a mitigation for exactly this class of attack. fs.protected_hardlinks, set to 1, prevents processes from hard-linking files they do not own. Hard-linking a setuid binary you do not own is the first step in this attack. With protected hardlinks on, the race never starts.

fs.protected_hardlinks shipped in Linux 3.6, which is from 2012. Every major Linux distribution has had it enabled by default for years. Check yours:

sysctl fs.protected_hardlinks

If you get fs.protected_hardlinks = 1, you are mitigated against CVE-2026-86805 today, before you patch. If you get 0, you have a bigger problem than this one CVE and you should fix that first.

The kernel team had the right instinct a decade ago. The glibc team introduced the bug that the kernel mitigation covers, and the bug sat there for fourteen years because most systems had the mitigation and nobody noticed.

Patch regardless. The CVSS score is 6.3 because the mitigation is so common, but that number disappears the moment someone runs your binary on a container host with a stripped-down kernel config or an older embedded distro. fs.protected_hardlinks is not universal.

This is the third glibc CVE batch this month. The wordexp stack clash and fopen charset overflow covered different code paths in the same library. At some point a pattern emerges. The glibc 2.44 FORTIFY_SOURCE hardening from last month is relevant context for why some of these paths are getting more scrutiny now.

The kernel-security hub has the rest of this month’s CVE coverage if your patch queue is already long and you want to triage in one place.

Sources

  1. CVE-2026-86805 Red Hat Security Advisory (Red Hat Product Security)
  2. glibc ld.so TOCTOU race in DT_RPATH $ORIGIN (2.14-2.44) (stack.watch)