Skip to content

lscpu: fix NULL+offset dereference in cpuinfo_parse_cache()#4433

Open
rawrmonster17 wants to merge 1 commit into
util-linux:masterfrom
rawrmonster17:lscpu-fix-null-offset-deref
Open

lscpu: fix NULL+offset dereference in cpuinfo_parse_cache()#4433
rawrmonster17 wants to merge 1 commit into
util-linux:masterfrom
rawrmonster17:lscpu-fix-null-offset-deref

Conversation

@rawrmonster17

Copy link
Copy Markdown

Problem

cpuinfo_parse_cache() in sys-utils/lscpu-cputype.c parses the
s390-style /proc/cpuinfo cache lines (format:
cache<nr> : level=<lvl> type=<type> scope=<scope> size=<size> ...).

Two fields are fetched with an offset applied before the NULL guard:

/* scope= */
p = strstr(data, "scope=") + 6;
if (!p || strncmp(p, "Private", 7) == 0)   /* !p is dead code */
    return 0;

/* type= */
p = strstr(data, "type=") + 5;
if (!p || !*p)                              /* !p is dead code */
    return 0;

When strstr() returns NULL (field absent from the cache line), p
becomes (char *)6 or (char *)5 — a non-NULL near-zero pointer that
passes the !p guard and immediately faults on the subsequent
strncmp() or *p dereference (CWE-476 / CWE-823).

Reachable via lscpu -s <sysroot> with a crafted proc/cpuinfo, or
on real hardware/firmware that emits a malformed s390 cache line.

The same function already uses the correct pattern for level=, size=,
line_size=, and associativity=strstr() result checked first,
arithmetic applied after:

p = strstr(data, "level=");
if (!p || sscanf(p, "level=%d", &level) != 1)
    return 0;

Fix

Move the offset arithmetic to after the NULL check for both affected
fields, matching the established pattern:

/* scope= */
p = strstr(data, "scope=");
if (!p || strncmp(p + 6, "Private", 7) == 0)
    return 0;

/* type= */
p = strstr(data, "type=");
if (!p || !*(p + 5))
    return 0;
p += 5;

No behaviour change when the fields are present. When absent, the
function now returns 0 safely instead of crashing.

Testing

Built with ./configure --disable-all-programs --enable-libsmartcols --enable-lscpu && make lscpu — zero errors, zero warnings on the changed translation unit.

Ran a dedicated self-check covering all six cases (missing field, scope=Private, scope=Shared, missing type=, type=Unified, type=Data) — all pass.

strstr() returns NULL when the needle is absent.  In two places the
return value had a constant offset added before the NULL guard:

  p = strstr(data, "scope=") + 6;
  if (!p ...)                       /* dead: p is (char*)6 if absent */

  p = strstr(data, "type=") + 5;
  if (!p || !*p)                    /* same: p is (char*)5 if absent */

When the field is missing from the cpuinfo cache line, p becomes a
low non-NULL address (CWE-476 / CWE-823).  The !p guard is dead, so
strncmp() or the *p dereference immediately faults.

Fix by moving the arithmetic after the NULL check, matching the
correct pattern already used for "level=", "size=", "line_size=", and
"associativity=" in the same function.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant