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
10 changes: 10 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,16 @@ if(BUILD_TESTING)
set_tests_properties(guest_kernel_errors PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_getrlimit_tests ${LIBS_INCLUDE_DIR}/tests/GuestGetrlimit.cpp)
target_include_directories(guest_getrlimit_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_getrlimit_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_getrlimit_tests)
add_test(NAME guest_getrlimit COMMAND guest_getrlimit_tests)
set_tests_properties(guest_getrlimit PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(guest_getrlimit PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_equeue_event_tests ${LIBS_INCLUDE_DIR}/tests/GuestEqueueEvents.cpp)
target_include_directories(guest_equeue_event_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_equeue_event_tests PRIVATE libkernel libc)
Expand Down
60 changes: 60 additions & 0 deletions core/libs/prx/libkernel/System/src/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,42 @@ void validateSchedulingPolicy(int policy) {
throw std::invalid_argument("Unsupported guest scheduling policy");
}

constexpr int guestFault = 14;
constexpr int guestInvalid = 22;
constexpr int guestRlimitData = 2;
constexpr int guestRlimitCount = 15;
constexpr std::int64_t guestRlimitInfinity = std::numeric_limits<std::int64_t>::max();

#ifdef _WIN32
std::int64_t hostMemoryLimit() {
BOOL inJob = FALSE;
if (!IsProcessInJob(GetCurrentProcess(), nullptr, &inJob))
throw std::system_error(GetLastError(), std::system_category(), "getrlimit: IsProcessInJob failed");
if (!inJob)
return guestRlimitInfinity;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits{};
if (!QueryInformationJobObject(nullptr, JobObjectExtendedLimitInformation, &limits, sizeof(limits), nullptr))
throw std::system_error(GetLastError(), std::system_category(), "getrlimit: QueryInformationJobObject failed");
if ((limits.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) == 0 || limits.ProcessMemoryLimit > static_cast<SIZE_T>(guestRlimitInfinity))
return guestRlimitInfinity;
return static_cast<std::int64_t>(limits.ProcessMemoryLimit);
}
#else
std::int64_t toGuestLimit(rlim_t value) {
if (value == RLIM_INFINITY || value > static_cast<rlim_t>(guestRlimitInfinity))
return guestRlimitInfinity;
return static_cast<std::int64_t>(value);
}
#endif

}

extern "C" int* APS5_VABI __error_nid_postfix();

struct GuestResourceLimit {
std::int64_t rlim_cur;
std::int64_t rlim_max;
};

struct GuestResourceUsage {
KernelTimeval ru_utime;
Expand Down Expand Up @@ -278,6 +313,31 @@ int APS5_VABI getrusage_nid_postfix(int who, GuestResourceUsage* usage) {
return 0;
}

int APS5_VABI getrlimit_nid_postfix(int resource, GuestResourceLimit* limit) {
if (resource < 0 || resource >= guestRlimitCount) {
*__error_nid_postfix() = guestInvalid;
return -1;
}
if (limit == nullptr) {
*__error_nid_postfix() = guestFault;
return -1;
}
if (resource != guestRlimitData)
throw std::runtime_error("getrlimit: unsupported resource " + std::to_string(resource));
#ifdef _WIN32
const std::int64_t memory = hostMemoryLimit();
limit->rlim_cur = memory;
limit->rlim_max = memory;
#else
rlimit native{};
if (::getrlimit(RLIMIT_DATA, &native) != 0)
throw std::system_error(errno, std::generic_category(), "getrlimit: getrlimit failed");
limit->rlim_cur = toGuestLimit(native.rlim_cur);
limit->rlim_max = toGuestLimit(native.rlim_max);
#endif
return 0;
}

int APS5_VABI sceKernelIsTrinityMode(void) {
return 0;
}
Expand Down
90 changes: 90 additions & 0 deletions core/libs/tests/GuestGetrlimit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <stdexcept>
#ifndef _WIN32
#include <sys/resource.h>
#endif

struct ResourceLimit {
std::int64_t rlim_cur;
std::int64_t rlim_max;
};

extern "C" int APS5_VABI getrlimit_nid_postfix(int resource, ResourceLimit* limit);
extern "C" int* APS5_VABI __error_nid_postfix();

static constexpr int GUEST_EFAULT = 14;
static constexpr int GUEST_EINVAL = 22;
static constexpr int GUEST_RLIMIT_DATA = 2;
static constexpr int GUEST_RLIMIT_NOFILE = 8;
static constexpr int GUEST_RLIMIT_AS = 10;
static constexpr int GUEST_RLIM_NLIMITS = 15;
static constexpr std::int64_t GUEST_RLIM_INFINITY = std::numeric_limits<std::int64_t>::max();

static void Require(bool value) { if (!value) std::abort(); }

static ResourceLimit GuestLimit(int resource) {
ResourceLimit limit{-1, -1};
Require(getrlimit_nid_postfix(resource, &limit) == 0);
Require(limit.rlim_cur >= 0 && limit.rlim_cur <= limit.rlim_max);
return limit;
}

static void RequireError(int resource, ResourceLimit* limit, int error) {
*__error_nid_postfix() = 0;
Require(getrlimit_nid_postfix(resource, limit) == -1);
Require(*__error_nid_postfix() == error);
}

#ifndef _WIN32
static std::int64_t Guest(rlim_t value) {
return value == RLIM_INFINITY ? GUEST_RLIM_INFINITY : static_cast<std::int64_t>(value);
}

static void RequireHostLimit(int resource, int hostResource) {
rlimit host{};
Require(getrlimit(hostResource, &host) == 0);
const ResourceLimit guest = GuestLimit(resource);
Require(guest.rlim_cur == Guest(host.rlim_cur));
Require(guest.rlim_max == Guest(host.rlim_max));
}

static void LoweredHostLimitIsReported() {
rlimit host{};
Require(getrlimit(RLIMIT_DATA, &host) == 0);
const rlim_t lowered = rlim_t{1} << 40;
if (host.rlim_max != RLIM_INFINITY && host.rlim_max < lowered) return;
host.rlim_cur = lowered;
Require(setrlimit(RLIMIT_DATA, &host) == 0);
Require(GuestLimit(GUEST_RLIMIT_DATA).rlim_cur == static_cast<std::int64_t>(lowered));
}
#endif

static void RequireUnsupported(int resource) {
ResourceLimit limit{};
bool rejected = false;
try {
getrlimit_nid_postfix(resource, &limit);
} catch (const std::runtime_error&) {
rejected = true;
}
Require(rejected);
}

int main() {
#ifdef _WIN32
GuestLimit(GUEST_RLIMIT_DATA);
#else
RequireHostLimit(GUEST_RLIMIT_DATA, RLIMIT_DATA);
LoweredHostLimitIsReported();
#endif
ResourceLimit limit{};
RequireError(GUEST_RLIM_NLIMITS, &limit, GUEST_EINVAL);
RequireError(-1, &limit, GUEST_EINVAL);
RequireError(GUEST_RLIM_NLIMITS, nullptr, GUEST_EINVAL);
RequireError(GUEST_RLIMIT_DATA, nullptr, GUEST_EFAULT);
RequireUnsupported(GUEST_RLIMIT_NOFILE);
RequireUnsupported(GUEST_RLIMIT_AS);
}
Loading