Skip to content

config_format: cf_yaml: fix Windows crash when libyaml is loaded as DLL - #11401

Closed
bp-cheng wants to merge 1 commit into
fluent:masterfrom
bp-cheng:patch-2
Closed

config_format: cf_yaml: fix Windows crash when libyaml is loaded as DLL#11401
bp-cheng wants to merge 1 commit into
fluent:masterfrom
bp-cheng:patch-2

Conversation

@bp-cheng

@bp-cheng bp-cheng commented Jan 27, 2026

Copy link
Copy Markdown
Contributor

On Windows, passing FILE* across DLL boundaries can cause crashes due to different C runtime libraries having incompatible internal FILE structures.

When libyaml is built as a dynamic library (DLL), using yaml_parser_set_input_file() causes the FILE* pointer to cross the DLL boundary, leading to memory corruption or crashes.

This fix reads the YAML config file into a memory buffer and uses yaml_parser_set_input_string() instead of yaml_parser_set_input_file(), avoiding the cross-DLL FILE* issue.

Changes:

  • Open file in binary mode ("rb") for consistent behavior
  • Read entire file content into a buffer using fseek/ftell/fread
  • Use yaml_parser_set_input_string() with the buffer
  • Properly free the buffer after parsing

Signed-off-by: bpcheng@hotmail.com

Summary by CodeRabbit

  • Refactor
    • Restructured internal YAML configuration file reading to utilize in-memory buffering instead of direct file streaming, improving error handling consistency across configuration loads and includes with no impact to user-facing functionality.

✏️ Tip: You can customize this high-level summary in your review settings.

On Windows, passing FILE* across DLL boundaries can cause crashes
due to different C runtime libraries having incompatible internal
FILE structures.

When libyaml is built as a dynamic library (DLL), using
yaml_parser_set_input_file() causes the FILE* pointer to cross
the DLL boundary, leading to memory corruption or crashes.

This fix reads the YAML config file into a memory buffer and uses
yaml_parser_set_input_string() instead of yaml_parser_set_input_file(),
avoiding the cross-DLL FILE* issue.

Changes:
 - Open file in binary mode ("rb") for consistent behavior
 - Read entire file content into a buffer using fseek/ftell/fread
 - Use yaml_parser_set_input_string() with the buffer
 - Properly free the buffer after parsing

Signed-off-by:  bpcheng@hotmail.com


Signed-off-by: BP Cheng <bp_cheng@hotmail.com>
@coderabbitai

coderabbitai Bot commented Jan 27, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The PR modifies YAML file reading for include/config files by switching from file-based input to in-memory buffering. Files are read entirely into allocated buffers in binary mode, then parsed via string input to the YAML parser, with proper error handling and cleanup.

Changes

Cohort / File(s) Summary
YAML file reading refactor
src/config_format/flb_cf_yaml.c
Switches from file-based to in-memory buffer approach: changes fopen mode to binary ("rb"), reads entire file into allocated buffer, uses yaml_parser_set_input_string instead of yaml_parser_set_input_file, adds file size validation and buffer allocation error handling, ensures buffer cleanup on both success and error paths, applies changes consistently across initial read and recursive include paths

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

Suggested labels

backport to v4.0.x, backport to v4.2.x

Suggested reviewers

  • edsiper
  • fujimotos

Poem

🐰 Files now leap into buffers so bright,
Binary mode holds content just right,
YAML parser grins at each string it consumes,
While cleanup ensures no memory blooms! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately describes the main change: fixing a Windows crash caused by passing FILE* across DLL boundaries when libyaml is loaded as a DLL, which is the primary issue addressed in the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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 and usage tips.

@bp-cheng bp-cheng closed this Jan 27, 2026
@bp-cheng
bp-cheng deleted the patch-2 branch January 27, 2026 04:56

@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: 7b10b9ea31

