[GH-ISSUE #7669] [Bug] Kernel-module guide omits the unsupported status for compressed this_module sections #4592

Open
opened 2026-10-31 00:51:05 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 28, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7669

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Documentation

Observed behavior

The Linux kernel-module guide gives three possible presence statuses for .gnu.linkonce.this_module: present, absent, and unknown. Public CLI inspection also returns unsupported when that section is stored compressed.

Running node scripts/rea.mjs inspect-kernel-module <compressed.ko> --json on an independently generated ELF64 relocatable object succeeded and returned normalized_result.this_module.status: "unsupported". The matching uncompressed input returned "present". The compressed input includes a valid zlib compression header and payload; the result is not dependent on malformed compression bytes.

node scripts/rea.mjs inspect-kernel-module --schema --json returns input argument and option schemas only. It does not give callers a result-status enumeration through that flag.

Reproduction

  1. Use the already built baseline checkout, with Node v24.22.1 on PATH. The recipe uses Python 3's standard library, creates inert fixtures and isolated state directories, places blocking package-runner fixtures first on PATH, and removes its temporary directory automatically.
  2. From the checkout root, run:
python3 -I - <<'PYTHON'
import json, os, pathlib, struct, subprocess, tempfile, zlib

repo = pathlib.Path.cwd()
with tempfile.TemporaryDirectory() as directory:
    work = pathlib.Path(directory)
    for folder in ('bin', 'home', 'config', 'data', 'cache', 'state', 'runtime', 'temp'):
        (work / folder).mkdir()
    for runner in ('npm', 'npx', 'bun', 'bunx', 'pnpm', 'pnpx', 'yarn'):
        stub = work / 'bin' / runner
        stub.write_text('#!/bin/sh\necho "blocked package runner" >&2\nexit 97\n')
        stub.chmod(0o755)
    env = {'PATH': str(work / 'bin') + os.pathsep + os.environ['PATH'],
           'REA_LOG_LEVEL': 'silent', 'npm_config_prefix': str(work / 'npm-global')}
    for key, folder in (('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'),
                        ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'),
                        ('XDG_STATE_HOME', 'state'), ('XDG_RUNTIME_DIR', 'runtime'),
                        ('TMPDIR', 'temp')):
        env[key] = str(work / folder)
    names = ['.shstrtab', '.modinfo', '.gnu.linkonce.this_module', '.init.text']
    strings, indices = bytes(1), {}
    for name in names:
        indices[name] = len(strings)
        strings += name.encode() + bytes(1)
    raw = b'module-layout-placeholder' + bytes(8)
    packed = struct.pack('<IIQQ', 1, 0, len(raw), 8) + zlib.compress(raw)
    assert zlib.decompress(packed[24:]) == raw
    for compressed in (False, True):
        payload = packed if compressed else raw
        sections = [strings, b'license=GPL' + bytes(1) + b'name=verifier' + bytes(1),
                    payload, payload]
        image = bytearray(64 + 5 * 64)
        ident = bytes([127]) + b'ELF' + bytes([2, 1, 1]) + bytes(9)
        struct.pack_into('<16sHHIQQQIHHHHHH', image, 0, ident,
                         1, 62, 1, 0, 0, 64, 0, 64, 0, 0, 64, 5, 1)
        for index, (name, data) in enumerate(zip(names, sections), 1):
            image.extend(bytes((-len(image)) % 8))
            offset = len(image)
            image.extend(data)
            flags = 0x800 if compressed and name in names[2:] else 0
            struct.pack_into('<IIQQQQIIQQ', image, 64 + index * 64,
                             indices[name], 3 if index == 1 else 1,
                             flags, 0, offset, len(data), 0, 0, 8, 0)
        target = work / ('compressed.ko' if compressed else 'plain.ko')
        target.write_bytes(image)
        result = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-kernel-module',
                                 str(target), '--json'], cwd=repo, env=env,
                                capture_output=True, text=True, timeout=60, check=True)
        assert not result.stderr, result.stderr
        report = json.loads(result.stdout)['normalized_result']
        print(target.name, 'exit=0', json.dumps({'this_module': report['this_module'],
                                               'init_text': report['init_text']}))
    schema = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-kernel-module',
                             '--schema', '--json'], cwd=repo, env=env,
                            capture_output=True, text=True, timeout=60, check=True)
    assert not schema.stderr, schema.stderr
    print('--schema exit=0 keys=' + ','.join(json.loads(schema.stdout)))
