From ad000aeb3163e187cd70effedd3cc9912b2850d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Fri, 16 Oct 2026 15:41:21 -0700 Subject: [PATCH] Fixed calcMinBoundingSphere hang, and ProcessReader quoting on Windows. (#463) --- src/bounds.cpp | 6 +++- src/process.cpp | 53 ++++++++++++++++++++++++++++------ tests/bounds_test.cpp | 17 +++++++++++ tests/process_test.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 132 insertions(+), 9 deletions(-) create mode 100644 tests/process_test.cpp diff --git a/src/bounds.cpp b/src/bounds.cpp index e6e3781..3e8f73c 100644 --- a/src/bounds.cpp +++ b/src/bounds.cpp @@ -326,7 +326,11 @@ namespace bx center.x += xx * radiusStep; center.y += yy * radiusStep; center.z += zz * radiusStep; - maxDistSq = lerp(maxDistSq, distSq, _step); + const float newMaxDistSq = lerp(maxDistSq, distSq, _step); + maxDistSq = newMaxDistSq > maxDistSq + ? newMaxDistSq + : distSq + ; break; } diff --git a/src/process.cpp b/src/process.cpp index fc8201e..7affdbb 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -44,6 +44,39 @@ namespace bx #endif // BX_PLATFORM_WINDOWS } + static bool makeCommandLine(char* _out, int32_t _outSize, const StringView& _filePath, const StringView& _args) + { +#if BX_PLATFORM_WINDOWS + // cmd.exe /c strips the first and the last quote of the command line. + const StringView prefix = "\"\""; + const StringView suffix = "\""; +#else + const StringView prefix = "\""; + const StringView suffix = ""; +#endif // BX_PLATFORM_WINDOWS + + const int32_t len = 0 + + prefix.getLength() + + _filePath.getLength() + + 2 + + _args.getLength() + + suffix.getLength() + ; + + if (len >= _outSize) + { + return false; + } + + strCopy(_out, _outSize, prefix); + strCat(_out, _outSize, _filePath); + strCat(_out, _outSize, "\" "); + strCat(_out, _outSize, _args); + strCat(_out, _outSize, suffix); + + return true; + } + ProcessReader::ProcessReader() : m_file(NULL) { @@ -64,10 +97,12 @@ namespace bx return false; } - char tmp[kMaxFilePath*2] = "\""; - strCat(tmp, BX_COUNTOF(tmp), _filePath); - strCat(tmp, BX_COUNTOF(tmp), "\" "); - strCat(tmp, BX_COUNTOF(tmp), _args); + char tmp[kMaxFilePath*2]; + if (!makeCommandLine(tmp, BX_COUNTOF(tmp), _filePath, _args) ) + { + BX_ERROR_SET(_err, kErrorReaderWriterOpen, "ProcessReader: Command line is too long."); + return false; + } m_file = openProcess(tmp, "r"); if (NULL == m_file) @@ -135,10 +170,12 @@ namespace bx return false; } - char tmp[kMaxFilePath*2] = "\""; - strCat(tmp, BX_COUNTOF(tmp), _filePath); - strCat(tmp, BX_COUNTOF(tmp), "\" "); - strCat(tmp, BX_COUNTOF(tmp), _args); + char tmp[kMaxFilePath*2]; + if (!makeCommandLine(tmp, BX_COUNTOF(tmp), _filePath, _args) ) + { + BX_ERROR_SET(_err, kErrorReaderWriterOpen, "ProcessWriter: Command line is too long."); + return false; + } m_file = openProcess(tmp, "w"); if (NULL == m_file) diff --git a/tests/bounds_test.cpp b/tests/bounds_test.cpp index fc7d5e9..84143b6 100644 --- a/tests/bounds_test.cpp +++ b/tests/bounds_test.cpp @@ -961,6 +961,23 @@ TEST_CASE("calcMinBoundingSphere", "[bounds]") REQUIRE(sphere.radius >= 1.73f - 0.05f); } +TEST_CASE("calcMinBoundingSphere far from origin", "[bounds]") +{ + const bx::Vec3 verts[] = { + {10000000.0f - 10.0f, 0.0f, 0.0f}, + {10000000.0f + 10.0f, 0.0f, 0.0f}, + {10000000.0f + 11.0f, 0.0f, 0.0f}, + }; + + bx::Sphere sphere; + bx::calcMinBoundingSphere(sphere, verts, BX_COUNTOF(verts), sizeof(bx::Vec3) ); + + for (uint32_t ii = 0; ii < BX_COUNTOF(verts); ++ii) + { + REQUIRE(bx::distanceSq(sphere.center, verts[ii]) <= bx::square(sphere.radius) ); + } +} + TEST_CASE("calcObb from cube vertices", "[bounds]") { const bx::Vec3 verts[] = { diff --git a/tests/process_test.cpp b/tests/process_test.cpp new file mode 100644 index 0000000..2d6b2e6 --- /dev/null +++ b/tests/process_test.cpp @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2026 Branimir Karadzic. All rights reserved. + * License: https://github.com/bkaradzic/bx/blob/master/LICENSE + */ + +#include "test.h" +#include +#include +#include + +#if BX_PLATFORM_WINDOWS +TEST_CASE("ProcessReader quoted path and arguments", "[process]") +{ + bx::FilePath dir(bx::Dir::Temp); + dir.join("bx process test"); + bx::removeAll(dir, bx::ErrorIgnore{}); + REQUIRE(bx::makeAll(dir, bx::ErrorAssert{}) ); + + bx::FilePath file(dir); + file.join("echo.bat"); + + { + const bx::StringView script = "@echo %*\r\n"; + + bx::FileWriter writer; + REQUIRE(bx::open(&writer, file, false, bx::ErrorAssert{}) ); + REQUIRE(script.getLength() == bx::write(&writer, script.getPtr(), script.getLength(), bx::ErrorAssert{}) ); + bx::close(&writer); + } + + bx::ProcessReader reader; + bx::Error err; + + REQUIRE(reader.open(file, "\"a b\" c", &err) ); + + char tmp[256]; + const int32_t size = bx::read(&reader, tmp, sizeof(tmp)-1, &err); + reader.close(); + tmp[size] = '\0'; + + bx::removeAll(dir, bx::ErrorIgnore{}); + + REQUIRE(0 == reader.getExitCode() ); + REQUIRE(bx::hasPrefix(tmp, "\"a b\" c") ); +} + +TEST_CASE("ProcessReader command line too long", "[process]") +{ + char args[bx::kMaxFilePath*2]; + bx::memSet(args, 'a', sizeof(args)-1); + args[sizeof(args)-1] = '\0'; + + bx::ProcessReader reader; + bx::Error err; + + const bool opened = reader.open("C:/Windows/System32/cmd.exe", args, &err); + if (opened) + { + reader.close(); + } + + REQUIRE(!opened); + REQUIRE(!err.isOk() ); +} +#endif // BX_PLATFORM_WINDOWS