Skip to content

Conversation

@dyupina
Copy link

@dyupina dyupina commented Feb 2, 2026

In pcmk__output_new() -> pcmk__bare_output_new(), when the condition if ((*out)->init(*out) == false) is met, pcmk__output_free(*out) is called and the function returns with rc = ENOMEM.
The *_init() functions return false when memory allocation via calloc() fails - an unlikely but possible scenario.

Since rc != pcmk_rc_ok, execution jumps to the done label after pcmk__output_new() returns.

The codebase contains numerous functions that follow the same cleanup pattern: they check if (out != NULL) at the done label before invoking out->finish() and pcmk__output_free(out). Because *out is freed but not set to NULL on initialization failure, this check evaluates to true.
This leads to:

  • Use-after-free: dereferencing the dangling pointer when calling out->finish(...);
  • Double-free: attempting to free the same memory again via pcmk__output_free(out).

To resolve this, set *out = NULL immediately after calling pcmk__output_free(*out) within pcmk__bare_output_new(). This ensures that if (out != NULL) check at the done label is false, thereby preventing both the use-after-free and the double-free.

In pcmk__output_new() -> pcmk__bare_output_new(), when the condition
if ((*out)->init(*out) == false) is met, pcmk__output_free(*out) is
called and the function returns with rc = ENOMEM.
The *_init() functions return false when memory allocation via calloc()
fails - an unlikely but possible scenario.

Since rc != pcmk_rc_ok, execution jumps to the done label after
pcmk__output_new() returns.

The codebase contains numerous functions that follow the same
cleanup pattern: they check if (out != NULL) at the done label
before invoking out->finish() and pcmk__output_free(out).
Because *out is freed but not set to NULL on initialization failure,
this check evaluates to true.
This leads to:
- Use-after-free: dereferencing the dangling pointer when
calling out->finish(...);
- Double-free: attempting to free the same memory again
via pcmk__output_free(out).

To resolve this, set *out = NULL immediately after calling
pcmk__output_free(*out) within pcmk__bare_output_new().
This ensures that if (out != NULL) check at the done label is false,
thereby preventing both the use-after-free and the double-free.

Signed-off-by: Alexandra Diupina <[email protected]>
@knet-jenkins
Copy link

knet-jenkins bot commented Feb 2, 2026

Can one of the project admins check and authorise this run please: https://ci.kronosnet.org/job/pacemaker/job/pacemaker-pipeline/job/PR-4044/1/input


if ((*out)->init(*out) == false) {
pcmk__output_free(*out);
*out = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the patch.

Instead, please use g_clear_pointer(out, pcmk__output_free) here. This is a relatively new thing we're starting to do, which is why you might not have run across it before. You'll also need to #include <glib.h> and update the copyright date to run through 2026. Thanks!

Copy link
Author

@dyupina dyupina Feb 3, 2026

Choose a reason for hiding this comment

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

Should I also replace the highlighted lines from the code below with g_clear_pointer(out, pcmk__output_free) ?

    if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
        (*out)->dest = stdout;
    } else {
        (*out)->dest = fopen(filename, "w");
        if ((*out)->dest == NULL) {
            pcmk__output_free(*out); // <<<<<
            *out = NULL; // <<<<<
            return errno;
        }
    }

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, exactly. Those two lines go away and g_clear_pointer gets used instead.

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.

2 participants