Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
df250f5
Fix potential broken state after transaction timeout
Mar 17, 2026
23c189e
Potential fix for pull request finding
chemwolf6922 Mar 17, 2026
9320322
resolve copilot comments
Mar 17, 2026
c283118
Merge branch 'fix-broken-state-after-transaction-timeout' of github.c…
Mar 17, 2026
69f4d11
revert magic related changes
Mar 20, 2026
9aaaf93
revert magic related changes
Mar 20, 2026
16d2043
protect sequence number with lock
Mar 20, 2026
8aaa647
disable sequence number check for dns tunneling
Mar 20, 2026
e28da89
simplify ignore sequence logic
Mar 20, 2026
19078fd
use new approach
Mar 20, 2026
e46e903
apply strict mode for the wslcoreport channel
Mar 20, 2026
5b21021
handle multiple reply
Mar 20, 2026
458df6f
Fix strict mode set time
Mar 20, 2026
fe9a05b
resolve comments
chemwolf6922 Mar 24, 2026
a71e2a4
Merge branch 'master' into fix-broken-state-after-transaction-timeout
chemwolf6922 Mar 25, 2026
32c1586
resolve comments
chemwolf6922 Mar 25, 2026
cee5ae5
Merge branch 'fix-broken-state-after-transaction-timeout' of github.c…
chemwolf6922 Mar 25, 2026
d96a7b1
reduce changed code scope
chemwolf6922 Mar 25, 2026
864e383
Merge branch 'master' into fix-broken-state-after-transaction-timeout
chemwolf6922 Mar 27, 2026
84aad97
replace sequence number with transaction logic
chemwolf6922 Apr 2, 2026
a59098f
resolve copilot comments
chemwolf6922 Apr 2, 2026
a4af7b6
resolve copilot comments
chemwolf6922 Apr 7, 2026
4959e90
Merge branch 'master' into fix-broken-state-after-transaction-timeout
chemwolf6922 Apr 16, 2026
504cb18
make transaction non-copyable
chemwolf6922 Apr 16, 2026
b846ace
resolve comments
chemwolf6922 Apr 17, 2026
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
14 changes: 8 additions & 6 deletions src/linux/init/GnsEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ constexpr auto c_ipStrings = {"ip", "ip6"};
const char* c_loopbackInterfaceName = "lo";

