+ int error = 0;
+ int num_items_to_copy = 0;
+ uint32_t user_data_to_copy = 0;
+ char *reason_user_desc = NULL;
+ size_t reason_user_desc_len = 0;
+
+ exit_reason = os_reason_create(reason_namespace, reason_code);
+ if (exit_reason == OS_REASON_NULL) {
+ printf("build_userspace_exit_reason: failed to allocate exit reason\n");
+ return exit_reason;
+ }
+
+ exit_reason->osr_flags |= OS_REASON_FLAG_FROM_USERSPACE;
+
+ /*
+ * Only apply flags that are allowed to be passed from userspace.
+ */
+ exit_reason->osr_flags |= (reason_flags & OS_REASON_FLAG_MASK_ALLOWED_FROM_USER);
+ if ((reason_flags & OS_REASON_FLAG_MASK_ALLOWED_FROM_USER) != reason_flags) {
+ printf("build_userspace_exit_reason: illegal flags passed from userspace (some masked off) 0x%llx, ns: %u, code 0x%llx\n",
+ reason_flags, reason_namespace, reason_code);
+ }
+
+ if (!(exit_reason->osr_flags & OS_REASON_FLAG_NO_CRASH_REPORT)) {
+ exit_reason->osr_flags |= OS_REASON_FLAG_GENERATE_CRASH_REPORT;
+ }
+
+ if (payload != USER_ADDR_NULL) {
+ if (payload_size == 0) {
+ printf("build_userspace_exit_reason: exit reason with namespace %u, nonzero payload but zero length\n",
+ reason_namespace);
+ exit_reason->osr_flags |= OS_REASON_FLAG_BAD_PARAMS;
+ payload = USER_ADDR_NULL;
+ } else {
+ num_items_to_copy++;
+
+ if (payload_size > EXIT_REASON_PAYLOAD_MAX_LEN) {
+ exit_reason->osr_flags |= OS_REASON_FLAG_PAYLOAD_TRUNCATED;
+ payload_size = EXIT_REASON_PAYLOAD_MAX_LEN;
+ }
+
+ user_data_to_copy += payload_size;
+ }
+ }
+
+ if (reason_string != USER_ADDR_NULL) {
+ reason_user_desc = (char *) kalloc(EXIT_REASON_USER_DESC_MAX_LEN);
+
+ if (reason_user_desc != NULL) {
+ error = copyinstr(reason_string, (void *) reason_user_desc,
+ EXIT_REASON_USER_DESC_MAX_LEN, &reason_user_desc_len);
+
+ if (error == 0) {
+ num_items_to_copy++;
+ user_data_to_copy += reason_user_desc_len;
+ } else if (error == ENAMETOOLONG) {
+ num_items_to_copy++;
+ reason_user_desc[EXIT_REASON_USER_DESC_MAX_LEN - 1] = '\0';
+ user_data_to_copy += reason_user_desc_len;
+ } else {
+ exit_reason->osr_flags |= OS_REASON_FLAG_FAILED_DATA_COPYIN;
+ kfree(reason_user_desc, EXIT_REASON_USER_DESC_MAX_LEN);
+ reason_user_desc = NULL;
+ reason_user_desc_len = 0;
+ }
+ }
+ }
+
+ if (num_items_to_copy != 0) {
+ uint32_t reason_buffer_size_estimate = 0;
+ mach_vm_address_t data_addr = 0;
+
+ reason_buffer_size_estimate = kcdata_estimate_required_buffer_size(num_items_to_copy, user_data_to_copy);
+
+ error = os_reason_alloc_buffer(exit_reason, reason_buffer_size_estimate);
+ if (error != 0) {
+ printf("build_userspace_exit_reason: failed to allocate signal reason buffer\n");
+ goto out_failed_copyin;
+ }
+
+ if (reason_user_desc != NULL && reason_user_desc_len != 0) {
+ if (KERN_SUCCESS == kcdata_get_memory_addr(&exit_reason->osr_kcd_descriptor,
+ EXIT_REASON_USER_DESC,
+ reason_user_desc_len,
+ &data_addr)) {
+
+ kcdata_memcpy(&exit_reason->osr_kcd_descriptor, (mach_vm_address_t) data_addr,
+ reason_user_desc, reason_user_desc_len);
+ } else {
+ printf("build_userspace_exit_reason: failed to allocate space for reason string\n");
+ goto out_failed_copyin;
+ }
+ }
+
+ if (payload != USER_ADDR_NULL) {
+ if (KERN_SUCCESS ==
+ kcdata_get_memory_addr(&exit_reason->osr_kcd_descriptor,
+ EXIT_REASON_USER_PAYLOAD,
+ payload_size,
+ &data_addr)) {
+ error = copyin(payload, (void *) data_addr, payload_size);
+ if (error) {
+ printf("build_userspace_exit_reason: failed to copy in payload data with error %d\n", error);
+ goto out_failed_copyin;
+ }
+ } else {
+ printf("build_userspace_exit_reason: failed to allocate space for payload data\n");
+ goto out_failed_copyin;
+ }
+ }
+ }
+
+ if (reason_user_desc != NULL) {
+ kfree(reason_user_desc, EXIT_REASON_USER_DESC_MAX_LEN);
+ reason_user_desc = NULL;
+ reason_user_desc_len = 0;
+ }
+
+ return exit_reason;
+
+out_failed_copyin:
+
+ if (reason_user_desc != NULL) {
+ kfree(reason_user_desc, EXIT_REASON_USER_DESC_MAX_LEN);
+ reason_user_desc = NULL;
+ reason_user_desc_len = 0;
+ }
+
+ exit_reason->osr_flags |= OS_REASON_FLAG_FAILED_DATA_COPYIN;
+ os_reason_alloc_buffer(exit_reason, 0);
+ return exit_reason;
+}
+
+static int
+terminate_with_payload_internal(struct proc *cur_proc, int target_pid, uint32_t reason_namespace,
+ uint64_t reason_code, user_addr_t payload, uint32_t payload_size,
+ user_addr_t reason_string, uint64_t reason_flags)
+{
+ proc_t target_proc = PROC_NULL;
+ kauth_cred_t cur_cred = kauth_cred_get();
+
+ os_reason_t signal_reason = OS_REASON_NULL;
+
+ AUDIT_ARG(pid, target_pid);
+ if ((target_pid <= 0)) {
+ return EINVAL;
+ }
+
+ target_proc = proc_find(target_pid);
+ if (target_proc == PROC_NULL) {
+ return ESRCH;
+ }
+
+ AUDIT_ARG(process, target_proc);
+
+ if (!cansignal(cur_proc, cur_cred, target_proc, SIGKILL, 0)) {
+ proc_rele(target_proc);
+ return EPERM;
+ }
+
+ KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXITREASON_CREATE) | DBG_FUNC_NONE,
+ target_proc->p_pid, reason_namespace,
+ reason_code, 0, 0);
+
+ signal_reason = build_userspace_exit_reason(reason_namespace, reason_code, payload, payload_size,
+ reason_string, reason_flags);
+
+ if (target_pid == cur_proc->p_pid) {
+ /*
+ * psignal_thread_with_reason() will pend a SIGKILL on the specified thread or
+ * return if the thread and/or task are already terminating. Either way, the
+ * current thread won't return to userspace.
+ */
+ psignal_thread_with_reason(target_proc, current_thread(), SIGKILL, signal_reason);
+ } else {
+ psignal_with_reason(target_proc, SIGKILL, signal_reason);
+ }
+
+ proc_rele(target_proc);
+
+ return 0;
+}
+
+int
+terminate_with_payload(struct proc *cur_proc, struct terminate_with_payload_args *args,
+ __unused int32_t *retval)
+{
+ return terminate_with_payload_internal(cur_proc, args->pid, args->reason_namespace, args->reason_code, args->payload,
+ args->payload_size, args->reason_string, args->reason_flags);
+}
+
+static int
+killpg1_filt(proc_t p, void * arg)
+{
+ struct killpg1_filtargs * kfargp = (struct killpg1_filtargs *)arg;
+ proc_t cp = kfargp->cp;
+ int posix = kfargp->posix;
+
+
+ if (p->p_pid <= 1 || p->p_flag & P_SYSTEM ||
+ (!posix && p == cp))
+ return(0);
+ else
+ return(1);
+}
+
+
+static int
+killpg1_pgrpfilt(proc_t p, __unused void * arg)
+{
+ if (p->p_pid <= 1 || p->p_flag & P_SYSTEM ||
+ (p->p_stat == SZOMB))
+ return(0);
+ else
+ return(1);
+}
+
+
+
+static int
+killpg1_callback(proc_t p, void * arg)
+{
+ struct killpg1_iterargs * kargp = (struct killpg1_iterargs *)arg;
+ proc_t cp = kargp->cp;
+ kauth_cred_t uc = kargp->uc; /* refcounted by the caller safe to use internal fields */
+ int signum = kargp->signum;
+ int * nfoundp = kargp->nfoundp;
+ int n;
+ int zombie = 0;
+ int error = 0;
+
+ if ((kargp->zombie != 0) && ((p->p_listflag & P_LIST_EXITED) == P_LIST_EXITED))
+ zombie = 1;
+
+ if (zombie != 0) {
+ proc_list_lock();
+ error = cansignal(cp, uc, p, signum, zombie);
+ proc_list_unlock();
+
+ if (error != 0 && nfoundp != NULL) {
+ n = *nfoundp;
+ *nfoundp = n+1;
+ }
+ } else {
+ if (cansignal(cp, uc, p, signum, 0) == 0)
+ return(PROC_RETURNED);
+
+ if (nfoundp != NULL) {
+ n = *nfoundp;
+ *nfoundp = n+1;
+ }
+ if (signum != 0)
+ psignal(p, signum);
+ }
+
+ return(PROC_RETURNED);
+}
+
+/*
+ * Common code for kill process group/broadcast kill.
+ * cp is calling process.