ℹ️ 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 on lines +2962 to +2966
/* Read file content */
bytes_read = fread(file_buffer, 1, file_size, fh);
fclose(fh);
fh = NULL; /* Mark as closed */
file_buffer[bytes_read] = '\0';

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 Treat short reads as errors before parsing

The new buffered read path never checks whether fread consumed the full file_size or whether ferror() was set. If an I/O error or concurrent truncation yields a short read, the parser will consume a truncated buffer and may succeed silently, leaving Fluent Bit running with an incomplete configuration. Please validate bytes_read == file_size (or check ferror) and fail the parse on short reads to preserve the prior behavior of surfacing read errors.

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

🤖 Fix all issues with AI agents
In `@src/config_format/flb_cf_yaml.c`:
- Around line 2934-2967: After fread(file_buffer, 1, file_size, fh) you must
verify bytes_read == file_size and handle incomplete reads: if bytes_read !=
file_size or ferror(fh) is set, log an error with flb_error including cfg_file
and the bytes_read/file_size values, free file_buffer, close fh if not already
closed, destroy include_dir and include_file, and return -1 to avoid parsing a
truncated config; use the existing symbols file_buffer, bytes_read, file_size,
fread, flb_error, fclose, flb_sds_destroy (and flb_free or equivalent) to
implement the check and cleanup.

Comment on lines +2934 to +2967
{
long file_size;
unsigned char *file_buffer = NULL;
size_t bytes_read;

/* Get file size */
fseek(fh, 0, SEEK_END);
file_size = ftell(fh);
fseek(fh, 0, SEEK_SET);

if (file_size < 0) {
flb_error("[config] could not determine file size for %s", cfg_file);
fclose(fh);
flb_sds_destroy(include_dir);
flb_sds_destroy(include_file);
return -1;
}

/* Allocate buffer */
file_buffer = flb_malloc(file_size + 1);
if (!file_buffer) {
flb_error("[config] could not allocate memory for config file %s", cfg_file);
fclose(fh);
flb_sds_destroy(include_dir);
flb_sds_destroy(include_file);
return -1;
}

/* Read file content */
bytes_read = fread(file_buffer, 1, file_size, fh);
fclose(fh);
fh = NULL; /* Mark as closed */
file_buffer[bytes_read] = '\0';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error checking after fread() to detect incomplete reads.

The code reads the file content but doesn't verify that the expected number of bytes were actually read. If fread() encounters an I/O error or EOF before reading file_size bytes, bytes_read will be less than expected, but the code continues silently. This could result in parsing an incomplete configuration file with cryptic YAML errors.

🔍 Proposed fix to add fread validation
         /* Read file content */
         bytes_read = fread(file_buffer, 1, file_size, fh);
+        if (bytes_read != (size_t)file_size) {
+            flb_error("[config] failed to read complete file %s: expected %ld bytes, got %zu bytes",
+                      cfg_file, file_size, bytes_read);
+            if (ferror(fh)) {
+                flb_error("[config] I/O error occurred while reading %s", cfg_file);
+            }
+            flb_free(file_buffer);
+            fclose(fh);
+            flb_sds_destroy(include_dir);
+            flb_sds_destroy(include_file);
+            return -1;
+        }
         fclose(fh);
         fh = NULL;  /* Mark as closed */
         file_buffer[bytes_read] = '\0';
🤖 Prompt for AI Agents
In `@src/config_format/flb_cf_yaml.c` around lines 2934 - 2967, After
fread(file_buffer, 1, file_size, fh) you must verify bytes_read == file_size and
handle incomplete reads: if bytes_read != file_size or ferror(fh) is set, log an
error with flb_error including cfg_file and the bytes_read/file_size values,
free file_buffer, close fh if not already closed, destroy include_dir and
include_file, and return -1 to avoid parsing a truncated config; use the
existing symbols file_buffer, bytes_read, file_size, fread, flb_error, fclose,
flb_sds_destroy (and flb_free or equivalent) to implement the check and cleanup.

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