Skip to content
Open
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
14 changes: 14 additions & 0 deletions core/libs/prx/libSceNet/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,20 @@ if(BUILD_TESTING)
endforeach()
endforeach()
endif()
add_test_executable(guest_net_address_fault_tests ${LIBS_INCLUDE_DIR}/tests/GuestNetAddressFaults.cpp)
target_include_directories(guest_net_address_fault_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_net_address_fault_tests PRIVATE libSceNet libkernel libc)
configure_windows_unwind(guest_net_address_fault_tests)
foreach(api IN ITEMS bind connect)
foreach(readable IN ITEMS 0 1 4 8)
add_test(NAME guest_net_address_fault_${api}_${readable} COMMAND guest_net_address_fault_tests ${api} ${readable})
set_tests_properties(guest_net_address_fault_${api}_${readable} PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_net_address_fault_${api}_${readable} PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNet>")
endif()
endforeach()
endforeach()
add_test_executable(guest_sce_net_tests ${LIBS_INCLUDE_DIR}/tests/GuestSceNet.cpp)
target_include_directories(guest_sce_net_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_sce_net_tests PRIVATE libSceNet libSceNetCtl libkernel libc)
Expand Down
18 changes: 15 additions & 3 deletions core/libs/prx/libSceNet/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ struct NativeSocketHandle : KernelSocketPoll::NativeOwner {
}
};

int ReadGuestAddress(const void* address, std::uint32_t length,
std::array<std::uint8_t, sizeof(sockaddr_storage)>& snapshot) {
if (!address || length < 2) return NET_EINVAL;
if (!GuestSocketReadGuest_nid_no_patch(snapshot.data(), address, std::min<std::size_t>(length, snapshot.size())))
return NET_EFAULT;
return 0;
}

bool guest_to_native_address(const void* address, std::uint32_t length,
sockaddr_storage& native, NativeLength& native_length) {
if (!address || length < 2) return false;
Expand Down Expand Up @@ -1004,9 +1012,11 @@ int APS5_VABI sceNetBind_nid_postfix(int s, const void* addr, uint32_t addrlen)
socket = it->second;
if (it->second.bound) return fail(NET_EINVAL);
}
std::array<std::uint8_t, sizeof(sockaddr_storage)> address{};
if (const int error = ReadGuestAddress(addr, addrlen, address)) return fail(error);
sockaddr_storage native{};
NativeLength native_length = 0;
if (!guest_to_native_address(addr, addrlen, native, native_length)) return fail(NET_EINVAL);
if (!guest_to_native_address(address.data(), addrlen, native, native_length)) return fail(NET_EINVAL);
if (native.ss_family != (socket.family == NET_AF_INET ? AF_INET : AF_INET6)) return fail(NET_EAFNOSUPPORT);
if (::bind(socket.native->value, reinterpret_cast<const sockaddr*>(&native), native_length) != 0)
return fail(native_error());
Expand Down Expand Up @@ -1137,13 +1147,15 @@ int APS5_VABI sceNetConnect(int s, const void* addr, uint32_t addrlen) {
it->second.reported_error = 0;
socket = it->second;
}
if (socket.type == NET_SOCK_DGRAM && addr && addrlen >= 2 && static_cast<const std::uint8_t*>(addr)[1] == 0) {
std::array<std::uint8_t, sizeof(sockaddr_storage)> address{};
if (const int error = ReadGuestAddress(addr, addrlen, address)) return fail(error);
if (socket.type == NET_SOCK_DGRAM && address[1] == 0) {
if (const int error = disconnect_datagram(socket)) return error;
return fail(NET_EAFNOSUPPORT);
}
sockaddr_storage native{};
NativeLength native_length = 0;
if (!guest_to_native_address(addr, addrlen, native, native_length)) return fail(NET_EINVAL);
if (!guest_to_native_address(address.data(), addrlen, native, native_length)) return fail(NET_EINVAL);
#ifdef _WIN32
if (socket.type == NET_SOCK_STREAM) RefuseClosedLoopbackAtOnce_nid_no_patch(socket.native->value, reinterpret_cast<const sockaddr*>(&native));
#endif
Expand Down
85 changes: 85 additions & 0 deletions core/libs/tests/GuestNetAddressFaults.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "prx/libkernel/include/Api.hpp"
#include "prx/libc/include/Api.hpp"
#include "prx/libSceNet/include/Net.hpp"
#include <array>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <source_location>

static void Require(bool condition, std::source_location location = std::source_location::current()) {
if (!condition) {
std::fprintf(stderr, "check failed at line %u\n", location.line());
std::abort();
}
}

static void Failed(int result, int error) {
Require(result == static_cast<int>(0x80410100u | static_cast<unsigned>(error)));
Require(*sceNetErrnoLoc() == error);
}

int main(int argc, char** argv) {
Require(argc == 3);
const bool connect = std::strcmp(argv[1], "connect") == 0;
const int readable = std::atoi(argv[2]);
constexpr std::size_t page = 16384;
Require(sceNetInit_nid_postfix() == 0);
std::int64_t physical = 0;
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 2, 0, 0, &physical) == 0);
void* mapping = nullptr;
Require(sceKernelMapDirectMemory(&mapping, page * 2, 3, 0, physical, 0) == 0);
for (const int family : {2, 28}) {
const std::uint32_t size = family == 2 ? 16 : 28;
std::array<std::uint8_t, 28> address{};
address[0] = size;
address[1] = family;
if (family == 2) {
address[4] = 127;
address[7] = 1;
} else {
address[23] = 1;
}
const int peer = sceNetSocket("address-peer", family, 2, 17);
const int socket = sceNetSocket("address-fault", family, 2, 17);
Require(peer >= 0 && socket >= 0);
Require(sceNetBind_nid_postfix(peer, address.data(), size) == 0);
std::uint32_t actual = size;
Require(sceNetGetsockname(peer, address.data(), &actual) == 0);
const auto call = [&](int descriptor, const void* pointer, std::uint32_t length) {
return connect ? sceNetConnect(descriptor, pointer, length) : sceNetBind_nid_postfix(descriptor, pointer, length);
};
if (connect) Require(call(socket, address.data(), size) == 0);
auto* boundary = static_cast<std::uint8_t*>(mapping) + page;
const int prefix = readable == 8 ? size - 1 : readable;
auto* fault = boundary - prefix;
std::memcpy(fault, address.data(), size);
Require(sceKernelMprotect(boundary, page, 0) == 0);
Failed(call(-1, fault, size), 9);
Failed(call(socket, nullptr, size), 22);
Failed(call(socket, fault, 0), 22);
Failed(call(socket, fault, 1), 22);
Failed(call(socket, fault, size), 14);
if (connect) {
if (prefix >= 2) {
fault[1] = 0;
Failed(call(socket, fault, size), 14);
}
std::array<std::uint8_t, 28> connected{};
actual = size;
Require(sceNetGetpeername(socket, connected.data(), &actual) == 0);
Require(std::memcmp(connected.data(), address.data(), size) == 0);
}
Require(sceKernelMprotect(boundary, page, 3) == 0);
if (!connect) {
address[2] = 0;
address[3] = 0;
}
Require(call(socket, address.data(), size) == 0);
Require(sceNetSocketClose(socket) == 0);
Require(sceNetSocketClose(peer) == 0);
}
Require(sceKernelMunmap(reinterpret_cast<std::uint64_t>(mapping), page * 2) == 0);
Require(sceKernelReleaseDirectMemory(physical, page * 2) == 0);
}
Loading