mem: fix possible integer overflow
[dpdk.git] / lib / librte_eal / common / eal_common_memzone.c
index 31bf6d8..5d28341 100644 (file)
@@ -77,6 +77,23 @@ memzone_lookup_thread_unsafe(const char *name)
        return NULL;
 }
 
+static inline struct rte_memzone *
+get_next_free_memzone(void)
+{
+       struct rte_mem_config *mcfg;
+       unsigned i = 0;
+
+       /* get pointer to global configuration */
+       mcfg = rte_eal_get_configuration()->mem_config;
+
+       for (i = 0; i < RTE_MAX_MEMZONE; i++) {
+               if (mcfg->memzone[i].addr == NULL)
+                       return &mcfg->memzone[i];
+       }
+
+       return NULL;
+}
+
 /* This function will return the greatest free block if a heap has been
  * specified. If no heap has been specified, it will return the heap and
  * length of the greatest free block available in all heaps */
@@ -102,13 +119,17 @@ find_heap_max_free_elem(int *s, unsigned align)
                }
        }
 
-       return (len - MALLOC_ELEM_OVERHEAD - align);
+       if (len < MALLOC_ELEM_OVERHEAD + align)
+               return 0;
+
+       return len - MALLOC_ELEM_OVERHEAD - align;
 }
 
 static const struct rte_memzone *
 memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
                int socket_id, unsigned flags, unsigned align, unsigned bound)
 {
+       struct rte_memzone *mz;
        struct rte_mem_config *mcfg;
        size_t requested_len;
        int socket, i;
@@ -117,7 +138,7 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
        mcfg = rte_eal_get_configuration()->mem_config;
 
        /* no more room in config */
-       if (mcfg->memzone_idx >= RTE_MAX_MEMZONE) {
+       if (mcfg->memzone_cnt >= RTE_MAX_MEMZONE) {
                RTE_LOG(ERR, EAL, "%s(): No more room in config\n", __func__);
                rte_errno = ENOSPC;
                return NULL;
@@ -131,6 +152,13 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
                return NULL;
        }
 
+       if (strlen(name) >= sizeof(mz->name) - 1) {
+               RTE_LOG(DEBUG, EAL, "%s(): memzone <%s>: name too long\n",
+                       __func__, name);
+               rte_errno = EEXIST;
+               return NULL;
+       }
+
        /* if alignment is not a power of two */
        if (align && !rte_is_power_of_2(align)) {
                RTE_LOG(ERR, EAL, "%s(): Invalid alignment: %u\n", __func__,
@@ -172,8 +200,13 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
        if (len == 0) {
                if (bound != 0)
                        requested_len = bound;
-               else
+               else {
                        requested_len = find_heap_max_free_elem(&socket_id, align);
+                       if (requested_len == 0) {
+                               rte_errno = ENOMEM;
+                               return NULL;
+                       }
+               }
        }
 
        if (socket_id == SOCKET_ID_ANY)
@@ -206,7 +239,16 @@ memzone_reserve_aligned_thread_unsafe(const char *name, size_t len,
        const struct malloc_elem *elem = malloc_elem_from_data(mz_addr);
 
        /* fill the zone in config */
-       struct rte_memzone *mz = &mcfg->memzone[mcfg->memzone_idx++];
+       mz = get_next_free_memzone();
+
+       if (mz == NULL) {
+               RTE_LOG(ERR, EAL, "%s(): Cannot find free memzone but there is room "
+                               "in config!\n", __func__);
+               rte_errno = ENOSPC;
+               return NULL;
+       }
+
+       mcfg->memzone_cnt++;
        snprintf(mz->name, sizeof(mz->name), "%s", name);
        mz->phys_addr = rte_malloc_virt2phy(mz_addr);
        mz->addr = mz_addr;
@@ -277,6 +319,54 @@ rte_memzone_reserve(const char *name, size_t len, int socket_id,
                                               flags, RTE_CACHE_LINE_SIZE, 0);
 }
 
+int
+rte_memzone_free(const struct rte_memzone *mz)
+{
+       struct rte_mem_config *mcfg;
+       int ret = 0;
+       void *addr;
+       unsigned idx;
+
+       if (mz == NULL)
+               return -EINVAL;
+
+       mcfg = rte_eal_get_configuration()->mem_config;
+
+       rte_rwlock_write_lock(&mcfg->mlock);
+
+       idx = ((uintptr_t)mz - (uintptr_t)mcfg->memzone);
+       idx = idx / sizeof(struct rte_memzone);
+
+#ifdef RTE_LIBRTE_IVSHMEM
+       /*
+        * If ioremap_addr is set, it's an IVSHMEM memzone and we cannot
+        * free it.
+        */
+       if (mcfg->memzone[idx].ioremap_addr != 0) {
+               rte_rwlock_write_unlock(&mcfg->mlock);
+               return -EINVAL;
+       }
+#endif
+
+       addr = mcfg->memzone[idx].addr;
+
+       if (addr == NULL)
+               ret = -EINVAL;
+       else if (mcfg->memzone_cnt == 0) {
+               rte_panic("%s(): memzone address not NULL but memzone_cnt is 0!\n",
+                               __func__);
+       } else {
+               memset(&mcfg->memzone[idx], 0, sizeof(mcfg->memzone[idx]));
+               mcfg->memzone_cnt--;
+       }
+
+       rte_rwlock_write_unlock(&mcfg->mlock);
+
+       rte_free(addr);
+
+       return ret;
+}
+
 /*
  * Lookup for the memzone identified by the given name
  */
@@ -349,7 +439,7 @@ rte_eal_memzone_init(void)
        rte_rwlock_write_lock(&mcfg->mlock);
 
        /* delete all zones */
-       mcfg->memzone_idx = 0;
+       mcfg->memzone_cnt = 0;
        memset(mcfg->memzone, 0, sizeof(mcfg->memzone));
 
        rte_rwlock_write_unlock(&mcfg->mlock);