From 96cdad12bd497082913d69ea0e01b2c31dd4c42b Mon Sep 17 00:00:00 2001 From: Mack Straight Date: Sat, 3 Oct 2026 10:46:55 -0400 Subject: [PATCH 1/2] test: run a host with endpoint names over the driver's limit Adds a long-endpoint-name scenario to the issues group: a host whose endpoint names are 92 characters, past the driver's 63-character limit. Layouts take --id-prefix so the IDs can stay short. Co-Authored-By: Claude Opus 5.5 --- SarTest/SarTest.cpp | 1 + SarTest/common.cpp | 1 + tools/test/Invoke-SarTest.ps1 | 5 +++++ tools/test/README.md | 4 +++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/SarTest/SarTest.cpp b/SarTest/SarTest.cpp index bb7fc2a..7639651 100644 --- a/SarTest/SarTest.cpp +++ b/SarTest/SarTest.cpp @@ -65,6 +65,7 @@ int usage() "\n" "Layout options: --endpoints N (playback/recording pairs, default 2)\n" " --channels C (per endpoint, default 2) --prefix \n" + " --id-prefix (endpoint IDs; default: the name prefix)\n" "Other options: --results --phase-timeout S --no-sarasio-log\n" " --keep-config (host/run: do not rewrite default.json)\n" " --registered (use the COM-registered SarAsio instead of a path)\n" diff --git a/SarTest/common.cpp b/SarTest/common.cpp index 8db42b7..92a767c 100644 --- a/SarTest/common.cpp +++ b/SarTest/common.cpp @@ -216,6 +216,7 @@ EndpointLayout EndpointLayout::fromArgs(const Args& args) layout.pairs = args.getInt(L"endpoints", 2); layout.channels = args.getInt(L"channels", 2); layout.prefix = args.get(L"prefix", L"SarTest"); + layout.idPrefix = args.get(L"id-prefix", L""); if (layout.pairs < 1) { layout.pairs = 1; diff --git a/tools/test/Invoke-SarTest.ps1 b/tools/test/Invoke-SarTest.ps1 index 20da33a..45d598a 100644 --- a/tools/test/Invoke-SarTest.ps1 +++ b/tools/test/Invoke-SarTest.ps1 @@ -153,6 +153,11 @@ if ($Scenarios -contains 'issues') { @('no-interface') + $common) -Timeout 120 $summary.scenarios += Invoke-Scenario 'issue-15-endpoint-names' ( @('endpoint-names') + $common) -Timeout 300 + # An endpoint name longer than the driver's 63 characters used to abort + # the host process in SarAsio. + $summary.scenarios += Invoke-Scenario 'long-endpoint-name' ( + @('host', '--endpoints', 1, '--duration', 2, '--id-prefix', 'SarTest-long', + '--prefix', ('SarTestLongEndpointName' * 4)) + $common) -Timeout 120 } if ($Scenarios -contains 'matrix') { diff --git a/tools/test/README.md b/tools/test/README.md index 8bf996c..e677faf 100644 --- a/tools/test/README.md +++ b/tools/test/README.md @@ -90,7 +90,9 @@ clock never had a second instance alive. It needs an interactive desktop. configured and checks that it still offers a stereo input and output. `SarTest.exe endpoint-names` (#15, #66) renames an endpoint keeping its ID, then gives it a new ID, and checks that the new ID shows the new name; the -results record what Windows shows for the reused ID. +results record what Windows shows for the reused ID. `long-endpoint-name` +runs a host whose endpoint names are longer than the driver's 63-character +limit. `Invoke-SarTest.ps1 -Scenarios` selects any of `issues`, `matrix`, `race` and `kill`. From f46ff2dc817437714418339e388c8bb0a35ae0a6 Mon Sep 17 00:00:00 2001 From: Mack Straight Date: Sat, 3 Oct 2026 10:47:21 -0400 Subject: [PATCH 2/2] SarAsio: don't take the host down over endpoint dialog input SarClient copied endpoint names and IDs into the driver request with wcscpy_s, which aborts the process when the value doesn't fit the driver's 63 characters, and the endpoint dialog let names of any length through. A name of 64 characters or more killed the DAW on start. Names are now truncated (and the dialog limits them), and an over-long ID, which can only come from an edited configuration, fails the start with a log message instead. The dialog also parsed the channel count with std::stoi, which throws on an empty or non-numeric value. It now accepts 1 to 32 (the driver's limit; larger counts made endpoint creation fail) and otherwise says so and stays open. Co-Authored-By: Claude Opus 5.5 --- SarAsio/configui.cpp | 29 +++++++++++++++++++++++++---- SarAsio/configui.h | 2 +- SarAsio/sarclient.cpp | 16 ++++++++++++++-- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/SarAsio/configui.cpp b/SarAsio/configui.cpp index 53e96de..82086c8 100644 --- a/SarAsio/configui.cpp +++ b/SarAsio/configui.cpp @@ -18,6 +18,7 @@ #include "configui.h" #include "dllmain.h" #include "utility.h" +#include "sar.h" using namespace Sar; @@ -390,6 +391,8 @@ void EndpointsPropertySheetPage::initEpDialogControls() _epDialogType = GetDlgItem(_epDialog, 1103); _epDialogChannelCount = GetDlgItem(_epDialog, 1105); + Edit_LimitText(_epDialogName, MAX_ENDPOINT_NAME_LENGTH); + ComboBox_AddString(_epDialogType, L"Playback"); ComboBox_AddString(_epDialogType, L"Recording"); ComboBox_SetCurSel(_epDialogType, @@ -402,7 +405,7 @@ void EndpointsPropertySheetPage::initEpDialogControls() Edit_SetText(_epDialogChannelCount, wos.str().c_str()); } -void EndpointsPropertySheetPage::updateEpDialogConfig() +bool EndpointsPropertySheetPage::updateEpDialogConfig() { auto len = Edit_GetTextLength(_epDialogName); auto buf = new WCHAR[len + 1]; @@ -415,12 +418,28 @@ void EndpointsPropertySheetPage::updateEpDialogConfig() buf = new WCHAR[len + 1]; Edit_GetText(_epDialogChannelCount, buf, len + 1); - auto countStr = TCHARToUTF8(buf); + WCHAR *end = nullptr; + long count = wcstol(buf, &end, 10); + bool valid = end != buf && *end == L'\0' && + count >= 1 && count <= SAR_MAX_CHANNEL_COUNT; delete[] buf; - _epDialogConfig.channelCount = max(1, std::stoi(countStr)); + + if (!valid) { + // std::stoi threw on these and took the host down with it. + std::wostringstream message; + + message << L"The channel count must be a number from 1 to " + << SAR_MAX_CHANNEL_COUNT << L"."; + MessageBox(_epDialog, message.str().c_str(), TEXT("Error"), + MB_OK | MB_ICONERROR); + return false; + } + + _epDialogConfig.channelCount = (int)count; _epDialogConfig.type = ComboBox_GetCurSel(_epDialogType) == 1 ? EndpointType::Recording : EndpointType::Playback; + return true; } INT_PTR EndpointsPropertySheetPage::epDialogProc( @@ -435,7 +454,9 @@ INT_PTR EndpointsPropertySheetPage::epDialogProc( case WM_COMMAND: switch (LOWORD(wparam)) { case IDOK: - updateEpDialogConfig(); + if (!updateEpDialogConfig()) { + break; + } // fall-through case IDCANCEL: diff --git a/SarAsio/configui.h b/SarAsio/configui.h index 1e07007..c52856d 100644 --- a/SarAsio/configui.h +++ b/SarAsio/configui.h @@ -100,7 +100,7 @@ struct EndpointsPropertySheetPage: public PropertySheetPage HWND _removeButton; void initEpDialogControls(); - void updateEpDialogConfig(); + bool updateEpDialogConfig(); INT_PTR epDialogProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); static INT_PTR CALLBACK epDialogProcStub( diff --git a/SarAsio/sarclient.cpp b/SarAsio/sarclient.cpp index 421ebcf..6e2bbc8 100644 --- a/SarAsio/sarclient.cpp +++ b/SarAsio/sarclient.cpp @@ -397,8 +397,20 @@ bool SarClient::createEndpoints() SAR_ENDPOINT_TYPE_PLAYBACK : SAR_ENDPOINT_TYPE_RECORDING; request.channelCount = endpoint.channelCount; request.index = i++; - wcscpy_s(request.name, endpoint.description.c_str()); - wcscpy_s(request.id, UTF8ToWide(endpoint.id).c_str()); + + // wcscpy_s would abort the host process on a value that doesn't fit. + // A long name is only cut short; a long ID can't be, since two IDs + // could then collide. + auto id = UTF8ToWide(endpoint.id); + + if (id.size() > MAX_ENDPOINT_NAME_LENGTH) { + LOG(ERROR) << "Endpoint ID " << endpoint.id << " is longer than " + << MAX_ENDPOINT_NAME_LENGTH << " characters."; + return false; + } + + wcsncpy_s(request.name, endpoint.description.c_str(), _TRUNCATE); + wcscpy_s(request.id, id.c_str()); if (!DeviceIoControl(_device, SAR_CREATE_ENDPOINT, (LPVOID)&request, sizeof(request), nullptr, 0, &dummy, nullptr)) {