Skip to content

Fix heap overflow in time formatting and disable ante/post exec by de…#1260

Closed
armin5872 wants to merge 1 commit into
mltframework:masterfrom
armin5872:security-fixes
Closed

Fix heap overflow in time formatting and disable ante/post exec by de…#1260
armin5872 wants to merge 1 commit into
mltframework:masterfrom
armin5872:security-fixes

Conversation

@armin5872

@armin5872 armin5872 commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

MLT security fixes — 2 patches

Apply from the repo root (where CMakeLists.txt lives):

git apply 0001-fix-heap-overflow-mlt_property_get_time.patch
git apply 0002-disable-ante-post-system-exec-by-default.patch

(Or patch -p1 < 0001-...patch if you're not using git.)

0001 — Heap buffer overflow in mlt_property_get_time

File: src/framework/mlt_property.c

mlt_property_get_time() allocated a fixed 32-byte buffer and filled it via
sprintf() with hours/minutes/seconds computed from an attacker-controlled
frame count and frame rate (both reachable from an untrusted .mlt XML
project file — e.g. a producer's in/out/length property, or an
animation keyframe, combined with a profile's frame rate). %02d is a
minimum field width, not a maximum, so a large hours value (achievable
with a huge frame count and/or a very small fps) produces a string far
longer than 32 bytes, overflowing the heap allocation.

Fix:

  • Replaced sprintf with snprintf bounded by a new MLT_TIME_STRING_MAX
    (64 bytes) constant, so the function can never write past the buffer
    regardless of input.
  • Increased the allocation at the call site from malloc(32) to
    malloc(MLT_TIME_STRING_MAX).
  • Clamped the intermediate hours/mins/secs values to sane ranges
    before formatting, so degenerate/extreme inputs (e.g. fps <= 0, or a
    frame count near INT_MAX/INT_MIN with a very small fps) produce a
    well-formed, bounded string instead of nonsensical or wildly long output.
  • Forced double arithmetic in a couple of intermediate computations
    (hours * 3600 * fps, mins * 60 * fps) that were previously done in
    int, which could overflow (undefined behavior) for large clamped
    hours/mins values.
    Verified with ASan + UBSan against a battery of extreme/adversarial inputs
    (INT_MAX/INT_MIN frame counts, fps of 0, negative, near-zero, and
    absurdly large) — no overflow or UB is triggered post-fix, and normal
    inputs (e.g. NTSC 29.97fps) produce identical output to before.

0002 — Arbitrary command execution via ante/post consumer properties

File: src/framework/mlt_consumer.c

The consumer's ante and post properties are passed directly to
system() when a consumer starts/stops. These properties can be set
purely by loading an .mlt XML file — a <consumer> element's attributes
(or child <property> tags) flow straight through
mlt_properties_inherit() into the real consumer's property set with no
sanitization or opt-in. This means simply opening/rendering an untrusted
project file (e.g. melt evil.mlt) can execute arbitrary shell commands
with no other action by the user.

Fix: Execution of ante/post is now disabled by default. If either
property is present but execution isn't explicitly allowed, MLT logs a
warning naming the blocked command and skips it instead of running it. An
application that intentionally wants this feature (e.g. for its own
trusted, locally-authored scripts/projects) can opt back in by setting the
environment variable:

MLT_CONSUMER_ANTE_POST_ALLOWED=1

This preserves the documented feature for users who knowingly want it,
while closing the "open a file → arbitrary code execution" path for the
common case of loading project files from an untrusted or unknown source.

Notes / things to double check before merging

  • The MLT_CONSUMER_ANTE_POST_ALLOWED opt-in is a minimal stopgap. If you'd
    prefer a different mechanism (e.g. a mlt_consumer API flag the
    application sets explicitly, rather than an environment variable),
    that's a reasonable alternative design — happy to rework it.
  • These two patches address the most severe issues I found in the time
    available. I did not do an exhaustive audit of the rest of the codebase
    (avformat/FFmpeg integration, Qt/GDK image loaders, the melt CLI parser,
    subtitle parsers, etc.) — see the earlier conversation for what was and
    wasn't covered.

@armin5872 armin5872 marked this pull request as draft June 24, 2026 13:30
@armin5872 armin5872 marked this pull request as ready for review June 24, 2026 13:30
@armin5872 armin5872 marked this pull request as draft June 24, 2026 13:30
@armin5872 armin5872 marked this pull request as ready for review June 24, 2026 13:33

static int ante_post_commands_allowed()
{
const char *e = getenv("MLT_CONSUMER_ANTE_POST_ALLOWED");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be documented in mlt_consumer.h using \envvar. See the other headers in the same directory for example. Likewise, these "anti" and "post" properties were intentionally undocumented because they are legacy cruft I did not really want people to use. Briefly document them as well using \properties \em with a suffix of " (DEPRECATED)".

Comment on lines +570 to +571
mlt_log(MLT_CONSUMER_SERVICE(self),
MLT_LOG_WARNING,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Prefer the logging macros mlt_log_... instead of this form. Not a required change, but if you can get around to it.

* it cannot be exceeded even for pathological/attacker-supplied frame counts
* or frame rates (see time_smpte_from_frames() and time_clock_from_frames()).
*/
#define MLT_TIME_STRING_MAX 64

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

64 is too big. Simply look at the sprintf format args. Leave it at the original 32.

Comment on lines +1026 to +1030
double hours_d = frames / (fps * 3600);
if (hours_d > 999999.0)
hours_d = 999999.0;
else if (hours_d < -999999.0)
hours_d = -999999.0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

mlt_types.h has MIN, MAX, and CLAMP macros that should be used in multiple places in your changes to improve readability.

@@ -0,0 +1,171 @@
--- a/src/framework/mlt_property.c

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do not include these patch files.

@ddennedy

Copy link
Copy Markdown
Member

Because I did not like your AI's style, I implemented the changes I requested in #1261 to supersede this PR.

@ddennedy ddennedy closed this Jun 24, 2026
@ddennedy ddennedy added this to the v7.40.0 milestone Jun 24, 2026
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