GnsEngine::GnsEngine(
wsl::shared::SocketChannel& channel,
const NotificationRoutine& notificationRoutine,
const StatusRoutine& statusRoutine,
NetworkManager& manager,
std::optional<int> dnsTunnelingFd,
const std::string& dnsTunnelingIpAddress) :
notificationRoutine(notificationRoutine), statusRoutine(statusRoutine), manager(manager)
channel(channel), notificationRoutine(notificationRoutine), statusRoutine(statusRoutine), manager(manager)
{
if (dnsTunnelingFd.has_value())
{
Expand Down Expand Up @@ -363,11 +364,11 @@ void GnsEngine::ProcessLinkChange(Interface& interface, const wsl::shared::hns::
}
}

std::tuple<bool, int> GnsEngine::ProcessNextMessage()
std::tuple<bool, int> GnsEngine::ProcessNextMessage(wsl::shared::Transaction& transaction)
{
int return_value = 0;

auto payload = notificationRoutine();
auto payload = notificationRoutine(transaction);
if (!payload.has_value())
{
GNS_LOG_ERROR("Received empty message, exiting");
Expand Down Expand Up @@ -723,22 +724,23 @@ void GnsEngine::run()

while (true)
{
auto transaction = channel.ReceiveTransaction();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if gns is running is test mode, channel would be an empty channel here. I'm not sure that we should allow transaction on empty channels since that could lead to some weird states, maybe the easiest way here would be to make the transaction an std::optional, and plumb the optional through NotificationRoutine and StatusRoutine so we don't have to deal with it in this class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a transaction on an empty channel is benign. It will not disrupt that channel's state. And using that transaction will behave the same as calling receive or send on that empty channel. The test path in StartGns already correctly ignores the input Transaction.
I feel that making the created transaction optional is too complicated for normal callers. Maybe we should keep it as is for this.

try
{
GNS_LOG_INFO("Processing Next Message");
auto [should_continue, return_value] = ProcessNextMessage();
auto [should_continue, return_value] = ProcessNextMessage(transaction);
if (!should_continue)
{
break;
}

GNS_LOG_INFO("Processing Next Message Successful ({:#x})", return_value);
statusRoutine(return_value, "");
statusRoutine(return_value, "", transaction);
}
catch (const std::exception& e)
{
GNS_LOG_ERROR("Error while processing message: {}", e.what());
statusRoutine(-1, e.what());
statusRoutine(-1, e.what(), transaction);
Comment thread
chemwolf6922 marked this conversation as resolved.
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/linux/init/GnsEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ class GnsEngine
std::optional<GUID> AdapterId;
};

using NotificationRoutine = std::function<std::optional<Message>()>;
using StatusRoutine = std::function<void(int, const std::string&)>;
using NotificationRoutine = std::function<std::optional<Message>(wsl::shared::Transaction&)>;
using StatusRoutine = std::function<void(int, const std::string&, wsl::shared::Transaction&)>;

GnsEngine(
wsl::shared::SocketChannel& channel,
const NotificationRoutine& notificationRoutine,
const StatusRoutine& statusRoutine,
NetworkManager& manager,
Expand All @@ -36,7 +37,7 @@ class GnsEngine
void run();

private:
std::tuple<bool, int> ProcessNextMessage();
std::tuple<bool, int> ProcessNextMessage(wsl::shared::Transaction& transaction);

void ProcessNotification(const nlohmann::json& payload, Interface& interface);

Expand Down Expand Up @@ -69,6 +70,7 @@ class GnsEngine
void ProcessNotificationImpl(
Interface& interface, const nlohmann::json& payload, void (GnsEngine::*routine)(Interface&, const T&, wsl::shared::hns::ModifyRequestType));

wsl::shared::SocketChannel& channel;
const NotificationRoutine& notificationRoutine;
const StatusRoutine& statusRoutine;
NetworkManager& manager;
Expand Down
3 changes: 2 additions & 1 deletion src/linux/init/binfmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ try
// Send the create process message to the interop server.
//

channel.SendMessage<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);
auto transaction = channel.StartTransaction();
transaction.Send<LX_INIT_CREATE_NT_PROCESS_UTILITY_VM>(Span);

//
// Accept connections from the interop server.
Expand Down
31 changes: 16 additions & 15 deletions src/linux/init/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ try
CATCH_LOG()

void ConfigHandleInteropMessage(
wsl::shared::SocketChannel& ResponseChannel,
wsl::shared::Transaction& Transaction,
wsl::shared::SocketChannel& InteropChannel,
bool Elevated,
gsl::span<gsl::byte> Message,
Expand All @@ -350,7 +350,7 @@ Routine Description:

Arguments:

ResponseChannel - Supplies channel used to send responses.
Transaction - Supplies transaction used to send responses.

InteropChannel - Supplies a channel to the host to be used for create
process requests.
Expand Down Expand Up @@ -381,7 +381,7 @@ try

case LxInitMessageQueryDrvfsElevated:
{
ResponseChannel.SendResultMessage<bool>(Elevated);
Transaction.SendResultMessage<bool>(Elevated);
break;
}

Expand All @@ -397,15 +397,15 @@ try
auto Value = UtilGetEnvironmentVariable(Query->Buffer);
wsl::shared::MessageWriter<LX_INIT_QUERY_ENVIRONMENT_VARIABLE> Response(LxInitMessageQueryEnvironmentVariable);
Response.WriteString(Value);
ResponseChannel.SendMessage<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>(Response.Span());
Transaction.Send<LX_INIT_QUERY_ENVIRONMENT_VARIABLE>(Response.Span());
}

break;

case LxInitMessageQueryFeatureFlags:
{
assert(Config.FeatureFlags.has_value());
ResponseChannel.SendResultMessage<int32_t>(Config.FeatureFlags.value());
Transaction.SendResultMessage<int32_t>(Config.FeatureFlags.value());
break;
}

Expand All @@ -419,7 +419,7 @@ try
}

bool success = false;
auto sendResponse = wil::scope_exit([&]() { ResponseChannel.SendResultMessage<bool>(success); });
auto sendResponse = wil::scope_exit([&]() { Transaction.SendResultMessage<bool>(success); });

if (!Config.BootInit || Config.InitPid.value_or(0) != getpid())
{
Expand All @@ -435,7 +435,7 @@ try

case LxInitMessageQueryNetworkingMode:
assert(Config.NetworkingMode.has_value());
ResponseChannel.SendResultMessage<uint8_t>(static_cast<uint8_t>(Config.NetworkingMode.value()));
Transaction.SendResultMessage<uint8_t>(static_cast<uint8_t>(Config.NetworkingMode.value()));
break;

case LxInitMessageQueryVmId:
Expand All @@ -446,7 +446,7 @@ try
Response.WriteString(Config.VmId.value());
}

ResponseChannel.SendMessage<LX_INIT_QUERY_VM_ID>(Response.Span());
Transaction.Send<LX_INIT_QUERY_VM_ID>(Response.Span());
break;
}

Expand Down Expand Up @@ -618,7 +618,7 @@ try
}
CATCH_LOG()

int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config)
int ConfigInitializeInstance(const std::function<void(const gsl::span<gsl::byte>&)>& SendResponse, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for taking an std::function instead of a reference to the transaction ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is also used for WSL1, where the transaction is initiated with UtilReadMessageLxBus. Which does not work with the new transaction model.
So a function is used here so WSL1 can keep the original behavior while WSL2 can use the transaction.


/*++

Expand All @@ -632,7 +632,7 @@ Routine Description:

Arguments:

MessageFd - Supplies a file descriptor to send the response message.
SendResponse - Supplies a function to send the response message.

Buffer - Supplies the message buffer.

Expand Down Expand Up @@ -923,7 +923,7 @@ try
Response.WriteString(Response->VersionIndex, Version->c_str());
}

Channel.SendMessage<LX_INIT_CONFIGURATION_INFORMATION_RESPONSE>(Response.Span());
SendResponse(Response.Span());

//
// Accept the interop connection.
Expand Down Expand Up @@ -973,13 +973,14 @@ try
continue;
}

auto [Message, Span] = ClientChannel.ReceiveMessageOrClosed<MESSAGE_HEADER>();
auto transaction = ClientChannel.ReceiveTransaction();
auto [Message, Span] = transaction.ReceiveOrClosed<MESSAGE_HEADER>();
if (Message == nullptr)
{
continue;
}

ConfigHandleInteropMessage(ClientChannel, InteropChannel, Elevated, Span, Message, Config);
ConfigHandleInteropMessage(transaction, InteropChannel, Elevated, Span, Message, Config);
}
});

Expand Down Expand Up @@ -2186,7 +2187,7 @@ Return Value:
return Result;
}

int ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config)
int ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::Transaction& Transaction, const wsl::linux::WslDistributionConfig& Config)

/*++

Expand All @@ -2207,7 +2208,7 @@ Return Value:

--*/
{
Channel.SendResultMessage<int32_t>(ConfigRemountDrvFsImpl(Buffer, Config));
Transaction.SendResultMessage<int32_t>(ConfigRemountDrvFsImpl(Buffer, Config));

return 0;
}
Expand Down
7 changes: 4 additions & 3 deletions src/linux/init/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Module Name:
#include <set>
#include <string_view>
#include <optional>
#include <functional>
#include "SocketChannel.h"
#include "WslDistributionConfig.h"

Expand Down Expand Up @@ -399,7 +400,7 @@ std::set<std::pair<unsigned int, std::string>> ConfigGetMountedDrvFsVolumes(void
std::vector<std::pair<std::string, std::string>> ConfigGetWslgEnvironmentVariables(const wsl::linux::WslDistributionConfig& Config);

void ConfigHandleInteropMessage(
wsl::shared::SocketChannel& ResponseChannel,
wsl::shared::Transaction& Transaction,
wsl::shared::SocketChannel& InteropChannel,
bool Elevated,
gsl::span<gsl::byte> Message,
Expand All @@ -408,7 +409,7 @@ void ConfigHandleInteropMessage(

void ConfigInitializeCgroups(wsl::linux::WslDistributionConfig& Config);

int ConfigInitializeInstance(wsl::shared::SocketChannel& Channel, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config);
int ConfigInitializeInstance(const std::function<void(const gsl::span<gsl::byte>&)>& SendResponse, gsl::span<gsl::byte> Buffer, wsl::linux::WslDistributionConfig& Config);

void ConfigMountDrvFsVolumes(unsigned int DrvFsVolumes, uid_t OwnerUid, std::optional<bool> Admin, const wsl::linux::WslDistributionConfig& Config);

Expand All @@ -420,7 +421,7 @@ int ConfigRegisterBinfmtInterpreter(void);

int ConfigSetMountNamespace(bool Elevated);

int ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::SocketChannel& Channel, const wsl::linux::WslDistributionConfig& Config);
int ConfigRemountDrvFs(gsl::span<gsl::byte> Buffer, wsl::shared::Transaction& Transaction, const wsl::linux::WslDistributionConfig& Config);

int ConfigRemountDrvFsImpl(gsl::span<gsl::byte> Buffer, const wsl::linux::WslDistributionConfig& Config);

Expand Down
5 changes: 3 additions & 2 deletions src/linux/init/drvfs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,9 @@ Return Value:
QueryPortMessage.MessageType = LxInitMessageQueryDrvfsElevated;
QueryPortMessage.MessageSize = sizeof(QueryPortMessage);

channel.SendMessage(QueryPortMessage);
return channel.ReceiveMessage<RESULT_MESSAGE<bool>>().Result;
auto transaction = channel.StartTransaction();
transaction.Send(QueryPortMessage);
return transaction.Receive<RESULT_MESSAGE<bool>>().Result;
}

int MountFilesystem(const char* FsType, const char* Source, const char* Target, const char* Options, int* ExitCode)
Expand Down
Loading