Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions SarTest/SarTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ int usage()
" --id-prefix <id> (endpoint IDs; default: the name prefix)\n"
"Other options: --results <file.json> --phase-timeout S --no-sarasio-log\n"
" --keep-config (host/run: do not rewrite default.json)\n"
" --app-routing (turn on application routing in default.json)\n"
" --registered (use the COM-registered SarAsio instead of a path)\n"
" --wait S --wait-gone S --restart-delay MS --rate HZ\n"
" --min-valid RATIO --max-discontinuities N\n"
Expand Down Expand Up @@ -145,7 +146,7 @@ bool prepareConfig(const Args& args, const EndpointLayout& layout,
}

if (!writeDriverConfig(layout, driverClsid,
args.getInt(L"wavert-min-frames", 0), path)) {
args.getInt(L"wavert-min-frames", 0), path, args.has(L"app-routing"))) {
return false;
}

Expand Down Expand Up @@ -211,7 +212,7 @@ int cmdConfig(const Args& args)
std::wstring out = args.get(L"out", configurationPath().c_str());

if (!writeDriverConfig(layout, SAR_TEST_CLOCK_CLSID_STR,
args.getInt(L"wavert-min-frames", 0), out)) {
args.getInt(L"wavert-min-frames", 0), out, args.has(L"app-routing"))) {
return 1;
}

Expand Down
5 changes: 3 additions & 2 deletions SarTest/install.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ std::wstring loggingPath()

bool writeDriverConfig(
const EndpointLayout& layout, const std::wstring& driverClsid,
int waveRtMinimumFrames, const std::wstring& path)
int waveRtMinimumFrames, const std::wstring& path,
bool applicationRouting)
{
std::vector<std::string> endpoints;

Expand All @@ -80,7 +81,7 @@ bool writeDriverConfig(
JsonObject root;

root.setString("driverClsid", narrow(driverClsid))
.setBool("enableApplicationRouting", false)
.setBool("enableApplicationRouting", applicationRouting)
.setInt("waveRtMinimumFrames", waveRtMinimumFrames)
.setRaw("endpoints", jsonArray(endpoints))
.setRaw("applications", "[]");
Expand Down
6 changes: 4 additions & 2 deletions SarTest/install.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ std::wstring configurationPath();
std::wstring loggingPath();

// Writes a SarAsio default.json describing the layout, with the given ASIO
// driver CLSID as the physical interface and application routing disabled.
// driver CLSID as the physical interface. Application routing (no rules, so
// only the redirection of the audio device enumerator) is off unless asked.
bool writeDriverConfig(
const EndpointLayout& layout, const std::wstring& driverClsid,
int waveRtMinimumFrames, const std::wstring& path);
int waveRtMinimumFrames, const std::wstring& path,
bool applicationRouting = false);

// Software device node for the SAR driver, matching the INF's hardware ID.
bool deviceNodeExists();
Expand Down
34 changes: 15 additions & 19 deletions SynchronousAudioRouter/entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,6 @@ NTSTATUS SarIrpDeviceControl(PDEVICE_OBJECT deviceObject, PIRP irp)
VOID SarUnload(PDRIVER_OBJECT driverObject)
{
SAR_INFO("SAR is unloading");
KIRQL irql;
SarDriverExtension *extension =
(SarDriverExtension *)IoGetDriverObjectExtension(
driverObject, DriverEntry);
Expand All @@ -731,12 +730,15 @@ VOID SarUnload(PDRIVER_OBJECT driverObject)
CmUnRegisterCallback(extension->filterCookie);
}

irql = ExAcquireSpinLockExclusive(&extension->registryRedirectLock);
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&extension->registryRedirectLock, TRUE);
SarClearStringTable(
&extension->registryRedirectTableWow64, SarDeleteRegistryRedirect);
SarClearStringTable(
&extension->registryRedirectTable, SarDeleteRegistryRedirect);
ExReleaseSpinLockExclusive(&extension->registryRedirectLock, irql);
ExReleaseResourceLite(&extension->registryRedirectLock);
KeLeaveCriticalRegion();
ExDeleteResourceLite(&extension->registryRedirectLock);

