Skip to content

Fixed out-of-bounds read in module protocol when a '%' line has no '='#6178

Open
aizu-m wants to merge 1 commit into
cfengine:masterfrom
aizu-m:module-protocol-percent-no-equals
Open

Fixed out-of-bounds read in module protocol when a '%' line has no '='#6178
aizu-m wants to merge 1 commit into
cfengine:masterfrom
aizu-m:module-protocol-percent-no-equals

Conversation

@aizu-m

@aizu-m aizu-m commented Jun 13, 2026

Copy link
Copy Markdown

AddressSanitizer, parsing a "%" module-protocol line with no "=":

ERROR: AddressSanitizer: SEGV on a READ
    #0 strlen
    #1 BufferNewFrom buffer.c:68
    #2 ModuleProtocol evalfunction.c:10252

Came across this whilst reading the module protocol parser. The "%" case
builds the JSON payload with

BufferNewFrom(line + strlen(name) + 1 + 1,
              length - strlen(name) - 1 - 1);

which assumes a well-formed "%name=" line. But sscanf("%256[^=]=",
name) also returns with name set when the line carries no "=": it leaves
the whole tail in name. So strlen(name) == length - 1, the length argument
underflows to SIZE_MAX, and the data pointer steps one past the terminating
NUL. BufferAppend() then scans from there and walks off the heap line
buffer.

read_module_protocol() hands arbitrary file lines straight to
ModuleProtocol(), so a single line "%x" is enough to reproduce it.

The other directives ("=", "@", ...) already guard their input; the "%"
case did not. Only do the arithmetic once the "=" delimiter is present.

AddressSanitizer, parsing a "%" module-protocol line with no "=":

    ERROR: AddressSanitizer: SEGV on a READ
        #0 strlen
        cfengine#1 BufferNewFrom buffer.c:68
        cfengine#2 ModuleProtocol evalfunction.c:10252

Came across this whilst reading the module protocol parser. The "%" case
builds the JSON payload with

    BufferNewFrom(line + strlen(name) + 1 + 1,
                  length - strlen(name) - 1 - 1);

which assumes a well-formed "%name=<json>" line. But sscanf("%256[^=]=",
name) also returns with name set when the line carries no "=": it leaves
the whole tail in name. So strlen(name) == length - 1, the length argument
underflows to SIZE_MAX, and the data pointer steps one past the terminating
NUL. BufferAppend() then scans from there and walks off the heap line
buffer.

read_module_protocol() hands arbitrary file lines straight to
ModuleProtocol(), so a single line "%x" is enough to reproduce it.

The other directives ("=", "@", ...) already guard their input; the "%"
case did not. Only do the arithmetic once the "=" delimiter is present.

Changelog: Title
Signed-off-by: aizu-m <aizumusheer2@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant