5 #include <spawn_private.h>
7 #include <sys/sysctl.h>
9 #include <sys/kern_memorystatus.h>
11 #include <crt_externs.h>
12 #include <mach-o/dyld.h>
13 #include <darwintest.h>
14 #include <darwintest_utils.h>
16 #include "memorystatus_assertion_helpers.h"
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))
23 #define TESTNAME entitlement_increased_memory_limit_entitled
25 #define TESTNAME entitlement_increased_memory_limit_unentitled
28 T_GLOBAL_META(T_META_NAMESPACE("xnu.vm"));
30 static int32_t old_entitled_max_task_pmem
= 0;
33 reset_old_entitled_max_task_mem()
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
);
41 T_HELPER_DECL(child
, "Child") {
42 // Doesn't do anything. Will start suspended
43 // so that its parent can check its memlimits
45 T_PASS("Child exiting");
49 spawn_child_with_memlimit(int32_t memlimit
)
51 posix_spawnattr_t attr
;
54 char testpath
[PATH_MAX
];
55 uint32_t testpath_buf_size
;
58 ret
= posix_spawnattr_init(&attr
);
59 T_QUIET
; T_ASSERT_POSIX_ZERO(ret
, "posix_spawnattr_init");
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
);
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");
85 "Verify that entitled processes can allocate up to the entitled memory limit",
86 T_META_CHECK_LEAKS(false))
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
);
95 memorystatus_memlimit_properties2_t mmprops
;
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
);
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.");
110 T_ATEND(reset_old_entitled_max_task_mem
);
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.
117 pid
= spawn_child_with_memlimit(max_task_pmem
);
118 T_ASSERT_POSIX_SUCCESS(pid
, "spawn child with task limit");
121 ret
= memorystatus_control(MEMORYSTATUS_CMD_GET_MEMLIMIT_PROPERTIES
, pid
, 0, &mmprops
, sizeof(mmprops
));
122 T_ASSERT_POSIX_SUCCESS(ret
, "memorystatus_control");
124 expected_limit
= MAX_TASK_MEM_ENTITLED_VALUE
;
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");
131 // Resume the child. It should exit immediately.
132 ret
= kill(pid
, SIGCONT
);
133 T_ASSERT_POSIX_SUCCESS(ret
, "kill child");
135 // Check child's exit code.
137 rc
= waitpid(pid
, &status
, 0);
138 if (rc
== -1 && errno
== EINTR
) {
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.");