vppinfra: fix random buffer OOB crash with ASAN
[vpp.git] / src / vppinfra / vec_bootstrap.h
index 304ea2d..006d167 100644 (file)
@@ -56,8 +56,10 @@ typedef struct
 {
   u32 len; /**< Number of elements in vector (NOT its allocated length). */
   u8 hdr_size;       /**< header size divided by VEC_MIN_ALIGN */
-  u8 log2_align;      /**< data alignment */
-  u8 vpad[2];        /**< pad to 8 bytes */
+  u8 log2_align : 7;  /**< data alignment */
+  u8 default_heap : 1; /**< vector uses default heap */
+  u8 grow_elts;               /**< number of elts vector can grow without realloc */
+  u8 vpad[1];         /**< pad to 8 bytes */
   u8 vector_data[0];  /**< Vector data . */
 } vec_header_t;
 
@@ -72,6 +74,7 @@ typedef struct
     @return pointer to the vector's vector_header_t
 */
 #define _vec_find(v)   ((vec_header_t *) (v) - 1)
+#define _vec_heap(v)   (((void **) (_vec_find (v)))[-1])
 
 always_inline uword __vec_align (uword data_align, uword configuered_align);
 always_inline uword __vec_elt_sz (uword elt_sz, int is_void);
@@ -83,7 +86,7 @@ always_inline uword __vec_elt_sz (uword elt_sz, int is_void);
 #define _vec_elt_sz(V)  __vec_elt_sz (sizeof ((V)[0]), _vec_is_void (V))
 #define _vec_align(V, A) __vec_align (__alignof__((V)[0]), A)
 
-always_inline CLIB_NOSANITIZE_ADDR uword
+always_inline __clib_nosanitize_addr uword
 vec_get_header_size (void *v)
 {
   uword header_size = _vec_find (v)->hdr_size * VEC_MIN_ALIGN;
@@ -114,21 +117,21 @@ vec_header_end (void *v)
   return v + vec_get_header_size (v);
 }
 
-/** \brief Number of elements in vector (lvalue-capable)
-
-   _vec_len (v) does not check for null, but can be used as an lvalue
-   (e.g. _vec_len (v) = 99).
-*/
-
-#define _vec_len(v)    (_vec_find(v)->len)
-
 /** \brief Number of elements in vector (rvalue-only, NULL tolerant)
 
     vec_len (v) checks for NULL, but cannot be used as an lvalue.
     If in doubt, use vec_len...
 */
 
+static_always_inline u32
+__vec_len (void *v)
+{
+  return _vec_find (v)->len;
+}
+
+#define _vec_len(v)    __vec_len ((void *) (v))
 #define vec_len(v)     ((v) ? _vec_len(v) : 0)
+
 u32 vec_len_not_inline (void *v);
 
 /** \brief Number of data bytes in vector. */
@@ -166,22 +169,37 @@ _vec_max_len (void *v, uword elt_sz)
 
 #define vec_max_len(v) _vec_max_len (v, _vec_elt_sz (v))
 
+static_always_inline void
+_vec_set_grow_elts (void *v, uword n_elts)
+{
+  uword max = pow2_mask (BITS (_vec_find (0)->grow_elts));
+
+  if (PREDICT_FALSE (n_elts > max))
+    n_elts = max;
+
+  _vec_find (v)->grow_elts = n_elts;
+}
+
 always_inline void
 _vec_set_len (void *v, uword len, uword elt_sz)
 {
   ASSERT (v);
   ASSERT (len <= _vec_max_len (v, elt_sz));
   uword old_len = _vec_len (v);
+  uword grow_elts = _vec_find (v)->grow_elts;
 
   if (len > old_len)
-    CLIB_MEM_UNPOISON (v + old_len * elt_sz, (len - old_len) * elt_sz);
-  else if (len > old_len)
-    CLIB_MEM_POISON (v + len * elt_sz, (old_len - len) * elt_sz);
+    clib_mem_unpoison (v + old_len * elt_sz, (len - old_len) * elt_sz);
+  else if (len < old_len)
+    clib_mem_poison (v + len * elt_sz, (old_len - len) * elt_sz);
 
-  _vec_len (v) = len;
+  _vec_set_grow_elts (v, old_len + grow_elts - len);
+  _vec_find (v)->len = len;
 }
 
 #define vec_set_len(v, l) _vec_set_len ((void *) v, l, _vec_elt_sz (v))
+#define vec_inc_len(v, l) vec_set_len (v, _vec_len (v) + (l))
+#define vec_dec_len(v, l) vec_set_len (v, _vec_len (v) - (l))
 
 /** \brief Reset vector length to zero
     NULL-pointer tolerant