fix: sanitize mpv event channel strings for valid UTF-8

This commit is contained in:
edde746
2026-03-04 14:59:11 +01:00
parent 551bd83ffe
commit 4121d568c8
9 changed files with 190 additions and 24 deletions
+11
View File
@@ -50,6 +50,17 @@ endfunction()
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})
# Fetch simdutf for SIMD-accelerated UTF-8 validation.
# mpv strings are not guaranteed to be valid UTF-8; invalid bytes sent through
# Flutter's StandardMessageCodec cause FormatException crashes.
include(FetchContent)
FetchContent_Declare(
simdutf
URL https://github.com/simdutf/simdutf/releases/download/v6.4.2/singleheader.zip
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(simdutf)
# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+8
View File
@@ -28,10 +28,18 @@ pkg_check_modules(MPV REQUIRED IMPORTED_TARGET mpv)
# Find epoxy (OpenGL loader).
pkg_check_modules(EPOXY REQUIRED IMPORTED_TARGET epoxy)
# Build simdutf as a static library from the single-header amalgamation.
add_library(simdutf STATIC "${simdutf_SOURCE_DIR}/simdutf.cpp")
target_include_directories(simdutf PUBLIC "${simdutf_SOURCE_DIR}")
target_compile_features(simdutf PUBLIC cxx_std_17)
# Suppress warnings in third-party code
target_compile_options(simdutf PRIVATE -w)
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::MPV)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::EPOXY)
target_link_libraries(${BINARY_NAME} PRIVATE simdutf)
target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+45 -4
View File
@@ -13,6 +13,47 @@
#include <clocale>
#include <cstring>
#include <string>
#include <simdutf.h>
// Sanitize a C string that may contain invalid UTF-8 sequences.
// Uses simdutf for SIMD-accelerated validation (fast path for valid strings),
// then falls back to iterative replacement with U+FFFD on the rare invalid case.
// mpv does not guarantee UTF-8 for log messages, error strings, or
// system-encoded paths — sending these unsanitized through Flutter's
// StandardMessageCodec causes FormatException crashes.
static std::string SanitizeUtf8(const char* input) {
if (!input) return std::string();
size_t len = strlen(input);
if (len == 0) return std::string();
// Fast path: SIMD-accelerated validation — almost all strings pass this
if (simdutf::validate_utf8(input, len)) {
return std::string(input, len);
}
// Slow path: find each invalid position, copy valid prefix, insert U+FFFD,
// skip the bad byte, and repeat.
std::string result;
result.reserve(len);
size_t pos = 0;
while (pos < len) {
auto r = simdutf::validate_utf8_with_errors(input + pos, len - pos);
// Copy the valid prefix up to the error
if (r.count > 0) {
result.append(input + pos, r.count);
}
pos += r.count;
if (r.error == simdutf::error_code::SUCCESS) {
break; // remaining tail is valid
}
// Replace the invalid byte with U+FFFD and skip it
result.append("\xEF\xBF\xBD");
pos++;
}
return result;
}
// Flutter on Linux uses EGL (OpenGL ES) for both X11 and Wayland.
static void* get_opengl_proc_address(void* ctx, const char* name) {
@@ -483,11 +524,11 @@ void MpvPlayer::HandleMpvEvent(mpv_event* event) {
FlValue* data = fl_value_new_map();
fl_value_set_string_take(data, "prefix",
fl_value_new_string(msg->prefix ? msg->prefix : ""));
fl_value_new_string(SanitizeUtf8(msg->prefix).c_str()));
fl_value_set_string_take(data, "level",
fl_value_new_string(msg->level ? msg->level : ""));
fl_value_new_string(SanitizeUtf8(msg->level).c_str()));
fl_value_set_string_take(data, "text",
fl_value_new_string(msg->text ? msg->text : ""));
fl_value_new_string(SanitizeUtf8(msg->text).c_str()));
SendEvent("log-message", data);
fl_value_unref(data);
break;
@@ -559,7 +600,7 @@ FlValue* MpvPlayer::NodeToFlValue(mpv_node* node) {
switch (node->format) {
case MPV_FORMAT_STRING:
return fl_value_new_string(node->u.string ? node->u.string : "");
return fl_value_new_string(SanitizeUtf8(node->u.string).c_str());
case MPV_FORMAT_FLAG:
return fl_value_new_bool(node->u.flag != 0);
case MPV_FORMAT_INT64: