C library vulnerabilities in embedded firmware are inherited by every program on the device from whichever libc the image was built with (glibc, uClibc, uClibc-ng or musl), and they include actively exploited flaws such as glibc's CVE-2023-4911, listed in CISA's KEV catalog since 2023-11-21. To handle them, identify the libc and its exact version from the loader, file names and version strings, check that project's own advisories, because the three publish fixes very differently, rank the matched CVEs by KEV, EPSS and reachability, then update or backport, rebuild every static binary and re-scan.
Key takeaways
- Because nearly every user-space program on an embedded Linux device calls into it.
- Unpack the image, read the loader and library names, then read the version text inside the files.
- The libc CVEs that matter most on devices sit in name resolution and in the dynamic loader, because DNS answers come from the network and the loader runs with elevated privileges for setuid programs.
- Because each project publishes fixes through a different channel, and scanners see only what reaches a CVE record with a usable version range.
Why does the C library matter so much in firmware?
Because nearly every user-space program on an embedded Linux device calls into it. Name resolution, environment handling, string conversion, regular expressions and the dynamic loader itself all live in the C library, so one flaw there reaches the web server, the update agent, the BusyBox shell and any vendor daemon at once. The choice is also made very early. The Buildroot manual states that its internal toolchain backend "supports several C libraries: uClibc-ng, glibc and musl", and that changing the type of C library or its configuration usually means a complete rebuild of the whole system. The Yocto Project's TCLIBC variable offers "glibc", "musl", "newlib" or "baremetal". Whatever the SDK or board support package picked at the start tends to stay for the life of the product.
The three libraries differ in more than size:
| Library | Typical use | Release and fix model | Where security fixes are announced |
|---|---|---|---|
| glibc | Larger devices, gateways, Debian- or Yocto-based images | Regular releases, with a set of maintained release branches that receive backports | GLIBC-SA advisories in the glibc git repository, with per-branch Fix-Commit tags |
| uClibc (original) | Older routers, cameras and SoC SDKs | Last release 0.9.33.2 on 15 May 2012 | No releases since 2012; CVEs stay unfixed upstream |
| uClibc-ng | Buildroot, OpenADK and Freetz-NG images, MMU-less targets | One stable line with frequent point releases | Commits and releases; the project site has no separate advisory page |
| musl | Small static binaries, Alpine-based containers, newer Buildroot and Yocto images | One stable 1.2 series | Advisories on musl.libc.org and the musl and oss-security mailing lists, usually with patches |
How do you identify which libc and version a firmware image ships?
Unpack the image, read the loader and library names, then read the version text inside the files. A practical sequence:
Unpack the root filesystemExtract the SquashFS, UBIFS, JFFS2 or ext4 filesystem with binwalk or the vendor's tooling. If parts refuse to unpack, our firmware entropy analysis guide explains how to tell encrypted regions from compressed ones. Read the loader nameThe program interpreter named in any ELF header, or the files in /lib, already identify the family. glibc usesld-linux-<arch>.so.N(for exampleld-linux-armhf.so.3orld-linux-aarch64.so.1) next tolibc.so.6. uClibc-ng names its loaderld-uClibc.so.<ABI>, orld64-uClibc.so.<ABI>on 64-bit targets such as x86-64. musl's build installs its loader asld-musl-<arch>.so.1, which points at musl's singlelibc.so.Read the glibc bannerglibc's libc carries a banner that starts "GNU C Library", followed by the package version in brackets and "release version 2.NN". Running stringsoverlibc.so.6and searching for "GNU C Library" finds it without executing anything. Distribution builds often put their own package version inside the brackets, which is the first hint of backported fixes. Note that the glibc 2.34 NEWS records the end of versioned file names such aslibc-2.33.so: from 2.34 onward the shared objects are installed directly under their ABI sonames, so on newer images the banner, not the file name, carries the version.Read the uClibc-ng file namesuClibc-ng's build names the real library libuClibc-<version>.soand the real loaderld-uClibc-<version>.so(orld64-uClibc-<version>.so), with the short sonames as symlinks, so the version is in the file name. Images built on the original uClibc 0.9.x usually showlibc.so.0andld-uClibc.so.0and a 0.9.x version in the real file names.Read the musl versionmusl's loader prints "musl libc", its architecture and "Version 1.2.x" when it is run with no arguments, on the device or under user-mode emulation. Without execution, the bare version string is still inside libc.so, but it is not labelled, so confirm it against package metadata or the build manifest.Search for static copiesmusl in particular is often linked statically, and each static binary carries its own libc copy at whatever version it was built with. Run the same searches across every executable, and record each static copy as its own component. Check build and package recordsBuildroot images may ship the build's package list, OpenWrt images carry opkg metadata and Debian-based images carry a dpkg status file. Our embedded Linux build systems and SBOM guide covers what Buildroot and Yocto can emit directly.
Record the result, family, version, path and whether it is shared or static, in the firmware SBOM. A libc entry with a wrong family is worse than none: a uClibc-ng 1.0.40 library recorded as "glibc" matches the wrong CVE list entirely.
Which C library CVEs matter most for embedded devices?
The libc CVEs that matter most on devices sit in name resolution and in the dynamic loader, because DNS answers come from the network and the loader runs with elevated privileges for setuid programs. Public data, read on 2026-11-12:
| CVE | Library | What it is | Affected / fixed | CISA KEV (catalog 2026.11.10) | FIRST EPSS (2026-11-10) |
|---|---|---|---|---|---|
| CVE-2015-7547 | glibc | Stack-based buffer overflow in getaddrinfo() DNS handling | Introduced in 2.9 (2008); NVD lists versions before 2.23 | Not listed | 0.924, ~99.8th percentile |
| CVE-2023-4911 ("Looney Tunables") | glibc | Buffer overflow in ld.so's GLIBC_TUNABLES parsing, local privilege escalation via setuid programs | Introduced in 2.34; fixed in 2.39 and backported to the 2.34 to 2.38 branches | Listed, added 2023-11-21 | 0.638, ~99.2nd percentile |
| CVE-2021-43523 | uClibc, uClibc-ng | Special characters in DNS-returned host names not rejected, enabling injection into applications | uClibc-ng before 1.0.39; no uClibc release | Not listed | 0.031, ~87.8th percentile |
| CVE-2022-30295 | uClibc, uClibc-ng | Predictable DNS transaction IDs, enabling cache poisoning | uClibc-ng through 1.0.40, uClibc through 0.9.33.2 | Not listed | 0.112, ~96.0th percentile |
| CVE-2025-26519 | musl | Out-of-bounds write in iconv() converting EUC-KR to UTF-8 | 0.9.13 through 1.2.5; fixed in 1.2.6 and by patches | Not listed | 0.0035, ~28.8th percentile |
| CVE-2026-107891 | musl | Integer overflows in regcomp() leading to undersized heap allocations | 0.5.0 through 1.2.6; patch published | Not listed | 0.0055, ~46.3rd percentile |
CVE-2015-7547 is the reference case for glibc's resolver. The glibc announcement on libc-alpha explains that calling getaddrinfo with the AF_UNSPEC family sends parallel A and AAAA queries, and that buffer mismanagement lets a crafted DNS response write up to 65535 bytes into a 2048-byte stack buffer, with attacker-controlled bytes. The same post lists mitigations that do not work, including options single-request and disabling IPv6, and notes that the flaw lives only in glibc's own copy of libresolv. Any networked device whose programs resolve names through glibc is in scope, which is almost all of them; our dnsmasq vulnerabilities guide covers the resolver that often sits in front of them on the same device.
CVE-2023-4911 shows how the loader becomes an attack surface. The glibc advisory GLIBC-SA-2023-0004 states that a tunable of the form NAME=NAME=VAL passed to a setuid program "may result in a buffer overflow, which could be exploited to achieve escalated privileges", and that the flaw was introduced in glibc 2.34. The Qualys advisory reports full root on default installs of Fedora 37 and 38, Ubuntu 22.04 and 23.04, and Debian 12 and 13, and names Alpine Linux as a notable exception because it uses musl rather than glibc.
The uClibc DNS pair matters because these resolvers sit on small routers and cameras that resolve names constantly. uClibc-ng's fix for CVE-2021-43523, commit 0f822af from August 2021, adds a check of DNS answers for validity "using the same rules glibc does" and shipped in 1.0.39. For CVE-2022-30295, commit f73fcb3 of 10 May 2022 replaced the previous simple counter with configurable query ID generation, random by default where possible, and shipped in 1.0.41. Note that the commit message does not name the CVE, which is typical of how uClibc-ng records security fixes.
The musl iconv and regcomp bugs depend on what the application does. The CVE-2025-26519 advisory says the bug is exposed only when an application opens a conversion from EUC-KR to UTF-8 and feeds it untrusted input, most commonly when the declared charset of XML, HTML or MIME email is passed to iconv_open. The CVE-2026-107891 advisory says an application is vulnerable only if it compiles regular expressions from an untrusted source with the libc regcomp function.
Why do glibc, uClibc-ng and musl differ in CVE coverage?
Because each project publishes fixes through a different channel, and scanners see only what reaches a CVE record with a usable version range.
glibc publishes structured advisories. Since 2023 the glibc repository holds a GLIBC-SA advisory per issue; the advisory list read on 2026-11-12 ran from GLIBC-SA-2023-0001 to GLIBC-SA-2026-0028. The advisory format README defines tags for CVE-Id, Public-Date, Vulnerable-Commit and one Fix-Commit per release branch. It also explains a detail that matters for embedded builds: a release-version such as 2.36-118 means the fix is on the release/2.36/master branch "and not in any released tarball". For CVE-2023-4911, the fix is in the 2.39 tarball and on branches 2.34 through 2.38 as commits only, so a device built from the 2.36 tarball stays vulnerable unless the build tracks the branch or applies the commit. The glibc Release wiki page listed release branches 2.32 through 2.44 as maintained when read on 2026-11-12. The glibc security policy also sets out what counts as a security bug, for example that regex denial of service from crafted patterns is treated as a regular bug, which explains why some scanner findings never receive a CVE.
The original uClibc has no fix channel. The uClibc home page states that "uClibc releases are on hold", points to uClibc-ng, and lists 0.9.33.2 from 15 May 2012 as the last release. Every CVE that NVD records against uClibc after that date, such as CVE-2022-30295, has no upstream fix in that codebase.
uClibc-ng fixes in commits and releases. The uClibc-ng project site describes it as a spin-off of uClibc whose main goal is "to provide regulary a stable and tested release", and listed 1.0.60 released on 7 November 2026 as the latest release. The site has news, downloads and a test suite, but no security advisory page. Fixes land as commits that sometimes do not name the CVE, so the version ranges in NVD records are often the only machine-readable link between a CVE and a fixed release.
musl publishes advisories with patches. The musl home page carries a security advisory banner, which on 2026-11-12 listed CVE-2026-107891, CVE-2026-40200 and CVE-2026-6042 as affecting all releases through 1.2.6. The release history gives each CVE's affected range, for example CVE-2026-40200, a stack-based overflow in qsort with large arrays that affects only 32-bit architectures. Fixes are often patches well before a release: the musl 1.2.6 release of 20 March 2026 "includes the fix for CVE-2025-26519 affecting iconv that was previously only available as patches", more than a year after the February 2025 advisory. A version-only scanner therefore flags a correctly patched musl 1.2.5, and cannot see that a 1.2.6 build lacks the regcomp patch unless the CVE range is recorded.
How do you triage C library CVEs in a firmware image?
Start with exploitation data, then check whether the vulnerable code path is reachable on this device, then check for backports.
KEV firstIn CISA's KEV catalog version 2026.11.10, CVE-2023-4911 was the only entry for the GNU C Library, and no uClibc, uClibc-ng or musl CVE was listed. A device that gives any local user a shell and runs glibc 2.34 to 2.38 without the branch fix goes to the top of the list. Our KEV and EPSS prioritisation guide explains how the two signals combine. Check reachability against the deviceFor CVE-2023-4911, list the setuid and setgid files in the unpacked filesystem and check who can get a shell; a command injection in the web interface is a common way to get the local foothold the exploit needs. For the DNS CVEs, check whether the device resolves names over untrusted networks. For the musl iconv and regcomp bugs, check whether any program converts untrusted charsets or compiles untrusted regular expressions. Check for backports before accepting a version matchFor glibc, compare the device's source or SDK patch set against the Fix-Commit for its branch in the GLIBC-SA advisory. For musl, look for the advisory's patch files in the build's patch directory. For uClibc-ng, compare against the fixing commit. A version string alone proves only the upstream base version. Record decisionsWhen a CVE is fixed by a backport or unreachable by configuration, record it as a VEX statement rather than leaving a silent gap; our CSAF and VEX for device makers guide covers the format and the VEX generator drafts one.
How do you fix C library vulnerabilities in firmware?
Update within the library's supported line. For glibc, build from the head of the maintained release branch rather than the original tarball, or cherry-pick the Fix-Commit the advisory lists for your branch; for CVE-2023-4911 on 2.36, that is the commit tagged 2.36-118. For uClibc-ng, move to the latest 1.0.x release, which on 2026-11-12 was 1.0.60. For musl, apply the patches attached to each advisory, or move to a release that includes them.
Leave the original uClibc. A product still on uClibc 0.9.x receives no upstream fixes. The uClibc-ng site states that porting applications from glibc to uClibc-ng "typically involves just recompiling the source code", and moving from the original uClibc to uClibc-ng is the shortest path. Moving to musl or glibc is the alternative where the product needs a library with a published advisory process. The Buildroot manual notes that changing the type of C library generally needs a complete rebuild, so plan the switch as a full release.
Use the build system's CVE tooling. The Buildroot manual describes make pkg-stats, whose output includes "whether known CVEs (security vulnerabilities) affect the packages in your current configuration", and the LIBFOO_IGNORE_CVES variable for CVEs fixed by a patch in the package, which must be preceded by a Makefile comment naming the patch. That makes a backport visible to the next scan instead of a silent exception.
Rebuild every static binary. A shared libc update does not touch programs linked statically against an older copy. The CVE-2025-26519 advisory says static binaries that cannot be relinked can be patched by overwriting the EUC-KR charset names in the binary, which disables that conversion entirely; treat that as a stopgap, not a fix.
Rebuild, re-scan, publish. Regenerate the SBOM, compare it with the previous release to confirm every libc copy changed, and publish the affected and fixed versions in your advisory. The SBOM diff tool compares two SBOM files. The same inventory step applies to the other shared components that sit on top of libc; see our BusyBox vulnerabilities, OpenSSL vulnerabilities and Linux kernel CVEs in embedded devices guides.
AI-assisted analysis: what the scan actually runs
SoftWSP's scan works from the firmware's software bill of materials. Where the SBOM records the C library as a component with a version, the scan matches that version against CVE records, CISA's KEV catalog and FIRST's EPSS scores, the same deterministic matching it applies to every other component. This part is code: the code scores, and AI narrates.
AI then writes the plain-language summary on top of that matching: which glibc, uClibc-ng or musl CVEs matched, which are KEV-listed or have a high EPSS score, and what the version evidence was. Every sentence cites the component, version and record it relies on, and an engineer reviews every finding before the report is delivered. The judgements this guide describes as manual stay with that engineer: whether the libc family and version were identified correctly, whether a static binary carries an older copy, whether a vendor applied a glibc Fix-Commit or a musl patch behind an unchanged version, and whether a setuid binary or untrusted DNS path makes a CVE reachable. Because matching is version-based, a correctly patched musl 1.2.5 is still flagged for CVE-2025-26519; the review is where the patch evidence turns that flag into a documented not-affected decision.
For where this check fits in a full assessment, see the OWASP Firmware Security Testing Methodology guide, or device & firmware security services to have a specific image reviewed.
This guide draws on the glibc GLIBC-SA advisories, advisory format README, security policy, Release wiki page and 2.34 NEWS, the 2016 libc-alpha announcement of CVE-2015-7547, the Qualys Looney Tunables advisory, the uClibc and uClibc-ng project sites and uClibc-ng commits, the musl home page, release history and Openwall advisories, the Buildroot manual, the Yocto Project reference manual, NVD, CISA's KEV catalog (version 2026.11.10) and FIRST EPSS data dated 2026-11-10, all read directly on 2026-11-12.
Put this guide to work
Frequently asked questions
How do I tell whether a firmware image uses glibc, uClibc-ng or musl?
Unpack the root filesystem and look at the dynamic loader and the libc file names in /lib. glibc ships a loader named ld-linux-<arch>.so.N next to libc.so.6, and its libc carries a banner that begins "GNU C Library" and states the release version. uClibc-ng installs ld-uClibc.so.<ABI> and a real library file named libuClibc-<version>.so, so the version is in the file name. musl installs ld-musl-<arch>.so.1 as its loader, and when that loader is run with no arguments it prints "musl libc" and its version. Statically linked binaries carry their own copy of the library, so run the same string search over every executable, not only /lib.
Is a device with glibc 2.34 or later automatically exploitable through Looney Tunables (CVE-2023-4911)?
No. The glibc advisory GLIBC-SA-2023-0004 describes a buffer overflow in the dynamic loader's handling of the GLIBC_TUNABLES environment variable when a setuid program is launched, introduced in glibc 2.34. An attacker needs a local foothold and a setuid binary on the device. Qualys obtained full root on default Fedora, Ubuntu and Debian installs, and CISA added the CVE to its Known Exploited Vulnerabilities catalog on 2023-11-21, so any device with an interactive shell, setuid binaries and an unpatched 2.34 to 2.38 glibc should be fixed first. On a locked-down image with no setuid files the path Qualys used is absent, but a command injection bug in the web interface can supply the local foothold.
Why do uClibc-based devices often show CVEs that never get fixed?
Because the original uClibc stopped releasing in 2012. Its website lists 0.9.33.2, released 15 May 2012, as the last release and points users to uClibc-ng. NVD records CVE-2022-30295, predictable DNS transaction IDs that can lead to cache poisoning, as affecting uClibc through 0.9.33.2 and uClibc-ng through 1.0.40. uClibc-ng changed its DNS query ID generation in May 2022 and shipped that in 1.0.41, but no uClibc 0.9.x release carries a fix, so devices on the old library stay exposed until the vendor backports the change or moves to uClibc-ng or musl.
Where does musl publish its security fixes?
On its own website and on the Openwall mailing lists. The musl home page and release history list each CVE with the affected version range, and Rich Felker posts the advisory with source patches to the musl list, copied to oss-security. Fixes are often available as patches long before a release: CVE-2025-26519, an out-of-bounds write in iconv, was announced with patches on 13 February 2025 and first shipped in a release with musl 1.2.6 on 20 March 2026. For CVE-2026-107891 in regcomp, announced 21 October 2026, the advisory gives a patch against 1.2.6, which was still the latest release on 12 November 2026.
Does SoftWSP decide whether a vendor backported a C library fix?
No, not automatically. SoftWSP's scan takes the C library as a component with its version from the firmware's software bill of materials and matches that version against CVE records, CISA's KEV catalog and FIRST EPSS scores. AI then writes a plain-language summary that cites the component, version and record behind each finding, and an engineer reviews every finding before the report is delivered. Whether a vendor cherry-picked a glibc Fix-Commit or applied a musl patch without changing the version, and whether a setuid binary or untrusted DNS path makes a CVE reachable, is decided by that engineer from the evidence, not by the scan.
Sources
- glibc: GLIBC-SA-2023-0004, tunables: local privilege escalation through buffer overflow (CVE-2023-4911)
- glibc: Security advisory format (advisories README)
- glibc: The GNU C Library Security Policy (SECURITY.md)
- glibc wiki: Release (current release branches and distribution branch mapping)
- glibc 2.34 NEWS (shared objects installed under ABI sonames)
- Carlos O'Donell, libc-alpha, 16 Feb 2016: CVE-2015-7547, glibc getaddrinfo() stack-based buffer overflow
- Qualys Security Advisory: Looney Tunables, Local Privilege Escalation in the glibc's ld.so (CVE-2023-4911)
- uClibc: project home page (0.9.33.2, 15 May 2012, releases on hold)
- uClibc-ng: Embedded C library (project home page, latest release 1.0.60)
- uClibc-ng commit f73fcb3: DnsLookup, configurable dnsQueryId generation including random
- uClibc-ng commit 0f822af: add __hnbad to check DNS entries for validity
- musl libc: home page and security advisories
- musl libc: Release History and Security Advisories
- Rich Felker, musl list, 13 Feb 2025: CVE-2025-26519, input-controlled out-of-bounds write primitive in iconv()
- Rich Felker, musl list, 21 Oct 2026: CVE-2026-107891, multiple integer overflow in regcomp
- The Buildroot user manual
- Yocto Project Reference Manual: Variables Glossary (TCLIBC)
- NIST NVD: CVE-2022-30295
- NIST NVD: CVE-2021-43523
- CISA: Known Exploited Vulnerabilities (KEV) Catalog
About this guide
SoftWSP Firmware Security Desk
Covers the Firmware vulnerabilities & fixes cluster: 110 guides.

In this cluster
Questions about this page?