PYTHON
  1. Compare the printed this_module.status values with the guide's three-value list. This exact recipe was executed once. Two earlier independent compressed inspections produced the same fourth status; both uncompressed inspections completed with present.

Expected behavior

The guide should include unsupported in the possible .gnu.linkonce.this_module statuses and explain that compressed storage selects that result. Its status list should agree with the implemented output contract. The result can retain the existing stored range and layout: "unknown" semantics.

Impact and scope

P3 documentation error: the prose presents an incomplete set of possible values. The inspected metadata remains available, and the output contract in source already allows the returned status. No failed downstream consumer, incorrect decoding, data loss, or security boundary crossing was established.

Scope checked: synthetic ELF64 little-endian x86-64 ET_REL objects with complete section names and file-backed metadata, on one host. Actual distribution modules, MCP execution, other ELF classes or byte orders, and the prevalence of compressed .gnu.linkonce.this_module sections were not tested. Callers can consult the source output contract and handle the returned unsupported value; that is an available workaround for the incomplete prose.

REA version

Exact node scripts/rea.mjs --version output:

6.8.0

Source baseline: 65f1c447344a423fa2785847dbcea1e9a9e2454a.

Runtime and platform

Node.js v24.22.1; Darwin arm64 (macOS). Python 3 generated the fixtures using struct and zlib. The inspection used REA's built-in reader without an external analysis provider.

Target and artifact context

A 603-byte compressed fixture and a 553-byte uncompressed control. Both contain .shstrtab, .modinfo, .gnu.linkonce.this_module, and .init.text. The compressed variant gives the last two sections SHF_COMPRESSED and a valid 24-byte ELF64 compression header followed by zlib data. The original payload is 33 bytes and each compressed section occupies 59 stored bytes. The recipe checks that decompression recovers the original bytes. Neither file is loaded as a kernel module.

Interface

CLI

Earliest failure stage

Unknown

Relevant output or evidence

The executed recipe printed:

plain.ko exit=0 {"this_module": {"status": "present", "location": {"offset": 480, "bytes": 33}, "layout": "unknown"}, "init_text": {"status": "present", "location": {"offset": 520, "bytes": 33}, "bytes": 33, "compressed": false}}
compressed.ko exit=0 {"this_module": {"status": "unsupported", "location": {"offset": 480, "bytes": 59}, "layout": "unknown"}, "init_text": {"status": "present", "location": {"offset": 544, "bytes": 59}, "bytes": 59, "compressed": true}}
--schema exit=0 keys=args,options

All listed commands exited 0 with empty stderr. The inspected results had applicability: "applicable" and decoded the normal license=GPL and name=verifier entries. Earlier complete-output inspection showed a generic limitation about the unknown struct module layout and a compression limitation for .init.text; it contained no compression explanation specifically for this_module.

Baseline source locators:

  • Expected guide: docs/kernel-modules.md:53-55 enumerates present, absent, or unknown and says the layout stays unknown. Lines 59-60 separately explain compressed init/exit text storage sizes.
  • Actual output contract: src/domain/native/kernelModule.ts:143-148 includes unsupported in thisModuleSchema.status. src/contracts/native/binaryDiagnosticsToolContracts.ts:145-146 binds the containing schema as the tool's output contract.
  • Supported public route: src/cli/binaryDiagnosticsCommands.ts:128-160 invokes kernel-module inspection; src/application/binaryDiagnostics/KernelModuleService.ts:60-64 validates the provider report using the output schema.
  • Returning branch: src/native/elf/KernelModuleParser.ts:360-375 selects unsupported for a file-backed compressed section and preserves its location.