if (extension->filterUser) {
// Allocated by SeQueryInformationToken
Expand Down Expand Up @@ -768,9 +770,6 @@ BOOL SarFilterMatchesPath(
{
PRTL_AVL_TABLE table = &extension->registryRedirectTable;
PVOID entry = nullptr;
KIRQL irql;
UNICODE_STRING nonpagedPath;
NTSTATUS status = STATUS_SUCCESS;

if (!path) {
return FALSE;
Expand All @@ -782,24 +781,20 @@ BOOL SarFilterMatchesPath(
}
#endif

// TODO: we get a path that's in paged memory from the configuration manager
// so we can't access it with IRQL raised to dispatch level by the spinlock.
// Probably better to just use an ERESOURCE or something here instead.
status = SarStringDuplicate(&nonpagedPath, path);

if (!NT_SUCCESS(status)) {
return FALSE;
}

irql = ExAcquireSpinLockShared(&extension->registryRedirectLock);
entry = SarGetStringTableEntry(table, &nonpagedPath);
// Stay at PASSIVE_LEVEL: the path from the configuration manager is in
// paged memory and the table's string comparison is pageable code. Under
// a spin lock this bugchecked (IRQL_NOT_LESS_OR_EQUAL) whenever that code
// was paged out.
KeEnterCriticalRegion();
ExAcquireResourceSharedLite(&extension->registryRedirectLock, TRUE);
entry = SarGetStringTableEntry(table, path);

if (entry) {
*redirectPath = *(PCUNICODE_STRING)entry;
}

ExReleaseSpinLockShared(&extension->registryRedirectLock, irql);
SarStringFree(&nonpagedPath);
ExReleaseResourceLite(&extension->registryRedirectLock);
KeLeaveCriticalRegion();
return entry != nullptr;
}

Expand Down Expand Up @@ -1129,6 +1124,7 @@ extern "C" NTSTATUS DriverEntry(

RtlZeroMemory(extension, sizeof(SarDriverExtension));
ExInitializeFastMutex(&extension->mutex);
ExInitializeResourceLite(&extension->registryRedirectLock);
SarInitializeTable(&extension->controlContextTable);
SarInitializeStringTable(&extension->registryRedirectTableWow64);
SarInitializeStringTable(&extension->registryRedirectTable);
Expand Down
9 changes: 5 additions & 4 deletions SynchronousAudioRouter/sar.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,12 @@ typedef struct SarDriverExtension
// This shouldn't really be needed as long as CmUnRegisterCallback can't
// complete while one of our callback routines is still running, but there
// is no explicit documentation of that, so we protect the registry redirect
// tables with a reader writer spinlock "just in case." Note that
// tables with a reader writer lock "just in case." Note that
// RTL_AVL_TABLE is used here instead of RTL_GENERIC_TABLE to avoid
// splaying which would be unsafe with a read-only lock. All related
// structures must be in nonpaged memory.
EX_SPIN_LOCK registryRedirectLock;
// splaying which would be unsafe with a read-only lock. It is a resource,
// not a spin lock: lookups compare strings with RtlCompareUnicodeString,
// which is pageable, and the registry callback runs at PASSIVE_LEVEL.
ERESOURCE registryRedirectLock;
RTL_AVL_TABLE registryRedirectTableWow64;
RTL_AVL_TABLE registryRedirectTable;
LARGE_INTEGER filterCookie;
Expand Down
20 changes: 19 additions & 1 deletion tools/test/Invoke-SarTest.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ function Invoke-Scenario {
return $record
}

# Registers or unregisters SarAsio's COM classes. The driver's application
# routing only redirects to SarAsio when they are registered.
function Set-SarAsioRegistered {
param([bool]$Registered)

$regArgs = @('/s') + $(if ($Registered) { @() } else { @('/u') }) + @("`"$sarAsio`"")
$proc = Start-Process regsvr32.exe -ArgumentList $regArgs -Wait -PassThru
if ($proc.ExitCode -ne 0) { Write-Warning "regsvr32 $($regArgs -join ' ') exited with $($proc.ExitCode)" }
}

# Windows Server images ship with the audio stack disabled.
foreach ($service in 'AudioEndpointBuilder', 'Audiosrv') {
try {
Expand Down Expand Up @@ -146,7 +156,7 @@ if (-not $SkipInstall) {
$common = @('--sarasio', "`"$sarAsio`"", '--channels', $Channels)

if ($Scenarios -contains 'issues') {
# Regression checks for specific GitHub issues, one short host each.
# Regression checks, one short host each, most for a GitHub issue.
$summary.scenarios += Invoke-Scenario 'issue-133-control-panel' (
@('control-panel', '--endpoints', 1) + $common) -Timeout 120
$summary.scenarios += Invoke-Scenario 'issue-54-no-interface' (
Expand All @@ -158,6 +168,14 @@ if ($Scenarios -contains 'issues') {
$summary.scenarios += Invoke-Scenario 'long-endpoint-name' (
@('host', '--endpoints', 1, '--duration', 2, '--id-prefix', 'SarTest-long',
'--prefix', ('SarTestLongEndpointName' * 4)) + $common) -Timeout 120
# Application routing on: from then on the driver's registry filter sees
# every registry value query on the machine. With SarAsio's COM classes
# registered, the harness's own WASAPI client also goes through SarAsio's
# device enumerator wrapper.
Set-SarAsioRegistered $true
$summary.scenarios += Invoke-Scenario 'app-routing' (
@('run', '--endpoints', 1, '--iterations', 1, '--duration', $Duration, '--app-routing') + $common)
Set-SarAsioRegistered $false
}

if ($Scenarios -contains 'matrix') {
Expand Down
4 changes: 3 additions & 1 deletion tools/test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ configured and checks that it still offers a stereo input and output.
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. `long-endpoint-name`
runs a host whose endpoint names are longer than the driver's 63-character
limit.
limit. `app-routing` runs a loopback with application routing on and
SarAsio's COM classes registered, which turns on the driver's registry
filter.
`Invoke-SarTest.ps1 -Scenarios` selects any of `issues`, `matrix`, `race`
and `kill`.

Expand Down
Loading