Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

VMCI: Fix integer overflow in VMCI handle arrays

The VMCI handle array has an integer overflow in
vmci_handle_arr_append_entry when it tries to expand the array. This can be
triggered from a guest, since the doorbell link hypercall doesn't impose a
limit on the number of doorbell handles that a VM can create in the
hypervisor, and these handles are stored in a handle array.

In this change, we introduce a mandatory max capacity for handle
arrays/lists to avoid excessive memory usage.

Signed-off-by: Vishnu Dasa <vdasa@vmware.com>
Reviewed-by: Adit Ranadive <aditr@vmware.com>
Reviewed-by: Jorgen Hansen <jhansen@vmware.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Vishnu DASA and committed by
Greg Kroah-Hartman
1c2eb5b2 84705f9f

+99 -59
+45 -35
drivers/misc/vmw_vmci/vmci_context.c
··· 21 21 #include "vmci_driver.h" 22 22 #include "vmci_event.h" 23 23 24 + /* Use a wide upper bound for the maximum contexts. */ 25 + #define VMCI_MAX_CONTEXTS 2000 26 + 24 27 /* 25 28 * List of current VMCI contexts. Contexts can be added by 26 29 * vmci_ctx_create() and removed via vmci_ctx_destroy(). ··· 120 117 /* Initialize host-specific VMCI context. */ 121 118 init_waitqueue_head(&context->host_context.wait_queue); 122 119 123 - context->queue_pair_array = vmci_handle_arr_create(0); 120 + context->queue_pair_array = 121 + vmci_handle_arr_create(0, VMCI_MAX_GUEST_QP_COUNT); 124 122 if (!context->queue_pair_array) { 125 123 error = -ENOMEM; 126 124 goto err_free_ctx; 127 125 } 128 126 129 - context->doorbell_array = vmci_handle_arr_create(0); 127 + context->doorbell_array = 128 + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); 130 129 if (!context->doorbell_array) { 131 130 error = -ENOMEM; 132 131 goto err_free_qp_array; 133 132 } 134 133 135 - context->pending_doorbell_array = vmci_handle_arr_create(0); 134 + context->pending_doorbell_array = 135 + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); 136 136 if (!context->pending_doorbell_array) { 137 137 error = -ENOMEM; 138 138 goto err_free_db_array; ··· 210 204 * We create an array to hold the subscribers we find when 211 205 * scanning through all contexts. 212 206 */ 213 - subscriber_array = vmci_handle_arr_create(0); 207 + subscriber_array = vmci_handle_arr_create(0, VMCI_MAX_CONTEXTS); 214 208 if (subscriber_array == NULL) 215 209 return VMCI_ERROR_NO_MEM; 216 210 ··· 629 623 630 624 spin_lock(&context->lock); 631 625 632 - list_for_each_entry(n, &context->notifier_list, node) { 633 - if (vmci_handle_is_equal(n->handle, notifier->handle)) { 634 - exists = true; 635 - break; 626 + if (context->n_notifiers < VMCI_MAX_CONTEXTS) { 627 + list_for_each_entry(n, &context->notifier_list, node) { 628 + if (vmci_handle_is_equal(n->handle, notifier->handle)) { 629 + exists = true; 630 + break; 631 + } 636 632 } 637 - } 638 633 639 - if (exists) { 640 - kfree(notifier); 641 - result = VMCI_ERROR_ALREADY_EXISTS; 634 + if (exists) { 635 + kfree(notifier); 636 + result = VMCI_ERROR_ALREADY_EXISTS; 637 + } else { 638 + list_add_tail_rcu(&notifier->node, 639 + &context->notifier_list); 640 + context->n_notifiers++; 641 + result = VMCI_SUCCESS; 642 + } 642 643 } else { 643 - list_add_tail_rcu(&notifier->node, &context->notifier_list); 644 - context->n_notifiers++; 645 - result = VMCI_SUCCESS; 644 + kfree(notifier); 645 + result = VMCI_ERROR_NO_MEM; 646 646 } 647 647 648 648 spin_unlock(&context->lock); ··· 733 721 u32 *buf_size, void **pbuf) 734 722 { 735 723 struct dbell_cpt_state *dbells; 736 - size_t n_doorbells; 737 - int i; 724 + u32 i, n_doorbells; 738 725 739 726 n_doorbells = vmci_handle_arr_get_size(context->doorbell_array); 740 727 if (n_doorbells > 0) { ··· 871 860 spin_lock(&context->lock); 872 861 873 862 *db_handle_array = context->pending_doorbell_array; 874 - context->pending_doorbell_array = vmci_handle_arr_create(0); 863 + context->pending_doorbell_array = 864 + vmci_handle_arr_create(0, VMCI_MAX_GUEST_DOORBELL_COUNT); 875 865 if (!context->pending_doorbell_array) { 876 866 context->pending_doorbell_array = *db_handle_array; 877 867 *db_handle_array = NULL; ··· 954 942 return VMCI_ERROR_NOT_FOUND; 955 943 956 944 spin_lock(&context->lock); 957 - if (!vmci_handle_arr_has_entry(context->doorbell_array, handle)) { 958 - vmci_handle_arr_append_entry(&context->doorbell_array, handle); 959 - result = VMCI_SUCCESS; 960 - } else { 945 + if (!vmci_handle_arr_has_entry(context->doorbell_array, handle)) 946 + result = vmci_handle_arr_append_entry(&context->doorbell_array, 947 + handle); 948 + else 961 949 result = VMCI_ERROR_DUPLICATE_ENTRY; 962 - } 963 950 964 951 spin_unlock(&context->lock); 965 952 vmci_ctx_put(context); ··· 1094 1083 if (!vmci_handle_arr_has_entry( 1095 1084 dst_context->pending_doorbell_array, 1096 1085 handle)) { 1097 - vmci_handle_arr_append_entry( 1086 + result = vmci_handle_arr_append_entry( 1098 1087 &dst_context->pending_doorbell_array, 1099 1088 handle); 1100 - 1101 - ctx_signal_notify(dst_context); 1102 - wake_up(&dst_context->host_context.wait_queue); 1103 - 1089 + if (result == VMCI_SUCCESS) { 1090 + ctx_signal_notify(dst_context); 1091 + wake_up(&dst_context->host_context.wait_queue); 1092 + } 1093 + } else { 1094 + result = VMCI_SUCCESS; 1104 1095 } 1105 - result = VMCI_SUCCESS; 1106 1096 } 1107 1097 spin_unlock(&dst_context->lock); 1108 1098 } ··· 1130 1118 if (context == NULL || vmci_handle_is_invalid(handle)) 1131 1119 return VMCI_ERROR_INVALID_ARGS; 1132 1120 1133 - if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle)) { 1134 - vmci_handle_arr_append_entry(&context->queue_pair_array, 1135 - handle); 1136 - result = VMCI_SUCCESS; 1137 - } else { 1121 + if (!vmci_handle_arr_has_entry(context->queue_pair_array, handle)) 1122 + result = vmci_handle_arr_append_entry( 1123 + &context->queue_pair_array, handle); 1124 + else 1138 1125 result = VMCI_ERROR_DUPLICATE_ENTRY; 1139 - } 1140 1126 1141 1127 return result; 1142 1128 }
+25 -13
drivers/misc/vmw_vmci/vmci_handle_array.c
··· 8 8 #include <linux/slab.h> 9 9 #include "vmci_handle_array.h" 10 10 11 - static size_t handle_arr_calc_size(size_t capacity) 11 + static size_t handle_arr_calc_size(u32 capacity) 12 12 { 13 - return sizeof(struct vmci_handle_arr) + 13 + return VMCI_HANDLE_ARRAY_HEADER_SIZE + 14 14 capacity * sizeof(struct vmci_handle); 15 15 } 16 16 17 - struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity) 17 + struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity) 18 18 { 19 19 struct vmci_handle_arr *array; 20 20 21 + if (max_capacity == 0 || capacity > max_capacity) 22 + return NULL; 23 + 21 24 if (capacity == 0) 22 - capacity = VMCI_HANDLE_ARRAY_DEFAULT_SIZE; 25 + capacity = min((u32)VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY, 26 + max_capacity); 23 27 24 28 array = kmalloc(handle_arr_calc_size(capacity), GFP_ATOMIC); 25 29 if (!array) 26 30 return NULL; 27 31 28 32 array->capacity = capacity; 33 + array->max_capacity = max_capacity; 29 34 array->size = 0; 30 35 31 36 return array; ··· 41 36 kfree(array); 42 37 } 43 38 44 - void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, 45 - struct vmci_handle handle) 39 + int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, 40 + struct vmci_handle handle) 46 41 { 47 42 struct vmci_handle_arr *array = *array_ptr; 48 43 49 44 if (unlikely(array->size >= array->capacity)) { 50 45 /* reallocate. */ 51 46 struct vmci_handle_arr *new_array; 52 - size_t new_capacity = array->capacity * VMCI_ARR_CAP_MULT; 53 - size_t new_size = handle_arr_calc_size(new_capacity); 47 + u32 capacity_bump = min(array->max_capacity - array->capacity, 48 + array->capacity); 49 + size_t new_size = handle_arr_calc_size(array->capacity + 50 + capacity_bump); 51 + 52 + if (array->size >= array->max_capacity) 53 + return VMCI_ERROR_NO_MEM; 54 54 55 55 new_array = krealloc(array, new_size, GFP_ATOMIC); 56 56 if (!new_array) 57 - return; 57 + return VMCI_ERROR_NO_MEM; 58 58 59 - new_array->capacity = new_capacity; 59 + new_array->capacity += capacity_bump; 60 60 *array_ptr = array = new_array; 61 61 } 62 62 63 63 array->entries[array->size] = handle; 64 64 array->size++; 65 + 66 + return VMCI_SUCCESS; 65 67 } 66 68 67 69 /* ··· 78 66 struct vmci_handle entry_handle) 79 67 { 80 68 struct vmci_handle handle = VMCI_INVALID_HANDLE; 81 - size_t i; 69 + u32 i; 82 70 83 71 for (i = 0; i < array->size; i++) { 84 72 if (vmci_handle_is_equal(array->entries[i], entry_handle)) { ··· 113 101 * Handle at given index, VMCI_INVALID_HANDLE if invalid index. 114 102 */ 115 103 struct vmci_handle 116 - vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index) 104 + vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index) 117 105 { 118 106 if (unlikely(index >= array->size)) 119 107 return VMCI_INVALID_HANDLE; ··· 124 112 bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array, 125 113 struct vmci_handle entry_handle) 126 114 { 127 - size_t i; 115 + u32 i; 128 116 129 117 for (i = 0; i < array->size; i++) 130 118 if (vmci_handle_is_equal(array->entries[i], entry_handle))
+19 -10
drivers/misc/vmw_vmci/vmci_handle_array.h
··· 9 9 #define _VMCI_HANDLE_ARRAY_H_ 10 10 11 11 #include <linux/vmw_vmci_defs.h> 12 + #include <linux/limits.h> 12 13 #include <linux/types.h> 13 14 14 - #define VMCI_HANDLE_ARRAY_DEFAULT_SIZE 4 15 - #define VMCI_ARR_CAP_MULT 2 /* Array capacity multiplier */ 16 - 17 15 struct vmci_handle_arr { 18 - size_t capacity; 19 - size_t size; 16 + u32 capacity; 17 + u32 max_capacity; 18 + u32 size; 19 + u32 pad; 20 20 struct vmci_handle entries[]; 21 21 }; 22 22 23 - struct vmci_handle_arr *vmci_handle_arr_create(size_t capacity); 23 + #define VMCI_HANDLE_ARRAY_HEADER_SIZE \ 24 + offsetof(struct vmci_handle_arr, entries) 25 + /* Select a default capacity that results in a 64 byte sized array */ 26 + #define VMCI_HANDLE_ARRAY_DEFAULT_CAPACITY 6 27 + /* Make sure that the max array size can be expressed by a u32 */ 28 + #define VMCI_HANDLE_ARRAY_MAX_CAPACITY \ 29 + ((U32_MAX - VMCI_HANDLE_ARRAY_HEADER_SIZE - 1) / \ 30 + sizeof(struct vmci_handle)) 31 + 32 + struct vmci_handle_arr *vmci_handle_arr_create(u32 capacity, u32 max_capacity); 24 33 void vmci_handle_arr_destroy(struct vmci_handle_arr *array); 25 - void vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, 26 - struct vmci_handle handle); 34 + int vmci_handle_arr_append_entry(struct vmci_handle_arr **array_ptr, 35 + struct vmci_handle handle); 27 36 struct vmci_handle vmci_handle_arr_remove_entry(struct vmci_handle_arr *array, 28 37 struct vmci_handle 29 38 entry_handle); 30 39 struct vmci_handle vmci_handle_arr_remove_tail(struct vmci_handle_arr *array); 31 40 struct vmci_handle 32 - vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, size_t index); 41 + vmci_handle_arr_get_entry(const struct vmci_handle_arr *array, u32 index); 33 42 bool vmci_handle_arr_has_entry(const struct vmci_handle_arr *array, 34 43 struct vmci_handle entry_handle); 35 44 struct vmci_handle *vmci_handle_arr_get_handles(struct vmci_handle_arr *array); 36 45 37 - static inline size_t vmci_handle_arr_get_size( 46 + static inline u32 vmci_handle_arr_get_size( 38 47 const struct vmci_handle_arr *array) 39 48 { 40 49 return array->size;
+10 -1
include/linux/vmw_vmci_defs.h
··· 62 62 63 63 /* 64 64 * A single VMCI device has an upper limit of 128MB on the amount of 65 - * memory that can be used for queue pairs. 65 + * memory that can be used for queue pairs. Since each queue pair 66 + * consists of at least two pages, the memory limit also dictates the 67 + * number of queue pairs a guest can create. 66 68 */ 67 69 #define VMCI_MAX_GUEST_QP_MEMORY (128 * 1024 * 1024) 70 + #define VMCI_MAX_GUEST_QP_COUNT (VMCI_MAX_GUEST_QP_MEMORY / PAGE_SIZE / 2) 71 + 72 + /* 73 + * There can be at most PAGE_SIZE doorbells since there is one doorbell 74 + * per byte in the doorbell bitmap page. 75 + */ 76 + #define VMCI_MAX_GUEST_DOORBELL_COUNT PAGE_SIZE 68 77 69 78 /* 70 79 * Queues with pre-mapped data pages must be small, so that we don't pin