+ *ret = KERN_INVALID_ARGUMENT;
+ return -1;
+ }
+}
+
+static bool
+rate_limit_host_statistics(bool is_stat64, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t* count, kern_return_t* ret, int *pindex)
+{
+ task_t task = current_task();
+
+ assert(task != kernel_task);
+
+ *ret = KERN_SUCCESS;
+
+ /* Access control only for third party applications */
+ if (task->t_flags & TF_PLATFORM) {
+ return FALSE;
+ }
+
+ /* Rate limit to HOST_STATISTICS_MAX_REQUESTS queries for each HOST_STATISTICS_TIME_WINDOW window of time */
+ bool rate_limited = FALSE;
+ bool set_last_access = TRUE;
+
+ /* there is a cache for every flavor */
+ int index = get_host_info_data_index(is_stat64, flavor, count, ret);
+ if (index == -1) {
+ goto out;
+ }
+
+ *pindex = index;
+ lck_mtx_lock(&host_statistics_lck);
+ if (g_host_stats_cache[index].last_access > mach_continuous_time() - host_statistics_time_window) {
+ set_last_access = FALSE;
+ if (g_host_stats_cache[index].current_requests++ >= g_host_stats_cache[index].max_requests) {
+ rate_limited = TRUE;
+ get_cached_info(index, info, count);
+ }
+ }
+ if (set_last_access) {
+ g_host_stats_cache[index].current_requests = 1;
+ /*
+ * select a random number of requests (included between HOST_STATISTICS_MIN_REQUESTS and HOST_STATISTICS_MAX_REQUESTS)
+ * to let query host_statistics.
+ * In this way it is not possible to infer looking at when the a cached copy changes if host_statistics was called on
+ * the provious window.
+ */
+ g_host_stats_cache[index].max_requests = (mach_absolute_time() % (HOST_STATISTICS_MAX_REQUESTS - HOST_STATISTICS_MIN_REQUESTS + 1)) + HOST_STATISTICS_MIN_REQUESTS;
+ g_host_stats_cache[index].last_access = mach_continuous_time();
+ }
+ lck_mtx_unlock(&host_statistics_lck);
+out:
+ return rate_limited;
+}
+
+kern_return_t host_statistics64(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_number_t * count);
+
+kern_return_t
+host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count)
+{
+ uint32_t i;
+
+ if (host == HOST_NULL) {
+ return KERN_INVALID_HOST;
+ }
+
+ switch (flavor) {
+ case HOST_VM_INFO64: /* We were asked to get vm_statistics64 */
+ {
+ processor_t processor;
+ vm_statistics64_t stat;
+ vm_statistics64_data_t host_vm_stat;
+ mach_msg_type_number_t original_count;
+ unsigned int local_q_internal_count;
+ unsigned int local_q_external_count;
+
+ if (*count < HOST_VM_INFO64_REV0_COUNT) {
+ return KERN_FAILURE;
+ }
+
+ processor = processor_list;
+ stat = &PROCESSOR_DATA(processor, vm_stat);
+ host_vm_stat = *stat;
+
+ if (processor_count > 1) {
+ simple_lock(&processor_list_lock, LCK_GRP_NULL);
+
+ while ((processor = processor->processor_list) != NULL) {
+ stat = &PROCESSOR_DATA(processor, vm_stat);
+
+ host_vm_stat.zero_fill_count += stat->zero_fill_count;
+ host_vm_stat.reactivations += stat->reactivations;
+ host_vm_stat.pageins += stat->pageins;
+ host_vm_stat.pageouts += stat->pageouts;
+ host_vm_stat.faults += stat->faults;
+ host_vm_stat.cow_faults += stat->cow_faults;
+ host_vm_stat.lookups += stat->lookups;
+ host_vm_stat.hits += stat->hits;
+ host_vm_stat.compressions += stat->compressions;
+ host_vm_stat.decompressions += stat->decompressions;
+ host_vm_stat.swapins += stat->swapins;
+ host_vm_stat.swapouts += stat->swapouts;
+ }
+
+ simple_unlock(&processor_list_lock);
+ }
+
+ stat = (vm_statistics64_t)info;
+
+ stat->free_count = vm_page_free_count + vm_page_speculative_count;
+ stat->active_count = vm_page_active_count;
+
+ local_q_internal_count = 0;
+ local_q_external_count = 0;
+ if (vm_page_local_q) {
+ for (i = 0; i < vm_page_local_q_count; i++) {
+ struct vpl * lq;
+
+ lq = &vm_page_local_q[i].vpl_un.vpl;
+
+ stat->active_count += lq->vpl_count;
+ local_q_internal_count += lq->vpl_internal_count;
+ local_q_external_count += lq->vpl_external_count;
+ }
+ }
+ stat->inactive_count = vm_page_inactive_count;
+#if CONFIG_EMBEDDED
+ stat->wire_count = vm_page_wire_count;
+#else
+ stat->wire_count = vm_page_wire_count + vm_page_throttled_count + vm_lopage_free_count;
+#endif
+ stat->zero_fill_count = host_vm_stat.zero_fill_count;
+ stat->reactivations = host_vm_stat.reactivations;
+ stat->pageins = host_vm_stat.pageins;
+ stat->pageouts = host_vm_stat.pageouts;
+ stat->faults = host_vm_stat.faults;
+ stat->cow_faults = host_vm_stat.cow_faults;
+ stat->lookups = host_vm_stat.lookups;
+ stat->hits = host_vm_stat.hits;
+
+ stat->purgeable_count = vm_page_purgeable_count;
+ stat->purges = vm_page_purged_count;
+
+ stat->speculative_count = vm_page_speculative_count;
+
+ /*
+ * Fill in extra info added in later revisions of the
+ * vm_statistics data structure. Fill in only what can fit
+ * in the data structure the caller gave us !
+ */
+ original_count = *count;
+ *count = HOST_VM_INFO64_REV0_COUNT; /* rev0 already filled in */
+ if (original_count >= HOST_VM_INFO64_REV1_COUNT) {
+ /* rev1 added "throttled count" */
+ stat->throttled_count = vm_page_throttled_count;
+ /* rev1 added "compression" info */
+ stat->compressor_page_count = VM_PAGE_COMPRESSOR_COUNT;
+ stat->compressions = host_vm_stat.compressions;
+ stat->decompressions = host_vm_stat.decompressions;
+ stat->swapins = host_vm_stat.swapins;
+ stat->swapouts = host_vm_stat.swapouts;
+ /* rev1 added:
+ * "external page count"
+ * "anonymous page count"
+ * "total # of pages (uncompressed) held in the compressor"
+ */
+ stat->external_page_count = (vm_page_pageable_external_count + local_q_external_count);
+ stat->internal_page_count = (vm_page_pageable_internal_count + local_q_internal_count);
+ stat->total_uncompressed_pages_in_compressor = c_segment_pages_compressed;
+ *count = HOST_VM_INFO64_REV1_COUNT;
+ }
+
+ return KERN_SUCCESS;
+ }
+
+ case HOST_EXTMOD_INFO64: /* We were asked to get vm_statistics64 */
+ {
+ vm_extmod_statistics_t out_extmod_statistics;
+
+ if (*count < HOST_EXTMOD_INFO64_COUNT) {
+ return KERN_FAILURE;
+ }
+
+ out_extmod_statistics = (vm_extmod_statistics_t)info;
+ *out_extmod_statistics = host_extmod_statistics;
+
+ *count = HOST_EXTMOD_INFO64_COUNT;
+
+ return KERN_SUCCESS;
+ }
+
+ default: /* If we didn't recognize the flavor, send to host_statistics */
+ return host_statistics(host, flavor, (host_info_t)info, count);
+ }
+}
+
+kern_return_t
+host_statistics64_from_user(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count)
+{
+ kern_return_t ret = KERN_SUCCESS;
+ int index;
+
+ if (host == HOST_NULL) {
+ return KERN_INVALID_HOST;
+ }
+
+ if (rate_limit_host_statistics(TRUE, flavor, info, count, &ret, &index)) {
+ return ret;
+ }
+
+ if (ret != KERN_SUCCESS) {
+ return ret;
+ }
+
+ ret = host_statistics64(host, flavor, info, count);
+
+ if (ret == KERN_SUCCESS) {
+ cache_host_statistics(index, info);
+ }
+
+ return ret;
+}
+
+kern_return_t
+host_statistics_from_user(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count)
+{
+ kern_return_t ret = KERN_SUCCESS;
+ int index;
+
+ if (host == HOST_NULL) {
+ return KERN_INVALID_HOST;
+ }
+
+ if (rate_limit_host_statistics(FALSE, flavor, info, count, &ret, &index)) {
+ return ret;