The selected failure stage is Unknown because this finding concerns inaccurate guide text; the inspected runtime commands succeed.

Analysis or suspected cause

Source-derived conclusion: the parser and source output schema agree on the fourth status, while the guide's explicit list omits it. The evidence supports updating the documented enumeration. No runtime contract change or additional output field is needed to establish this documentation defect. The absent compression explanation in the result is context rather than a separately established bug.

Additional context

Duplicate searches covered open and closed issues, issue comments, and open, merged, and closed PRs using kernel-module, this_module, compressed, unsupported, and guide/status queries.

Issue #7324 and PR #7491 address the signature magic's byte count; the PR changes only that count. Issue #7587 concerns verbatim CLI input-path provenance, and issue #7437 concerns MCP input-path schema validation. Closed issue #6869 concerns compressed note decoding in a different inspection command. The merged feature PR #7010, including its discussion and inline comments, does not report this missing guide status. The documentation-index duplicate in issue #7405 and PR #7476 is also distinct. No report with the same violated contract and closure signal was found.

No last known good guide revision was established. Verification changed no checkout files and published nothing. Proposed restoration check: the guide includes the compressed-section outcome while the same fixture continues to return unsupported and its uncompressed control returns present.

Triage assessment

  • Impact level: P3
  • Assessment status: supported
  • Impact basis: The guide's enumerated values disagree with successful CLI output, but the source output contract already accepts the value and no consequential client failure was observed.
  • Workaround status: available
  • Workaround basis: The source output schema explicitly lists unsupported, and the executed CLI returns it with the stored section range; callers can use those facts to handle the value without a runtime change.
