high

CVE

CVE-2026-58302

CWE

CWE-22, CWE-427

Affected Surface

linuxcnc-uspace before 2.9.9, LinuxCNC deployments that install rtapi_app with SUID root or equivalent elevated privileges, Debian and Ubuntu systems that package LinuxCNC userspace components, CNC controllers, lab systems, and developer hosts where untrusted local users or code can invoke rtapi_app

CVE-2026-58302 is a fresh Linux privilege-escalation issue in linuxcnc-uspace that NVD published on 30 June 2026. The vulnerable binary is rtapi_app, a LinuxCNC helper that loads realtime modules. On affected builds, it runs with elevated privileges and accepts a user-controlled module name that is later turned into a shared-library path for dlopen().

The vulnerability is conceptually simple and technically important: if a privileged program builds a path like ${trusted_dir}/${user_input}.so without rejecting / or .., the trusted directory is no longer a boundary. Local attackers can climb out of that directory and make the process load an arbitrary shared object under root privileges.

Affected package and versions

The affected package is:

  • linuxcnc-uspace before 2.9.9

NVD explicitly scopes the issue to rtapi_app in linuxcnc-uspace, and the public fix commits show the remediation landing as module-name sanitization. If you package LinuxCNC from source instead of distro packages, compare your tree against the 2.9.9 release or the referenced fix commits.

Start with inventory:

dpkg -l | rg 'linuxcnc|linuxcnc-uspace'
command -v rtapi_app

Then check whether the installed binary is privileged:

ls -l "$(command -v rtapi_app)"
getcap "$(command -v rtapi_app)" 2>/dev/null

The vulnerable code path

The pre-fix logic took the requested module name and interpolated it directly into a path:

static int do_load_cmd(string name, vector<string> args) {
    void *w = modules[name];
    if (w == NULL) {
        char what[LINELEN+1];
        snprintf(what, LINELEN, "%s/%s.so", EMC2_RTLIB_DIR, name.c_str());
        void *module = modules[name] = dlopen(what, RTLD_GLOBAL | RTLD_NOW);
        ...
    }
}

That means an attacker-controlled name such as:

../../tmp/evil

would resolve to a path like:

${EMC2_RTLIB_DIR}/../../tmp/evil.so

If the binary still holds root privileges at that point, any constructor code inside evil.so, or the exported module entrypoint that LinuxCNC later resolves, executes with those same privileges.

The fix

The upstream fix is intentionally small. Before building the path, LinuxCNC now rejects slash characters and .. segments:

if (name.find("/") != std::string::npos || name.find("..") != std::string::npos) {
    rtapi_print_msg(
        RTAPI_MSG_ERR,
        "%s: Not allowed as module name. Slashes or with \"..\" (even /a..b/) are not allowed.\n",
        name.c_str()
    );
    return -1;
}

what = fmt::format("{}/{}.so", EMC2_RTLIB_DIR, name);
void *module = modules[name] = dlopen(what.c_str(), RTLD_GLOBAL | RTLD_NOW);

This is the correct fix because it restores the intended trust boundary: only module basenames are allowed, not attacker-controlled paths.

Why this is more than a niche CNC bug

LinuxCNC is specialized software, but the vulnerability pattern is generic and high value:

  • the target host is Linux
  • the vulnerable binary is privileged
  • exploitation does not require network access
  • arbitrary library load under root usually means immediate full host compromise

That matters for AppSec teams because many real intrusion chains begin with low-privilege code execution. A malicious package, a compromised developer account, an SSRF-to-shell chain, or a weak web app running on the same workstation is often “only local” until a SUID-root primitive turns it into root.

In other words, CVE-2026-58302 is relevant anywhere LinuxCNC is deployed on systems that also handle source code, package management, build jobs, secrets, or remote administration.

Exploit model

The vulnerable flow is short:

unprivileged local user
  -> writes malicious /tmp/evil.so
  -> invokes rtapi_app with module name ../../tmp/evil
  -> rtapi_app formats ${EMC2_RTLIB_DIR}/../../tmp/evil.so
  -> dlopen() loads attacker library before privileges are dropped
  -> attacker code runs as root

The critical boundary failure is not in dlopen() itself. dlopen() is doing exactly what it was asked to do. The bug is that the program treated a path component as though it were a harmless module identifier.

Detection and scoping

Check installed versions and privilege state:

dpkg -l | rg 'linuxcnc|linuxcnc-uspace'
command -v rtapi_app && ls -l "$(command -v rtapi_app)"

Search for the patched behavior if you maintain custom source builds:

rg -n 'name\\.find\\(\"/\"\\)|name\\.find\\(\"\\.\\.\"\\)' /path/to/linuxcnc/source

Prioritize systems where all of these are true:

  • LinuxCNC is installed
  • rtapi_app is SUID root or otherwise runs elevated
  • untrusted local users or code can execute on the host
  • the host stores useful secrets or controls physical equipment

Examples include:

  • shared lab or workshop workstations
  • engineering laptops used for both development and machine control
  • university or training environments with multiple local users
  • industrial systems where a web app, agent, or automation job can reach a shell

Remediation

Upgrade to LinuxCNC 2.9.9 or later, or backport the fix commits if you maintain a vendor fork.

For packaged installs, follow LinuxCNC’s documented update flow and prefer distro or project-supplied packages over ad-hoc source overlays.

If you need a short-term reduction while patching is in progress:

  1. Remove SUID or unnecessary elevated execution from rtapi_app if your environment permits it.
  2. Restrict shell access on LinuxCNC hosts to trusted operators.
  3. Treat any local-code-execution foothold on the host as potentially equivalent to root until the fix is installed.

Example mitigation if you have to reduce privilege exposure temporarily and have validated that your workflow tolerates it:

sudo chmod u-s "$(command -v rtapi_app)"

That is an operational workaround, not a universal fix. Test carefully because LinuxCNC environments may rely on the existing privilege model.

Response guidance

If an affected host may have exposed rtapi_app to untrusted local code, respond as a root-compromise scenario:

  • rotate local secrets stored on the host
  • inspect /tmp, user home directories, and module search paths for suspicious .so files
  • review shell history and process-execution logs for unexpected rtapi_app invocations
  • validate system integrity before returning the controller or workstation to service

For defenders, this is a reminder that path traversal is not only a web vulnerability. When the sink is dlopen() inside a privileged Linux binary, a single unchecked module name is enough to turn “local user” into root.

References