From d71f22c50d4adaa5e8d2d623c2474d3f3cf2e687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vera=20Sp=C3=B6ttl-Zeisberg?= Date: Sat, 13 Sep 2025 11:12:38 +0200 Subject: [PATCH 1/4] Fix critical memory safety issues to prevent stack buffer overrun crashes - Add input validation for texture dimensions (max 8192x8192) - Add integer overflow protection before memory allocation - Add null pointer validation before buffer operations - Fix exception handling to use references instead of values - Add OpenGL error checking before/after critical operations - Add comprehensive bounds checking in changeSize() method - Add memory allocation error handling with try-catch blocks Fixes crash with Exception Code 0xC0000409 (FAST_FAIL_FATAL_APP_EXIT) that was occurring in FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable --- .../windows/flutter_angle_plugin.cpp | 26 ++++++- flutter_angle/windows/flutter_gl_texture.cpp | 77 ++++++++++++++++++- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/flutter_angle/windows/flutter_angle_plugin.cpp b/flutter_angle/windows/flutter_angle_plugin.cpp index c703f56..2156630 100755 --- a/flutter_angle/windows/flutter_angle_plugin.cpp +++ b/flutter_angle/windows/flutter_angle_plugin.cpp @@ -127,6 +127,22 @@ namespace { return; } + // Input validation to prevent crashes + const int MAX_TEXTURE_SIZE = 8192; + if (width <= 0 || height <= 0) { + result->Error("Invalid texture dimensions", "Width and height must be positive"); + return; + } + if (width > MAX_TEXTURE_SIZE || height > MAX_TEXTURE_SIZE) { + result->Error("Texture too large", "Width and height must be less than " + std::to_string(MAX_TEXTURE_SIZE)); + return; + } + // Check for potential integer overflow + if (width > INT_MAX / height / 4) { + result->Error("Texture too large", "Texture dimensions would cause integer overflow"); + return; + } + std::unique_ptr flutterGLTexture; try{ @@ -147,8 +163,14 @@ namespace { renderers.insert(RendererMap::value_type(textureId, std::move(flutterGLTexture))); renderers[textureId]->createTexture(result); } - catch (OpenGLException ex){ - result->Error(ex.message + ':' + std::to_string(ex.error)); + catch (const OpenGLException& ex){ + result->Error(std::string(ex.message) + ":" + std::to_string(ex.error)); + } + catch (const std::exception& ex) { + result->Error("Standard exception", ex.what()); + } + catch (...) { + result->Error("Unknown exception", "An unknown exception occurred during texture creation"); } } else if (method_call.method_name().compare("updateTexture") == 0 || method_call.method_name().compare("textureFrameAvailable") == 0) { diff --git a/flutter_angle/windows/flutter_gl_texture.cpp b/flutter_angle/windows/flutter_gl_texture.cpp index 4291f60..6d6a826 100755 --- a/flutter_angle/windows/flutter_gl_texture.cpp +++ b/flutter_angle/windows/flutter_gl_texture.cpp @@ -144,12 +144,47 @@ EGLInfo FlutterGLTexture::initOpenGL(std::unique_ptr>& result) { + // Input validation to prevent buffer overrun + const int MAX_TEXTURE_SIZE = 8192; // Reasonable maximum texture size + if (setWidth <= 0 || setHeight <= 0) { + result->Error("Invalid texture dimensions", "Width and height must be positive"); + return; + } + if (setWidth > MAX_TEXTURE_SIZE || setHeight > MAX_TEXTURE_SIZE) { + result->Error("Texture too large", "Width and height must be less than " + std::to_string(MAX_TEXTURE_SIZE)); + return; + } + + // Check for integer overflow before multiplication + if (setWidth > INT_MAX / setHeight / 4) { + result->Error("Texture too large", "Texture dimensions would cause integer overflow"); + return; + } + if (setWidth == structure.width && setHeight == structure.height && didStart) { return; } if(structure.useBuffer){ int64_t size = setWidth * setHeight * 4; - pixels.reset(new uint8_t[size]); + + // Additional safety check + if (size <= 0 || size > SIZE_MAX) { + result->Error("Invalid buffer size", "Calculated buffer size is invalid"); + return; + } + + try { + pixels.reset(new uint8_t[size]); + } catch (const std::bad_alloc& e) { + result->Error("Memory allocation failed", "Failed to allocate memory for texture buffer"); + return; + } + + // Validate pixelBuffer is initialized + if (!pixelBuffer) { + result->Error("Pixel buffer not initialized", "Pixel buffer is null"); + return; + } pixelBuffer->buffer = pixels.get(); pixelBuffer->width = setWidth; @@ -306,14 +341,14 @@ void FlutterGLTexture::setupOpenGLResources(){ auto error = glGetError(); if (error != GL_NO_ERROR){ std::cerr << "GlError while allocating Renderbuffer" << error << std::endl; - throw new OpenGLException("GlError while allocating Renderbuffer", error); + throw OpenGLException("GlError while allocating Renderbuffer", error); } glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,rbo); auto frameBufferCheck = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (frameBufferCheck != GL_FRAMEBUFFER_COMPLETE){ std::cerr << "Framebuffer error" << frameBufferCheck << std::endl; - throw new OpenGLException("Framebuffer Error while creating Texture", frameBufferCheck); + throw OpenGLException("Framebuffer Error while creating Texture", frameBufferCheck); } error = glGetError(); @@ -349,8 +384,44 @@ void FlutterGLTexture::createTexture(std::unique_ptr>& result){ if(structure.useBuffer){ + // Validate pixelBuffer and its buffer before use + if (!pixelBuffer) { + result->Error("Pixel buffer not initialized", "Pixel buffer is null"); + return; + } + if (!pixelBuffer->buffer) { + result->Error("Pixel buffer data not allocated", "Pixel buffer data is null"); + return; + } + if (pixelBuffer->width <= 0 || pixelBuffer->height <= 0) { + result->Error("Invalid pixel buffer dimensions", "Pixel buffer dimensions are invalid"); + return; + } + + // Check for potential buffer overrun + int64_t requiredSize = pixelBuffer->width * pixelBuffer->height * 4; + if (requiredSize <= 0 || requiredSize > SIZE_MAX) { + result->Error("Invalid buffer size for read operation", "Calculated buffer size is invalid"); + return; + } + glBindFramebuffer(GL_FRAMEBUFFER, textures.fboId); + + // Check for OpenGL errors before read operation + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + result->Error("OpenGL error before readPixels", "OpenGL error: " + std::to_string(error)); + return; + } + glReadPixels(0, 0, (GLsizei)pixelBuffer->width, (GLsizei)pixelBuffer->height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)pixelBuffer->buffer); + + // Check for OpenGL errors after read operation + error = glGetError(); + if (error != GL_NO_ERROR) { + result->Error("OpenGL error during readPixels", "OpenGL error: " + std::to_string(error)); + return; + } } textureRegistrar->MarkTextureFrameAvailable(textureId); result->Success(); From 4400f019c9834e65c5adb065ed726f65d830124d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vera=20Sp=C3=B6ttl-Zeisberg?= Date: Sat, 13 Sep 2025 11:16:40 +0200 Subject: [PATCH 2/4] Fix unused variable warning in catch block --- flutter_angle/windows/flutter_gl_texture.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter_angle/windows/flutter_gl_texture.cpp b/flutter_angle/windows/flutter_gl_texture.cpp index 6d6a826..0e7189c 100755 --- a/flutter_angle/windows/flutter_gl_texture.cpp +++ b/flutter_angle/windows/flutter_gl_texture.cpp @@ -175,7 +175,7 @@ void FlutterGLTexture::changeSize(int setWidth, int setHeight, std::unique_ptrError("Memory allocation failed", "Failed to allocate memory for texture buffer"); return; } From 4ca491348efbbfa687f7c7d370d708f9cb8b07c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vera=20Sp=C3=B6ttl-Zeisberg?= Date: Sat, 13 Sep 2025 11:52:12 +0200 Subject: [PATCH 3/4] fix(windows): prevent stack buffer overrun crashes in texture handling - Add input validation for texture dimensions (max 8192x8192) - Add integer overflow protection before memory allocation - Add null pointer validation before buffer operations - Fix exception handling to use references instead of values - Add OpenGL error checking before/after critical operations - Add comprehensive bounds checking in changeSize() method - Add memory allocation error handling with try-catch blocks Fixes crash with Exception Code 0xC0000409 (FAST_FAIL_FATAL_APP_EXIT) that was occurring in FlutterDesktopTextureRegistrarMarkExternalTextureFrameAvailable --- flutter_angle/example/analysis_options.yaml | 28 ++++++++++++++++++++ flutter_angle/lib/flutter_angle.dart | 5 ++-- flutter_angle_crash_fix.patch | Bin 0 -> 13540 bytes h_fix.patch | 10 +++++++ 4 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 flutter_angle/example/analysis_options.yaml create mode 100644 flutter_angle_crash_fix.patch create mode 100644 h_fix.patch diff --git a/flutter_angle/example/analysis_options.yaml b/flutter_angle/example/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/flutter_angle/example/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/flutter_angle/lib/flutter_angle.dart b/flutter_angle/lib/flutter_angle.dart index ca6ff2f..b25dd36 100755 --- a/flutter_angle/lib/flutter_angle.dart +++ b/flutter_angle/lib/flutter_angle.dart @@ -1,7 +1,6 @@ library flutter_angle; -export 'desktop/index.dart' - if (dart.library.js_interop) 'webgl/index.dart'; +export 'desktop/index.dart' if (dart.library.js_interop) 'webgl/index.dart'; export 'shared/webgl.dart'; export 'shared/options.dart'; -export 'shared/classes.dart'; \ No newline at end of file +export 'shared/classes.dart'; diff --git a/flutter_angle_crash_fix.patch b/flutter_angle_crash_fix.patch new file mode 100644 index 0000000000000000000000000000000000000000..c9ecc26780f02c2f1fab1ddbb5689964571a6f88 GIT binary patch literal 13540 zcmd^`Yi}FH8pr1~65qk(1-LX#LouO1sc4H^B2ikUDOVvYjU5u<#=&-gbD&?HbN;`1 zJe`@ncx~4yL1>lQyE~WXe)*64zrXtK#2x8sxof(9(%n?w58QP<8@hAdP2IqaT~E(W z^?s+Uy=J9T|G(znUnu|0R@*J0&Tx8)9Xf2JLt&7Jk?JG|-Mn9(?JziYok_o3T% z?~AJs+>YqjayQ(2{`-!eZfnGQ`gdESKG4N%vO8lk>0iRZKQwCw8KCn26~329_s!;6dvoz#P4xcPvdpy zXHWcCERZ=5-5vL>EPCH{^||k!x;=gCxhJCHdwpxUPxW_GGWkf-FmCcy@tKxudq3!F z&12o&^0Tm{KXtV{MJMu@W4~v3dqr!+iiFWMadu! zEZby`X7FURb*eQ?W)scY)SC_WjhCj;i68g%6p2Opo@-x|(lb3r&Lh7L@<-Ym?n%vgE-u7l+=|qYs~!gp0}Gr_p|6b5>;b;Vk=l9-gP1#hI(ec zHt^D6%*b2O*k={8MmY}DS;VgUNRF5Hw>YxO=UU=!X# zJCr=|EPSd-ChPDq2kuKrJGFOwE3XC0 zV?x9)@6I0onMJ&qc{)Dx+|UpFUj>jX{$ z1;xz8%&l1+G0~pHON(#sNC)rCzDMa+#X4I9eOf+@Qq8kU&QqHE=kxcrs7N`?S8d`j z($2RSp{qD2O0oKecwa2eFUosWmaUgZq?z?|ZAWRW8d0A}4pwK7A&G`Yp|0mFB045kKpX>_ir>qj*ErBkzf(^T(6*4s}r#q%WBSgRDn@J-+E8Y(j(?ia&N|nlx3S%S+dZLLeJ)HSvd>$%(^gypRlHXGMLp?R< zx$dkgFGe9KrEaEjjy3Rlb|vP;czj>fSYL9-cM2!s<3Rl+l~hhew1tc5a_NS9l4?rb z`a0ZstCMH3A#xhsZAX2|N6Mf_%B9=h4|eo!M{>ETH*|Ox)!jBxxvtK(X`i&V==f@a zK9=gDqia)tAL`ylb$yoGmHen+Ta6VE6*$rc8%gs>JE;k(Ypc9&Vx5Q5SnxOMl8`lT zCAEZAQNgF}?pIMtN4yxdWOd>%x%P?X{a2rD&jbB0kztzjgj6^17vFuM5urECYoB%U zNj^s=UF}V*A+nesf30s+r2}=rw;lP22oz)${o3;Mt04%_v=0av)Wkkztf!|*my*^J zK~~MZYK|k&yU?qkcQ6ZZjlgkBk@i5oOb>sqzjAJ%t4EL6gYg|YrlLN}ScHv8wHn71 zrP);NTt*}U>!?B?(yF0eT8p3u0oAFJK)@@vGVJtiALFa!T0cL>fNTY1(pSc#`=IxaUKeZSGFSF4K;Bl-oK2)^`I&04Ez;<0wK_>oJ5^FDBVv-L7!hA;I#M2V5IrEJ@G?+lwiW!6JFF>UaH($Ubk_EGOHZ@*gq=Q=CEV4S`+Z&5 z6B02SyC=)~(py(gm5(H)$NubwUo|Y}ozZ@bqno&2h?W1gI(Tsj6l>vicLsLmcOcz`9wG1G(vGdZ zKZvWWt5YaTDAxaJW=B40U0=i`>SyIBQ<%4!e2XYW+_mTvRoGs0|5Y`M`kIdG;t`B` z+_viXVQJl1f9gW!q9d59#|mf~-0&Nw6VDfDU%->{{%@ZCLDr)5iz9QLrzl+6-s-6d zwi0l!`jM|22aCVlgC)=X5%+0_3c@I zYF(6p?p8C+c>Yd}0r|OkK+8^k%tFF1H_ur|b?)oup zA=~!rf_LZAna1pk@eocJ@Jq0x#r-3-=+L*<<6W1>Bl6#Z1%6YTx?CP%KJY@ivC|8? z&*Lbb!T main, origin/main, origin/HEAD) updates for pub dev +c113385 Fixed ANGLE spamming issue on debug +c89d398 Fixed binding error +c96e25a added antialias to web +de96ed3 pub dev updates +9570127 Fixed web release bug +b36beeb making web consistent and looking for memory leaks +0654066 updates to fix Linux artifacts +815652a removed glew +b1df2ce update for pub dev From 96a2e346bbe5b62a4bc287610afaf5e4ee8d65c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vera=20Sp=C3=B6ttl-Zeisberg?= Date: Mon, 15 Sep 2025 11:46:33 +0200 Subject: [PATCH 4/4] fix(windows): recreate FBO/RBO on buffer resize and sync dimensions to avoid partial reads and flicker - Update structure.width/height in buffer mode - Recreate framebuffer/renderbuffer when buffer size changes - Avoid duplicate FBO creation at init (setup handled after changeSize) Improves stability of PixelBufferTexture reads and texture updates --- flutter_angle/windows/flutter_gl_texture.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/flutter_angle/windows/flutter_gl_texture.cpp b/flutter_angle/windows/flutter_gl_texture.cpp index 0e7189c..cbd97ea 100755 --- a/flutter_angle/windows/flutter_gl_texture.cpp +++ b/flutter_angle/windows/flutter_gl_texture.cpp @@ -165,6 +165,10 @@ void FlutterGLTexture::changeSize(int setWidth, int setHeight, std::unique_ptrwidth = setWidth; pixelBuffer->height = setHeight; memset(pixels.get(), 0x00, size); + + // Recreate FBO/RBO to match new size to avoid partial reads/flicker + if (textures.rboId != 0) { + glDeleteRenderbuffers(1, &textures.rboId); + textures.rboId = 0; + } + if (textures.fboId != 0) { + glDeleteFramebuffers(1, &textures.fboId); + textures.fboId = 0; + } + setupOpenGLResources(); if(didStart){ /// we send back the context so that the Dart side can create a linked context. auto response = flutter::EncodableValue(flutter::EncodableMap{ @@ -364,7 +379,6 @@ void FlutterGLTexture::createTexture(std::unique_ptr(); changeSize(structure.width,structure.height,result); - setupOpenGLResources(); } didStart = true;