diff --git a/eng/install-native-dependencies.sh b/eng/install-native-dependencies.sh
index f8c9db632860de..d09651ca1e1a66 100755
--- a/eng/install-native-dependencies.sh
+++ b/eng/install-native-dependencies.sh
@@ -44,6 +44,13 @@ case "$os" in
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
# Skip brew update for now, see https://github.com/actions/setup-python/issues/577
# brew update --preinstall
+
+ # Remove Homebrew LLVM if present. The CI runner image may ship with a
+ # Homebrew LLVM whose libraries (e.g., libunwind.dylib) are the wrong
+ # architecture or conflict with the Apple SDK, breaking native linking.
+ # The build uses Apple clang from /usr/bin/clang exclusively.
+ brew uninstall --ignore-dependencies llvm 2>/dev/null || true
+
brew bundle --no-upgrade --file "$(dirname "$0")/Brewfile"
;;
diff --git a/src/libraries/System.Net.WebSockets/src/Resources/Strings.resx b/src/libraries/System.Net.WebSockets/src/Resources/Strings.resx
index a57e81b239a92c..a6033ed9cf8313 100644
--- a/src/libraries/System.Net.WebSockets/src/Resources/Strings.resx
+++ b/src/libraries/System.Net.WebSockets/src/Resources/Strings.resx
@@ -117,6 +117,9 @@
The WebSocket server sent a masked frame.
+
+ The WebSocket client sent an unmasked frame.
+
The WebSocket received a continuation frame from a previous final message.
diff --git a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
index 8a26a4c29e2eb0..1e80e82bdde01a 100644
--- a/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
+++ b/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/ManagedWebSocket.cs
@@ -1366,6 +1366,11 @@ private async ValueTask CloseWithReceiveErrorAndThrowAsync(
// Consume the mask bytes
ConsumeFromBuffer(4);
}
+ else if (_isServer)
+ {
+ resultHeader = default;
+ return SR.net_Websockets_ServerReceivedUnmaskedFrame;
+ }
// Do basic validation of the header
switch (header.Opcode)
diff --git a/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs b/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs
index 73e84998a94197..41c7fe341d266d 100644
--- a/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs
+++ b/src/libraries/System.Net.WebSockets/tests/WebSocketTests.cs
@@ -182,6 +182,20 @@ public async Task ThrowWhenContinuationWithDifferentCompressionFlags()
client.SendAsync(Memory.Empty, WebSocketMessageType.Binary, WebSocketMessageFlags.EndOfMessage, default));
}
+ [Fact]
+ public async Task ReceiveAsync_ServerUnmaskedFrame_ThrowsWebSocketException()
+ {
+ byte[] frame = { 0x81, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F };
+ using var stream = new MemoryStream();
+ stream.Write(frame, 0, frame.Length);
+ stream.Position = 0;
+ using WebSocket websocket = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true });
+ WebSocketException exception = await Assert.ThrowsAsync(() =>
+ websocket.ReceiveAsync(new byte[5], CancellationToken.None));
+ Assert.Equal(SR.net_Websockets_ServerReceivedUnmaskedFrame, exception.Message);
+ Assert.Equal(WebSocketState.Aborted, websocket.State);
+ }
+
[Fact]
public async Task ReceiveAsync_WhenDisposedInParallel_DoesNotGetStuck()
{