Skip to content

out_es, out_opensearch: fix compose_index_header stack overflow - #12127

Open
saddamr3e wants to merge 1 commit into
fluent:masterfrom
saddamr3e:es-compose-index-header-overflow
Open

out_es, out_opensearch: fix compose_index_header stack overflow#12127
saddamr3e wants to merge 1 commit into
fluent:masterfrom
saddamr3e:es-compose-index-header-overflow

Conversation

@saddamr3e

@saddamr3e saddamr3e commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

compose_index_header builds the Logstash index inside a 256-byte on-stack buffer (logstash_index). When logstash_prefix_key is unset it takes the write offset from flb_sds_len(ctx->logstash_prefix), the full prefix length, but the prefix was already strncpy-truncated into that 256-byte buffer a few lines earlier. A logstash_prefix of 256 bytes or more pushes the offset past the buffer, logstash_index_size - len underflows to a large size_t, and the snprintf and strftime that follow write out of bounds. The per-record logstash_prefix_key path already caps its copy at 128 bytes, so only the config-prefix branch is affected. out_opensearch carries the same code and the same bug. Taking the offset from strlen(logstash_index), the length actually present in the buffer, keeps the write in bounds and leaves the result unchanged for prefixes that fit.


Testing

  • Example configuration file for the change
  • Debug log output from testing the change
  • Attached unit test exercising compose_index_header with an oversized logstash_prefix
[INPUT]
    name   dummy
    tag    test

[OUTPUT]
    name             es
    match            *
    logstash_format  on
    logstash_prefix  aaaaaaaa...   # 256 or more characters

Added tests/runtime/out_elasticsearch.c:logstash_format_long_prefix and tests/runtime/out_opensearch.c:logstash_format_long_prefix, each configuring logstash_format with a 300-byte prefix. Built with -DSANITIZE_ADDRESS=On, both abort on the unpatched tree and pass after the change (the index becomes the safely truncated prefix). ASan on the unpatched tree:

==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x...
WRITE of size 1 at 0x... thread T1
    #6 compose_index_header es.c:271
SUMMARY: AddressSanitizer: stack-buffer-overflow in __sfvwrite
  • [N/A] Run local packaging test showing all targets build.
  • [N/A] Set ok-package-test label to test for all targets.

Documentation

  • [N/A] Documentation required for this feature

Backporting

  • Backport to latest stable release.

Fluent Bit is licensed under Apache 2.0, by submitting this pull request I understand that this code will be released under the terms of that license.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Logstash-formatted index generation when the configured prefix exceeds the available index buffer.
    • Ensured date and remaining index components are appended at the correct position.
  • Tests

    • Added coverage verifying that oversized prefixes are safely truncated and correctly included in Elasticsearch and OpenSearch output.

Signed-off-by: Saddam <saddamr3e@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The Elasticsearch and OpenSearch outputs now calculate Logstash index append offsets from the copied index string. Runtime tests cover truncation of overlong logstash_prefix values in both plugins.

Changes

Logstash index formatting

Layer / File(s) Summary
Index append offset correction
plugins/out_es/es.c, plugins/out_opensearch/opensearch.c
Logstash index composition uses strlen(logstash_index) instead of the configured prefix length when determining the append position.
Long-prefix runtime coverage
tests/runtime/out_elasticsearch.c, tests/runtime/out_opensearch.c
New runtime tests construct 255-byte expected prefixes, validate formatted output for 300-byte prefixes, and register both tests in their test lists.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: cosmo0920

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main fix in compose_index_header for the Elasticsearch and OpenSearch outputs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 78205fec2f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread plugins/out_es/es.c
p = logstash_index + es_index_custom_len;
} else {
p = logstash_index + flb_sds_len(ctx->logstash_prefix);
p = logstash_index + strlen(logstash_index);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reuse the original prefix length on fallback

When logstash_prefix_separator is configured so the first compose_index_header() call fails, elasticsearch_format() retries with "-" on the same logstash_index buffer. This new strlen(logstash_index) then starts the retry after the partially written failed separator/date instead of after the static prefix, so the fallback can leave an index like prefix<oversized-separator> with no date rather than rebuilding prefix-YYYY...; the OpenSearch copy has the same issue. Please keep the truncated prefix length before mutating the buffer, or reset the buffer before retrying.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugins/out_es/es.c`:
- Around line 267-269: Replace the dynamic strlen-based offset with the bounded
flb_sds_len(ctx->logstash_prefix) calculation in compose_index_header for
plugins/out_es/es.c lines 267-269 and plugins/out_opensearch/opensearch.c lines
257-259, preserving the prefix-based append position across retries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75336672-43da-4945-a2d7-941637a193c4

📥 Commits

Reviewing files that changed from the base of the PR and between fd5ea1f and 78205fe.

📒 Files selected for processing (4)
  • plugins/out_es/es.c
  • plugins/out_opensearch/opensearch.c
  • tests/runtime/out_elasticsearch.c
  • tests/runtime/out_opensearch.c

Comment thread plugins/out_es/es.c
Comment on lines 267 to 269
} else {
p = logstash_index + flb_sds_len(ctx->logstash_prefix);
p = logstash_index + strlen(logstash_index);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Avoid strlen to prevent state corruption and undefined behavior on retry.

Both Elasticsearch and OpenSearch retry compose_index_header with a default separator if the first attempt fails. Using strlen(logstash_index) calculates the append offset dynamically based on the buffer's current contents. If the first attempt fails (e.g., during strftime), snprintf has already written the custom separator to the buffer. On retry, strlen will include this failed write, causing the new separator to append rather than overwrite. Furthermore, if strftime fails, the array contents are undefined per POSIX and may not be null-terminated, potentially causing strlen to read out of bounds.

To safely fix the overflow without introducing this retry bug, calculate the bounded prefix length directly from ctx->logstash_prefix.

  • plugins/out_es/es.c#L267-L269: Replace the strlen calculation with a bounded flb_sds_len(ctx->logstash_prefix).
  • plugins/out_opensearch/opensearch.c#L257-L259: Replace the strlen calculation with a bounded flb_sds_len(ctx->logstash_prefix).
🐛 Proposed fix for both files
-        p = logstash_index + strlen(logstash_index);
+        size_t default_len = flb_sds_len(ctx->logstash_prefix);
+        if (default_len > logstash_index_size - 1) {
+            default_len = logstash_index_size - 1;
+        }
+        p = logstash_index + default_len;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else {
p = logstash_index + flb_sds_len(ctx->logstash_prefix);
p = logstash_index + strlen(logstash_index);
}
} else {
size_t default_len = flb_sds_len(ctx->logstash_prefix);
if (default_len > logstash_index_size - 1) {
default_len = logstash_index_size - 1;
}
p = logstash_index + default_len;
}
📍 Affects 2 files
  • plugins/out_es/es.c#L267-L269 (this comment)
  • plugins/out_opensearch/opensearch.c#L257-L259
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugins/out_es/es.c` around lines 267 - 269, Replace the dynamic strlen-based
offset with the bounded flb_sds_len(ctx->logstash_prefix) calculation in
compose_index_header for plugins/out_es/es.c lines 267-269 and
plugins/out_opensearch/opensearch.c lines 257-259, preserving the prefix-based
append position across retries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant