]> git.saurik.com Git - apple/xnu.git/blame - tests/vm/entitlement_increased_memory_limit.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / vm / entitlement_increased_memory_limit.c
CommitLineData
f427ee49
A
1#include <stdlib.h>
2#include <string.h>
3#include <signal.h>
4#include <spawn.h>
5#include <spawn_private.h>
6
7#include <sys/sysctl.h>
8#include <sys/errno.h>
9#include <sys/kern_memorystatus.h>
10
11#include <crt_externs.h>
12#include <mach-o/dyld.h>
13#include <darwintest.h>
14#include <darwintest_utils.h>
15
16#include "memorystatus_assertion_helpers.h"
17
18#define MAX_TASK_MEM_ENTITLED "kern.entitled_max_task_pmem"
19#define MAX_TASK_MEM "kern.max_task_pmem"
20#define MAX_TASK_MEM_ENTITLED_VALUE (3 * (1 << 10))
21
22#if ENTITLED
23#define TESTNAME entitlement_increased_memory_limit_entitled
24#else /* ENTITLED */
25#define TESTNAME entitlement_increased_memory_limit_unentitled
26#endif /* ENTITLED */
27
28T_GLOBAL_META(T_META_NAMESPACE("xnu.vm"));
29
30static int32_t old_entitled_max_task_pmem = 0;
31
32static void
33reset_old_entitled_max_task_mem()
34{
35 int ret;
36 size_t size_old_entitled_max_task_pmem = sizeof(old_entitled_max_task_pmem);
37 // Use sysctl to change entitled limit
38 ret = sysctlbyname(MAX_TASK_MEM_ENTITLED, NULL, 0, &old_entitled_max_task_pmem, size_old_entitled_max_task_pmem);
39}
40
41T_HELPER_DECL(child, "Child") {
42 // Doesn't do anything. Will start suspended
43 // so that its parent can check its memlimits
44 // and then kill it.
45 T_PASS("Child exiting");
46}
47
48static pid_t
49spawn_child_with_memlimit(int32_t memlimit)
50{
51 posix_spawnattr_t attr;
52 int ret;
53 char **args;
54 char testpath[PATH_MAX];
55 uint32_t testpath_buf_size;
56 pid_t pid;
57
58 ret = posix_spawnattr_init(&attr);
59 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_init");
60
61 testpath_buf_size = sizeof(testpath);
62 ret = _NSGetExecutablePath(testpath, &testpath_buf_size);
63 T_ASSERT_POSIX_ZERO(ret, "_NSGetExecutablePath");
64 T_LOG("Executable path: %s", testpath);
65 args = (char *[]){
66 testpath,
67 "-n",
68 "child",
69 NULL
70 };
71
72 ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED);
73 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setflags() failed");
74 ret = posix_spawnattr_setjetsam_ext(&attr,
75 0, JETSAM_PRIORITY_FOREGROUND, memlimit, memlimit);
76 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawnattr_setjetsam_ext");
77 ret = posix_spawn(&pid, testpath, NULL, &attr, args, *_NSGetEnviron());
78 T_QUIET; T_ASSERT_POSIX_ZERO(ret, "posix_spawn() failed");
79
80 return pid;
81}
82
83
84T_DECL(TESTNAME,
85 "Verify that entitled processes can allocate up to the entitled memory limit",
86 T_META_CHECK_LEAKS(false))
87{
88 int32_t entitled_max_task_pmem = MAX_TASK_MEM_ENTITLED_VALUE, max_task_pmem = 0, expected_limit;
89 size_t size_entitled_max_task_pmem = sizeof(entitled_max_task_pmem);
90 size_t size_old_entitled_max_task_pmem = sizeof(old_entitled_max_task_pmem);
91 size_t size_max_task_pmem = sizeof(max_task_pmem);
92 int status;
93 pid_t pid, rc;
94 bool signaled;
95 memorystatus_memlimit_properties2_t mmprops;
96
97 int ret = 0;
98
99 // Get the unentitled limit
100 ret = sysctlbyname(MAX_TASK_MEM, &max_task_pmem, &size_max_task_pmem, NULL, 0);
101 T_ASSERT_POSIX_SUCCESS(ret, "call sysctlbyname to get max task physical memory.");
102 if (max_task_pmem >= MAX_TASK_MEM_ENTITLED_VALUE) {
103 T_SKIP("max_task_pmem (%lld) is larger than entitled value (%lld). Skipping test on this device.", max_task_pmem, MAX_TASK_MEM_ENTITLED_VALUE);
104 }
105
106 // Use sysctl to change entitled limit
107 ret = sysctlbyname(MAX_TASK_MEM_ENTITLED, &old_entitled_max_task_pmem, &size_old_entitled_max_task_pmem, &entitled_max_task_pmem, size_entitled_max_task_pmem);
108 T_ASSERT_POSIX_SUCCESS(ret, "call sysctlbyname to set entitled hardware mem size.");
109
110 T_ATEND(reset_old_entitled_max_task_mem);
111
112 /*
113 * Spawn child with the normal task limit (just as launchd does for an app)
114 * The child will start suspended, so we can check its memlimit.
115 */
116
117 pid = spawn_child_with_memlimit(max_task_pmem);
118 T_ASSERT_POSIX_SUCCESS(pid, "spawn child with task limit");
119
120 // Check its memlimt
121 ret = memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES, pid, 0, &mmprops, sizeof(mmprops));
122 T_ASSERT_POSIX_SUCCESS(ret, "memorystatus_control");
123#if ENTITLED
124 expected_limit = MAX_TASK_MEM_ENTITLED_VALUE;
125#else /* ENTITLED */
126 expected_limit = max_task_pmem;
127#endif /* ENTITLED */
128 T_ASSERT_EQ(mmprops.v1.memlimit_active, expected_limit, "active limit");
129 T_ASSERT_EQ(mmprops.v1.memlimit_inactive, expected_limit, "inactive limit");
130
131 // Resume the child. It should exit immediately.
132 ret = kill(pid, SIGCONT);
133 T_ASSERT_POSIX_SUCCESS(ret, "kill child");
134
135 // Check child's exit code.
136 while (true) {
137 rc = waitpid(pid, &status, 0);
138 if (rc == -1 && errno == EINTR) {
139 continue;
140 }
141 T_ASSERT_EQ(rc, pid, "waitpid");
142 signaled = WIFSIGNALED(status);
143 T_ASSERT_FALSE(signaled, "Child exited cleanly");
144 ret = WEXITSTATUS(status);
145 T_ASSERT_EQ(ret, 0, "child exited with code 0.");
146 break;
147 }
148}