Skip to content

fake_kworker: stop firing CRITICAL on genuine Linux kernel images - #1696

Merged
egibs merged 2 commits into
chainguard-dev:mainfrom
dustinkirkland:kirkland/kworker-kernel-fp
Sep 7, 2026
Merged

fake_kworker: stop firing CRITICAL on genuine Linux kernel images#1696
egibs merged 2 commits into
chainguard-dev:mainfrom
dustinkirkland:kirkland/kworker-kernel-fp

Conversation

@dustinkirkland

Copy link
Copy Markdown
Member

Fixes #1695.

Every uncompressed Linux kernel image carries kernel/workqueue.c's own worker-naming format strings, so fake_kworker flags real kernels as CRITICAL "Pretends to be a kworker kernel thread". A kernel is not pretending to be a kworker; it is the thing that creates them.

Why it fires

The rule already recognised one member of this family:

$not_rescue = "kworker/R-%s"

which is itself a kernel string — the rescuer-thread name. A kernel carries four such spellings and only that one was subtracted, so the count always exceeded the exclusions and the rule fired on every kernel, on every rebuild. This adds its three siblings from the same source file, so a genuine kernel is fully explained rather than one quarter explained.

Counted, not suppressed, matching how the existing exclusions already work. The distinction the rule now draws is between format strings carrying % conversions and the rendered names malware copies to blend into ps output.

Verification

yara 4.5.5, against the existing samples in tests/linux/2024.kworker_pretenders:

target before after
kernel strings only FIRES clean
kernel strings + [kworker/0:0] FIRES FIRES
gafgyt FIRES FIRES
emp3r0r.agent FIRES FIRES
aclocal.m4 clean clean

Both real masquerade samples are still detected. Row two is the one that matters for the tradeoff: because the exclusions are counted rather than suppressed, a sample that ships the kernel's format strings and a rendered name still exceeds the exclusions and still fires — so this does not hand anything a bypass by embedding kernel strings.

Diffing every rule in the file across all five targets, the only behavioural change is fake_kworker on the kernel-strings input.

The separate kworker: medium rule still matches, deliberately left alone: "Mentions kworker" is accurate for a kernel, and its filesize < 1MB gate puts real kernel images out of scope anyway.

Origin

Observed on linux-firecracker-6.18, a kernel built from source in chainguard-dev/stereo, where it fails the scan on every version bump — so the finding is permanently red and gets routinely ignored, which is the failure mode worth avoiding for a CRITICAL rule.

Only uncompressed images are affected. A sibling package shipping compressed vmlinuz reports no findings in the same scan, because the strings are unreadable until decompression — so today whether the CRITICAL fires depends on compression rather than on anything security-relevant.

Happy to add a kernel sample to malcontent-samples if that is wanted; it would be a large binary, so I have not assumed.

Fixes chainguard-dev#1695.

Every uncompressed Linux kernel image carries kernel/workqueue.c's own
worker-naming format strings, so fake_kworker flags real kernels as
"Pretends to be a kworker kernel thread". A kernel is not pretending to be
a kworker; it is the thing that creates them.

The rule already recognised one member of this family:

    $not_rescue = "kworker/R-%s"

which is itself a kernel string -- the rescuer-thread name. A kernel carries
four such spellings and only that one was subtracted, so the count always
exceeded the exclusions and the rule fired on every kernel, on every rebuild.
Adding its three siblings from the same source file makes a genuine kernel
fully explained instead of one quarter explained.

Counted, not suppressed, matching how the existing exclusions work: a sample
carrying the kernel's format strings *and* a rendered name still exceeds the
exclusions and still fires. The distinction the rule now draws is between
format strings with % conversions and the rendered names malware copies to
blend into ps output.

Verified with yara 4.5.5 against the existing samples:

    target                        before     after
    ----------------------------  ---------  ---------
    kernel strings only           FIRES      clean
    kernel strings + [kworker/0:0] FIRES     FIRES
    2024.kworker_pretenders/gafgyt         FIRES  FIRES
    2024.kworker_pretenders/emp3r0r.agent  FIRES  FIRES
    2024.kworker_pretenders/aclocal.m4     clean  clean

Both real masquerade samples are still detected. Diffing every rule in the
file across all five targets, the only behavioural change is fake_kworker on
the kernel-strings input.

The separate `kworker: medium` rule still matches, deliberately left alone:
"Mentions kworker" is accurate for a kernel, and its filesize < 1MB gate puts
real kernel images out of scope anyway.

Observed on linux-firecracker-6.18, a kernel built from source in
chainguard-dev/stereo. Only uncompressed images are affected -- a sibling
package shipping compressed vmlinuz reports no findings in the same scan,
because the strings are unreadable until decompression, so today the CRITICAL
depends on compression rather than on anything security-relevant.

Happy to add a kernel sample to malcontent-samples if that is wanted; it
would be a large binary, so I have not assumed.
@egibs
egibs merged commit d3f7190 into chainguard-dev:main Sep 7, 2026
17 checks passed
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.

false positive: fake_kworker fires CRITICAL on genuine Linux kernel images

2 participants