OverviewFilesCommitsIssuesDocsReleasesPeers

blender-bridge commits / 1200c0a

Fix crash and shading: bounds checks, winding reversal, null-safe delete (v0.8.0)
- Add vert_count bounds checking (max 100k, data_end validation) to prevent SEGFAULT when geometry nodes produce large or corrupt vertex data - Add winding reversal for non-indexed per-corner data (swap vertices 1 and 2 of each triangle) to fix inverted faces from Y↔Z coordinate swap - Null-safe DELETE handling: guard target dereference to prevent crash when tracked object's instance was already freed - Bump version to 0.8.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1200c0a2b761f8aba4868269b18d008d9ce9cbbc
by plankatron <fleeky@gmail.com> · 2026-03-03T17:59:06.000Z

Changed files (4) +34 -10

blender_addon/__init__.py +1 -1
……@@ -11,7 +11,7 @@ Usage:
1111
1212import bpy
1313
14ADDON_VERSION = "0.7.0"
14ADDON_VERSION = "0.8.0"
1515
1616# Support hot-reload (F3 > Reload Scripts)
1717if "shm_protocol" in locals():
blender_addon/blender_manifest.toml +1 -1
……@@ -2,7 +2,7 @@ schema_version = "1.0.0"
22id = "blender_godot_bridge"
33name = "Blender-Godot Bridge"
44tagline = "Real-time bidirectional scene sync with Godot"
5version = "0.7.0"
5version = "0.8.0"
66blender_version_min = "4.2.0"
77type = "add-on"
88maintainer = "blender-bridge <noreply@localhost>"
blender_bridge_addon.zip +0 -0
(no content changes — binary or rename)
src/blender_bridge.cpp +32 -8
……@@ -161,7 +161,7 @@ Error BlenderBridge::start_server(const String &socket_path) {
161161 _known_instance_ids.clear();
162162 _next_object_id = 1;
163163
164 UtilityFunctions::print("BlenderBridge v0.7.0: Server started (shm=", _shm_name, ", sock=", socket_path, ", regions=2x", (int)_region_size, ")");
164 UtilityFunctions::print("BlenderBridge v0.8.0: Server started (shm=", _shm_name, ", sock=", socket_path, ", regions=2x", (int)_region_size, ")");
165165 return OK;
166166}
167167
……@@ -706,10 +706,17 @@ void BlenderBridge::import_changes(Node *root) {
706706 MeshInstance3D *mesh_inst = Object::cast_to<MeshInstance3D>(target);
707707 if (mesh_inst && entry.data_size > TRANSFORM_SIZE + 4) {
708708 uint32_t cursor = transform_offset + TRANSFORM_SIZE;
709 uint32_t data_end = entry.data_offset + entry.data_size;
709710 uint32_t vert_count;
710711 memcpy(&vert_count, base + cursor, 4);
711712 cursor += 4;
712713
714 // Bounds check: ensure position data fits within the entry
715 if (vert_count > 100000 || cursor + vert_count * 12 > data_end) {
716 UtilityFunctions::printerr("BlenderBridge: vert_count out of bounds: ", (int)vert_count);
717 continue;
718 }
719
713720 PackedVector3Array positions;
714721 positions.resize(vert_count);
715722 Vector3 *pos_w = positions.ptrw();
……@@ -725,12 +732,12 @@ void BlenderBridge::import_changes(Node *root) {
725732 // Read has_normals flag
726733 PackedVector3Array normals;
727734 uint32_t has_normals = 0;
728 if (cursor + 4 <= entry.data_offset + entry.data_size) {
735 if (cursor + 4 <= data_end) {
729736 memcpy(&has_normals, base + cursor, 4);
730737 cursor += 4;
731738 }
732739
733 if (has_normals && cursor + vert_count * 12 <= entry.data_offset + entry.data_size) {
740 if (has_normals && cursor + vert_count * 12 <= data_end) {
734741 normals.resize(vert_count);
735742 Vector3 *norm_w = normals.ptrw();
736743 for (uint32_t v = 0; v < vert_count; v++) {
……@@ -746,12 +753,12 @@ void BlenderBridge::import_changes(Node *root) {
746753 // Read index buffer
747754 PackedInt32Array indices;
748755 uint32_t idx_count = 0;
749 if (cursor + 4 <= entry.data_offset + entry.data_size) {
756 if (cursor + 4 <= data_end) {
750757 memcpy(&idx_count, base + cursor, 4);
751758 cursor += 4;
752759 }
753760
754 if (idx_count > 0 && cursor + idx_count * 4 <= entry.data_offset + entry.data_size) {
761 if (idx_count > 0 && cursor + idx_count * 4 <= data_end) {
755762 indices.resize(idx_count);
756763 memcpy(indices.ptrw(), base + cursor, idx_count * 4);
757764 cursor += idx_count * 4;
……@@ -763,6 +770,21 @@ void BlenderBridge::import_changes(Node *root) {
763770 idx_w[t + 1] = idx_w[t + 2];
764771 idx_w[t + 2] = tmp;
765772 }
773 } else if (idx_count == 0 && vert_count >= 3) {
774 // Per-corner data (no index buffer): reverse winding by
775 // swapping vertices 1 and 2 of each triangle in-place.
776 Vector3 *pw = positions.ptrw();
777 Vector3 *nw = normals.size() > 0 ? normals.ptrw() : nullptr;
778 for (uint32_t t = 0; t + 2 < vert_count; t += 3) {
779 Vector3 tmp = pw[t + 1];
780 pw[t + 1] = pw[t + 2];
781 pw[t + 2] = tmp;
782 if (nw) {
783 tmp = nw[t + 1];
784 nw[t + 1] = nw[t + 2];
785 nw[t + 2] = tmp;
786 }
787 }
766788 }
767789
768790 // Read per-triangle smooth flags (after indices)
……@@ -858,14 +880,16 @@ void BlenderBridge::import_changes(Node *root) {
858880
859881 if (entry.update_type == UPDATE_DELETE) {
860882 // Remove from tracking before freeing
861 uint64_t del_id = target->get_instance_id();
862 _known_instance_ids.erase(del_id);
883 if (target) {
884 uint64_t del_id = target->get_instance_id();
885 _known_instance_ids.erase(del_id);
886 target->queue_free();
887 }
863888 _tracked_objects.erase(
864889 std::remove_if(_tracked_objects.begin(), _tracked_objects.end(),
865890 [&](const TrackedObject &t) { return t.object_id == entry.object_id; }),
866891 _tracked_objects.end());
867892 _last_export_node_count--;
868 target->queue_free();
869893 }
870894
871895 imported++;