Originally created by @coygeek on GitHub (Oct 28, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7669 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Documentation ### Observed behavior The Linux kernel-module guide gives three possible presence statuses for `.gnu.linkonce.this_module`: `present`, `absent`, and `unknown`. Public CLI inspection also returns `unsupported` when that section is stored compressed. Running `node scripts/rea.mjs inspect-kernel-module <compressed.ko> --json` on an independently generated ELF64 relocatable object succeeded and returned `normalized_result.this_module.status: "unsupported"`. The matching uncompressed input returned `"present"`. The compressed input includes a valid zlib compression header and payload; the result is not dependent on malformed compression bytes. `node scripts/rea.mjs inspect-kernel-module --schema --json` returns input argument and option schemas only. It does not give callers a result-status enumeration through that flag. ### Reproduction 1. Use the already built baseline checkout, with Node v24.22.1 on PATH. The recipe uses Python 3's standard library, creates inert fixtures and isolated state directories, places blocking package-runner fixtures first on PATH, and removes its temporary directory automatically. 2. From the checkout root, run: ```sh python3 -I - <<'PYTHON' import json, os, pathlib, struct, subprocess, tempfile, zlib repo = pathlib.Path.cwd() with tempfile.TemporaryDirectory() as directory: work = pathlib.Path(directory) for folder in ('bin', 'home', 'config', 'data', 'cache', 'state', 'runtime', 'temp'): (work / folder).mkdir() for runner in ('npm', 'npx', 'bun', 'bunx', 'pnpm', 'pnpx', 'yarn'): stub = work / 'bin' / runner stub.write_text('#!/bin/sh\necho "blocked package runner" >&2\nexit 97\n') stub.chmod(0o755) env = {'PATH': str(work / 'bin') + os.pathsep + os.environ['PATH'], 'REA_LOG_LEVEL': 'silent', 'npm_config_prefix': str(work / 'npm-global')} for key, folder in (('HOME', 'home'), ('XDG_CONFIG_HOME', 'config'), ('XDG_DATA_HOME', 'data'), ('XDG_CACHE_HOME', 'cache'), ('XDG_STATE_HOME', 'state'), ('XDG_RUNTIME_DIR', 'runtime'), ('TMPDIR', 'temp')): env[key] = str(work / folder) names = ['.shstrtab', '.modinfo', '.gnu.linkonce.this_module', '.init.text'] strings, indices = bytes(1), {} for name in names: indices[name] = len(strings) strings += name.encode() + bytes(1) raw = b'module-layout-placeholder' + bytes(8) packed = struct.pack('<IIQQ', 1, 0, len(raw), 8) + zlib.compress(raw) assert zlib.decompress(packed[24:]) == raw for compressed in (False, True): payload = packed if compressed else raw sections = [strings, b'license=GPL' + bytes(1) + b'name=verifier' + bytes(1), payload, payload] image = bytearray(64 + 5 * 64) ident = bytes([127]) + b'ELF' + bytes([2, 1, 1]) + bytes(9) struct.pack_into('<16sHHIQQQIHHHHHH', image, 0, ident, 1, 62, 1, 0, 0, 64, 0, 64, 0, 0, 64, 5, 1) for index, (name, data) in enumerate(zip(names, sections), 1): image.extend(bytes((-len(image)) % 8)) offset = len(image) image.extend(data) flags = 0x800 if compressed and name in names[2:] else 0 struct.pack_into('<IIQQQQIIQQ', image, 64 + index * 64, indices[name], 3 if index == 1 else 1, flags, 0, offset, len(data), 0, 0, 8, 0) target = work / ('compressed.ko' if compressed else 'plain.ko') target.write_bytes(image) result = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-kernel-module', str(target), '--json'], cwd=repo, env=env, capture_output=True, text=True, timeout=60, check=True) assert not result.stderr, result.stderr report = json.loads(result.stdout)['normalized_result'] print(target.name, 'exit=0', json.dumps({'this_module': report['this_module'], 'init_text': report['init_text']})) schema = subprocess.run(['node', 'scripts/rea.mjs', 'inspect-kernel-module', '--schema', '--json'], cwd=repo, env=env, capture_output=True, text=True, timeout=60, check=True) assert not schema.stderr, schema.stderr print('--schema exit=0 keys=' + ','.join(json.loads(schema.stdout))) PYTHON ``` 3. Compare the printed `this_module.status` values with the guide's three-value list. This exact recipe was executed once. Two earlier independent compressed inspections produced the same fourth status; both uncompressed inspections completed with `present`. ### Expected behavior The guide should include `unsupported` in the possible `.gnu.linkonce.this_module` statuses and explain that compressed storage selects that result. Its status list should agree with the implemented output contract. The result can retain the existing stored range and `layout: "unknown"` semantics. ### Impact and scope P3 documentation error: the prose presents an incomplete set of possible values. The inspected metadata remains available, and the output contract in source already allows the returned status. No failed downstream consumer, incorrect decoding, data loss, or security boundary crossing was established. Scope checked: synthetic ELF64 little-endian x86-64 ET_REL objects with complete section names and file-backed metadata, on one host. Actual distribution modules, MCP execution, other ELF classes or byte orders, and the prevalence of compressed `.gnu.linkonce.this_module` sections were not tested. Callers can consult the source output contract and handle the returned `unsupported` value; that is an available workaround for the incomplete prose. ### REA version Exact `node scripts/rea.mjs --version` output: ```text 6.8.0 ``` Source baseline: `65f1c447344a423fa2785847dbcea1e9a9e2454a`. ### Runtime and platform Node.js v24.22.1; Darwin arm64 (macOS). Python 3 generated the fixtures using `struct` and `zlib`. The inspection used REA's built-in reader without an external analysis provider. ### Target and artifact context A 603-byte compressed fixture and a 553-byte uncompressed control. Both contain `.shstrtab`, `.modinfo`, `.gnu.linkonce.this_module`, and `.init.text`. The compressed variant gives the last two sections `SHF_COMPRESSED` and a valid 24-byte ELF64 compression header followed by zlib data. The original payload is 33 bytes and each compressed section occupies 59 stored bytes. The recipe checks that decompression recovers the original bytes. Neither file is loaded as a kernel module. ### Interface CLI ### Earliest failure stage Unknown ### Relevant output or evidence The executed recipe printed: ```text plain.ko exit=0 {"this_module": {"status": "present", "location": {"offset": 480, "bytes": 33}, "layout": "unknown"}, "init_text": {"status": "present", "location": {"offset": 520, "bytes": 33}, "bytes": 33, "compressed": false}} compressed.ko exit=0 {"this_module": {"status": "unsupported", "location": {"offset": 480, "bytes": 59}, "layout": "unknown"}, "init_text": {"status": "present", "location": {"offset": 544, "bytes": 59}, "bytes": 59, "compressed": true}} --schema exit=0 keys=args,options ``` All listed commands exited 0 with empty stderr. The inspected results had `applicability: "applicable"` and decoded the normal `license=GPL` and `name=verifier` entries. Earlier complete-output inspection showed a generic limitation about the unknown `struct module` layout and a compression limitation for `.init.text`; it contained no compression explanation specifically for `this_module`. Baseline source locators: - Expected guide: `docs/kernel-modules.md:53-55` enumerates present, absent, or unknown and says the layout stays unknown. Lines 59-60 separately explain compressed init/exit text storage sizes. - Actual output contract: `src/domain/native/kernelModule.ts:143-148` includes `unsupported` in `thisModuleSchema.status`. `src/contracts/native/binaryDiagnosticsToolContracts.ts:145-146` binds the containing schema as the tool's output contract. - Supported public route: `src/cli/binaryDiagnosticsCommands.ts:128-160` invokes kernel-module inspection; `src/application/binaryDiagnostics/KernelModuleService.ts:60-64` validates the provider report using the output schema. - Returning branch: `src/native/elf/KernelModuleParser.ts:360-375` selects `unsupported` for a file-backed compressed section and preserves its location. The selected failure stage is Unknown because this finding concerns inaccurate guide text; the inspected runtime commands succeed. ### Analysis or suspected cause Source-derived conclusion: the parser and source output schema agree on the fourth status, while the guide's explicit list omits it. The evidence supports updating the documented enumeration. No runtime contract change or additional output field is needed to establish this documentation defect. The absent compression explanation in the result is context rather than a separately established bug. ### Additional context Duplicate searches covered open and closed issues, issue comments, and open, merged, and closed PRs using kernel-module, this_module, compressed, unsupported, and guide/status queries. [Issue #7324](https://github.com/morluto/rea/issues/7324) and [PR #7491](https://github.com/morluto/rea/pull/7491) address the signature magic's byte count; the PR changes only that count. [Issue #7587](https://github.com/morluto/rea/issues/7587) concerns verbatim CLI input-path provenance, and [issue #7437](https://github.com/morluto/rea/issues/7437) concerns MCP input-path schema validation. Closed [issue #6869](https://github.com/morluto/rea/issues/6869) concerns compressed note decoding in a different inspection command. The merged feature [PR #7010](https://github.com/morluto/rea/pull/7010), including its discussion and inline comments, does not report this missing guide status. The documentation-index duplicate in [issue #7405](https://github.com/morluto/rea/issues/7405) and [PR #7476](https://github.com/morluto/rea/pull/7476) is also distinct. No report with the same violated contract and closure signal was found. No last known good guide revision was established. Verification changed no checkout files and published nothing. Proposed restoration check: the guide includes the compressed-section outcome while the same fixture continues to return `unsupported` and its uncompressed control returns `present`. ## Triage assessment - Impact level: P3 - Assessment status: supported - Impact basis: The guide's enumerated values disagree with successful CLI output, but the source output contract already accepts the value and no consequential client failure was observed. - Workaround status: available - Workaround basis: The source output schema explicitly lists unsupported, and the executed CLI returns it with the stored section range; callers can use those facts to handle the value without a runtime change.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4592
No description provided.