Skip to content
Merged
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
29 changes: 17 additions & 12 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,9 @@ int64_t APS5_VABI sceNetSendmsg(int s, const NetMsghdr* msg, int flags) {
int64_t APS5_VABI sceNetRecvmsg(int s, NetMsghdr* msg, int flags) {
const auto total = message_length(msg);
if (total < 0) return total;
NetMsghdr header = *msg;
std::vector<NetIovec> vectors;
if (header.iov_length > 0) vectors.assign(header.iov, header.iov + header.iov_length);
if (!message_flags_supported(flags, NET_MSG_PEEK | NET_MSG_DONTWAIT)) return fail(NET_EOPNOTSUPP);
Sock socket;
{
Expand All @@ -1377,18 +1380,18 @@ int64_t APS5_VABI sceNetRecvmsg(int s, NetMsghdr* msg, int flags) {
const GuestArena::HostWrite messageOutput(msg, sizeof(*msg));
if (!messageOutput.Open()) return fail(NET_EFAULT);
std::deque<GuestArena::HostWrite> destinations;
for (int i = 0; i < msg->iov_length; ++i) {
const auto& entry = msg->iov[i];
for (const auto& entry : vectors) {
if (entry.length == 0) continue;
if (!destinations.emplace_back(entry.base, static_cast<std::size_t>(entry.length)).Open()) return fail(NET_EFAULT);
}
const bool datagram = socket.type != NET_SOCK_STREAM;
#if defined(_WIN32) || defined(__APPLE__)
if (const int wait = wait_for_local_address(s, socket, total == 0, flags)) {
if (wait != RECEIVE_NOTHING) return fail(wait);
msg->flags = 0;
if (msg->name) msg->name_length = 0;
msg->control_length = 0;
header.flags = 0;
if (header.name) header.name_length = 0;
header.control_length = 0;
*msg = header;
return 0;
}
#endif
Expand All @@ -1413,19 +1416,21 @@ int64_t APS5_VABI sceNetRecvmsg(int s, NetMsghdr* msg, int flags) {
return fail(native_error());
#endif
}
msg->flags = 0;
header.flags = 0;
if (received > total) {
received = total;
msg->flags = NET_MSG_TRUNC;
header.flags = NET_MSG_TRUNC;
}
std::size_t offset = 0;
for (int i = 0; i < msg->iov_length && offset < static_cast<std::size_t>(received); ++i) {
const auto count = std::min<std::size_t>(msg->iov[i].length, static_cast<std::size_t>(received) - offset);
std::memcpy(msg->iov[i].base, buffer.data() + offset, count);
for (const auto& entry : vectors) {
if (offset >= static_cast<std::size_t>(received)) break;
const auto count = std::min<std::size_t>(entry.length, static_cast<std::size_t>(received) - offset);
std::memcpy(entry.base, buffer.data() + offset, count);
offset += count;
}
if (msg->name && !native_to_guest_address(peer, msg->name, &msg->name_length)) msg->name_length = 0;
msg->control_length = 0;
if (header.name && !native_to_guest_address(peer, header.name, &header.name_length)) header.name_length = 0;
header.control_length = 0;
*msg = header;
return received;
}

Expand Down
39 changes: 39 additions & 0 deletions core/libs/tests/GuestSceNet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,43 @@ static void CheckMessages(int receiver, int sender, const std::array<std::uint8_
Require(threw);
}

static void CheckOverlappingReceiveVectors(int receiver, int sender, const std::array<std::uint8_t, 16>& address) {
constexpr std::size_t page = 0x4000;
std::int64_t physical = 0;
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page, 0, 0, &physical) == 0);
void* blocked = nullptr;
Require(sceKernelMapDirectMemory(&blocked, page, 3, 0, physical, 0) == 0);
Require(sceKernelMprotect(blocked, page, 0) == 0);
char tail[4]{};
NetIovec vectors[2]{};
vectors[1] = {tail, sizeof(tail)};
vectors[0] = {&vectors[1], sizeof(NetIovec)};
const NetIovec redirect{blocked, sizeof(tail)};
char payload[sizeof(NetIovec) + sizeof(tail)];
std::memcpy(payload, &redirect, sizeof(redirect));
std::memcpy(payload + sizeof(redirect), "tail", sizeof(tail));
std::array<std::uint8_t, 16> destination = address;
Require(sceNetSendto(sender, payload, sizeof(payload), 0, destination.data(), destination.size()) == sizeof(payload));
std::array<std::uint8_t, 16> source{};
NetMsghdr message{source.data(), static_cast<std::uint32_t>(source.size()), vectors, 2, nullptr, 0, -1};
Require(sceNetRecvmsg(receiver, &message, 0) == sizeof(payload));
Require(std::memcmp(tail, "tail", sizeof(tail)) == 0);
Require(std::memcmp(&vectors[1], &redirect, sizeof(redirect)) == 0);
Require(message.iov == vectors && message.iov_length == 2 && message.flags == 0 && message.name_length == 16);
Require(sceKernelMunmap(reinterpret_cast<std::uint64_t>(blocked), page) == 0);
Require(sceKernelReleaseDirectMemory(physical, page) == 0);
}

static void CheckEmptyReceiveVectors(int receiver, int sender, const std::array<std::uint8_t, 16>& address) {
std::array<std::uint8_t, 16> destination = address;
Require(sceNetSendto(sender, "x", 1, 0, destination.data(), destination.size()) == 1);
std::array<std::uint8_t, 16> source{};
NetMsghdr message{source.data(), static_cast<std::uint32_t>(source.size()), nullptr, 0, nullptr, 0, -1};
Require(sceNetRecvmsg(receiver, &message, 0) == 0);
Require(message.iov == nullptr && message.iov_length == 0 && message.flags == 0x10 && message.name_length == 16);
Require(source[0] == 16 && source[1] == 2 && source[4] == 127 && source[7] == 1);
}

static void CheckEpollWritable() {
std::array<std::uint8_t, 16> address{16, 2, 0, 0, 127, 0, 0, 1};
const int receiver = sceNetSocket("epoll-receiver", 2, 2, 17);
Expand Down Expand Up @@ -2282,6 +2319,8 @@ int main() {
CheckStreamReceiveTimeout();
CheckMicrosecondTimeouts(udp_receiver);
CheckMessages(udp_receiver, udp_sender, address);
CheckOverlappingReceiveVectors(udp_receiver, udp_sender, address);
CheckEmptyReceiveVectors(udp_receiver, udp_sender, address);
Require(sceNetSocketClose(udp_sender) == 0);
Require(sceNetSocketClose(udp_receiver) == 0);

Expand Down
Loading