svm: asan: fix asan support
[vpp.git] / src / svm / svm.c
index 663324e..20f4b7a 100644 (file)
@@ -58,6 +58,47 @@ svm_get_root_rp (void)
 
 #define MUTEX_DEBUG
 
+u64
+svm_get_global_region_base_va ()
+{
+#ifdef CLIB_SANITIZE_ADDR
+  return 0x200000000000;
+#endif
+
+#if __aarch64__
+  /* On AArch64 VA space can have different size, from 36 to 48 bits.
+     Here we are trying to detect VA bits by parsing /proc/self/maps
+     address ranges */
+  int fd;
+  unformat_input_t input;
+  u64 start, end = 0;
+  u8 bits = 0;
+
+  if ((fd = open ("/proc/self/maps", 0)) < 0)
+    clib_unix_error ("open '/proc/self/maps'");
+
+  unformat_init_clib_file (&input, fd);
+  while (unformat_check_input (&input) != UNFORMAT_END_OF_INPUT)
+    {
+      if (unformat (&input, "%llx-%llx", &start, &end))
+       end--;
+      unformat_skip_line (&input);
+    }
+  unformat_free (&input);
+  close (fd);
+
+  bits = count_leading_zeros (end);
+  bits = 64 - bits;
+  if (bits >= 36 && bits <= 48)
+    return ((1ul << bits) / 4) - (2 * SVM_GLOBAL_REGION_SIZE);
+  else
+    clib_unix_error ("unexpected va bits '%u'", bits);
+#endif
+
+  /* default value */
+  return 0x130000000ULL;
+}
+
 static void
 region_lock (svm_region_t * rp, int tag)
 {
@@ -66,7 +107,7 @@ region_lock (svm_region_t * rp, int tag)
   rp->mutex_owner_pid = getpid ();
   rp->mutex_owner_tag = tag;
 #endif
-  ASSERT (nheld < MAXLOCK);
+  ASSERT (nheld < MAXLOCK);    //NOSONAR
   /*
    * Keep score of held mutexes so we can try to exit
    * cleanly if the world comes to an end at the worst possible
@@ -199,14 +240,6 @@ format_svm_region (u8 * s, va_list * args)
                }
            }
        }
-      s = format (s, "  rgn heap stats: %U", format_mheap,
-                 rp->region_heap, 0);
-      if ((rp->flags & SVM_FLAGS_MHEAP) && rp->data_heap)
-       {
-         s = format (s, "\n  data heap stats: %U", format_mheap,
-                     rp->data_heap, 1);
-       }
-      s = format (s, "\n");
     }
 
   return (s);
@@ -296,15 +329,17 @@ svm_data_region_create (svm_map_region_args_t * a, svm_region_t * rp)
          return -3;
        }
       close (fd);
+      CLIB_MEM_UNPOISON (rp->data_base, map_size);
       rp->backing_file = (char *) format (0, "%s\0", a->backing_file);
       rp->flags |= SVM_FLAGS_FILE;
     }
 
   if (a->flags & SVM_FLAGS_MHEAP)
     {
-      rp->data_heap =
-       mheap_alloc_with_flags ((void *) (rp->data_base), map_size,
-                               MHEAP_FLAG_DISABLE_VM);
+      rp->data_heap = create_mspace_with_base (rp->data_base,
+                                              map_size, 1 /* locked */ );
+      mspace_disable_expand (rp->data_heap);
+
       rp->flags |= SVM_FLAGS_MHEAP;
     }
   return 0;
@@ -380,6 +415,7 @@ svm_data_region_map (svm_map_region_args_t * a, svm_region_t * rp)
          return -3;
        }
       close (fd);
+      CLIB_MEM_UNPOISON (rp->data_base, map_size);
     }
   return 0;
 }
@@ -387,10 +423,7 @@ svm_data_region_map (svm_map_region_args_t * a, svm_region_t * rp)
 u8 *
 shm_name_from_svm_map_region_args (svm_map_region_args_t * a)
 {
-  u8 *path;
   u8 *shm_name;
-  u8 *split_point;
-  u8 *mkdir_arg = 0;
   int root_path_offset = 0;
   int name_offset = 0;
 
@@ -400,33 +433,10 @@ shm_name_from_svm_map_region_args (svm_map_region_args_t * a)
       if (a->root_path[0] == '/')
        root_path_offset++;
 
-      /* create the root_path under /dev/shm
-         iterate through path creating directories */
-
-      path = format (0, "/dev/shm/%s%c", &a->root_path[root_path_offset], 0);
-      split_point = path + 1;
-      vec_add1 (mkdir_arg, '-');
-
-      while (*split_point)
-       {
-         while (*split_point && *split_point != '/')
-           {
-             vec_add1 (mkdir_arg, *split_point);
-             split_point++;
-           }
-         vec_add1 (mkdir_arg, 0);
-
-         /* ready to descend another level */
-         mkdir_arg[vec_len (mkdir_arg) - 1] = '-';
-         split_point++;
-       }
-      vec_free (mkdir_arg);
-      vec_free (path);
-
       if (a->name[0] == '/')
        name_offset = 1;
 
-      shm_name = format (0, "/%s-%s%c", a->root_path,
+      shm_name = format (0, "/%s-%s%c", &a->root_path[root_path_offset],
                         &a->name[name_offset], 0);
     }
   else
@@ -434,6 +444,108 @@ shm_name_from_svm_map_region_args (svm_map_region_args_t * a)
   return (shm_name);
 }
 
+void
+svm_region_init_mapped_region (svm_map_region_args_t * a, svm_region_t * rp)
+{
+  pthread_mutexattr_t attr;
+  pthread_condattr_t cattr;
+  int nbits, words, bit;
+  int overhead_space;
+  void *oldheap;
+  uword data_base;
+  ASSERT (rp);
+  int rv;
+
+  clib_memset (rp, 0, sizeof (*rp));
+
+  if (pthread_mutexattr_init (&attr))
+    clib_unix_warning ("mutexattr_init");
+
+  if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
+    clib_unix_warning ("mutexattr_setpshared");
+
+  if (pthread_mutex_init (&rp->mutex, &attr))
+    clib_unix_warning ("mutex_init");
+
+  if (pthread_mutexattr_destroy (&attr))
+    clib_unix_warning ("mutexattr_destroy");
+
+  if (pthread_condattr_init (&cattr))
+    clib_unix_warning ("condattr_init");
+
+  if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
+    clib_unix_warning ("condattr_setpshared");
+
+  if (pthread_cond_init (&rp->condvar, &cattr))
+    clib_unix_warning ("cond_init");
+
+  if (pthread_condattr_destroy (&cattr))
+    clib_unix_warning ("condattr_destroy");
+
+  region_lock (rp, 1);
+
+  rp->virtual_base = a->baseva;
+  rp->virtual_size = a->size;
+
+  rp->region_heap = create_mspace_with_base
+    (uword_to_pointer (a->baseva + MMAP_PAGESIZE, void *),
+     (a->pvt_heap_size !=
+      0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE, 1 /* locked */ );
+
+  mspace_disable_expand (rp->region_heap);
+
+  oldheap = svm_push_pvt_heap (rp);
+
+  rp->region_name = (char *) format (0, "%s%c", a->name, 0);
+  vec_add1 (rp->client_pids, getpid ());
+
+  nbits = rp->virtual_size / MMAP_PAGESIZE;
+
+  ASSERT (nbits > 0);
+  rp->bitmap_size = nbits;
+  words = (nbits + BITS (uword) - 1) / BITS (uword);
+  vec_validate (rp->bitmap, words - 1);
+
+  overhead_space = MMAP_PAGESIZE /* header */  +
+    ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
+
+  bit = 0;
+  data_base = (uword) rp->virtual_base;
+
+  if (a->flags & SVM_FLAGS_NODATA)
+    rp->flags |= SVM_FLAGS_NEED_DATA_INIT;
+
+  do
+    {
+      clib_bitmap_set_no_check (rp->bitmap, bit, 1);
+      bit++;
+      overhead_space -= MMAP_PAGESIZE;
+      data_base += MMAP_PAGESIZE;
+    }
+  while (overhead_space > 0);
+
+  rp->data_base = (void *) data_base;
+
+  /*
+   * Note: although the POSIX spec guarantees that only one
+   * process enters this block, we have to play games
+   * to hold off clients until e.g. the mutex is ready
+   */
+  rp->version = SVM_VERSION;
+
+  /* setup the data portion of the region */
+
+  rv = svm_data_region_create (a, rp);
+  if (rv)
+    {
+      clib_warning ("data_region_create: %d", rv);
+    }
+
+  region_unlock (rp);
+
+  svm_pop_heap (oldheap);
+}
+
 /*
  * svm_map_region
  */
@@ -442,15 +554,10 @@ svm_map_region (svm_map_region_args_t * a)
 {
   int svm_fd;
   svm_region_t *rp;
-  pthread_mutexattr_t attr;
-  pthread_condattr_t cattr;
   int deadman = 0;
   u8 junk = 0;
   void *oldheap;
-  int overhead_space;
   int rv;
-  uword data_base;
-  int nbits, words, bit;
   int pid_holding_region_lock;
   u8 *shm_name;
   int dead_region_recovery = 0;
@@ -471,7 +578,7 @@ svm_map_region (svm_map_region_args_t * a)
 
   if (svm_fd >= 0)
     {
-      if (fchmod (svm_fd, 0770) < 0)
+      if (fchmod (svm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0)
        clib_unix_warning ("segment chmod");
       /* This turns out to fail harmlessly if the client starts first */
       if (fchown (svm_fd, a->uid, a->gid) < 0)
@@ -502,93 +609,9 @@ svm_map_region (svm_map_region_args_t * a)
          return (0);
        }
       close (svm_fd);
-      memset (rp, 0, sizeof (*rp));
-
-      if (pthread_mutexattr_init (&attr))
-       clib_unix_warning ("mutexattr_init");
-
-      if (pthread_mutexattr_setpshared (&attr, PTHREAD_PROCESS_SHARED))
-       clib_unix_warning ("mutexattr_setpshared");
-
-      if (pthread_mutex_init (&rp->mutex, &attr))
-       clib_unix_warning ("mutex_init");
-
-      if (pthread_mutexattr_destroy (&attr))
-       clib_unix_warning ("mutexattr_destroy");
-
-      if (pthread_condattr_init (&cattr))
-       clib_unix_warning ("condattr_init");
-
-      if (pthread_condattr_setpshared (&cattr, PTHREAD_PROCESS_SHARED))
-       clib_unix_warning ("condattr_setpshared");
-
-      if (pthread_cond_init (&rp->condvar, &cattr))
-       clib_unix_warning ("cond_init");
-
-      if (pthread_condattr_destroy (&cattr))
-       clib_unix_warning ("condattr_destroy");
-
-      region_lock (rp, 1);
-
-      rp->virtual_base = a->baseva;
-      rp->virtual_size = a->size;
-
-      rp->region_heap =
-       mheap_alloc_with_flags (uword_to_pointer
-                               (a->baseva + MMAP_PAGESIZE, void *),
-                               (a->pvt_heap_size !=
-                                0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE,
-                               MHEAP_FLAG_DISABLE_VM);
-      oldheap = svm_push_pvt_heap (rp);
-
-      rp->region_name = (char *) format (0, "%s%c", a->name, 0);
-      vec_add1 (rp->client_pids, getpid ());
-
-      nbits = rp->virtual_size / MMAP_PAGESIZE;
-
-      ASSERT (nbits > 0);
-      rp->bitmap_size = nbits;
-      words = (nbits + BITS (uword) - 1) / BITS (uword);
-      vec_validate (rp->bitmap, words - 1);
-
-      overhead_space = MMAP_PAGESIZE /* header */  +
-       ((a->pvt_heap_size != 0) ? a->pvt_heap_size : SVM_PVT_MHEAP_SIZE);
-
-      bit = 0;
-      data_base = (uword) rp->virtual_base;
-
-      if (a->flags & SVM_FLAGS_NODATA)
-       rp->flags |= SVM_FLAGS_NEED_DATA_INIT;
-
-      do
-       {
-         clib_bitmap_set_no_check (rp->bitmap, bit, 1);
-         bit++;
-         overhead_space -= MMAP_PAGESIZE;
-         data_base += MMAP_PAGESIZE;
-       }
-      while (overhead_space > 0);
-
-      rp->data_base = (void *) data_base;
+      CLIB_MEM_UNPOISON (rp, a->size);
 
-      /*
-       * Note: although the POSIX spec guarantees that only one
-       * process enters this block, we have to play games
-       * to hold off clients until e.g. the mutex is ready
-       */
-      rp->version = SVM_VERSION;
-
-      /* setup the data portion of the region */
-
-      rv = svm_data_region_create (a, rp);
-      if (rv)
-       {
-         clib_warning ("data_region_create: %d", rv);
-       }
-
-      region_unlock (rp);
-
-      svm_pop_heap (oldheap);
+      svm_region_init_mapped_region (a, rp);
 
       return ((void *) rp);
     }
@@ -604,6 +627,10 @@ svm_map_region (svm_map_region_args_t * a)
          return (0);
        }
 
+      /* Reset ownership in case the client started first */
+      if (fchown (svm_fd, a->uid, a->gid) < 0)
+       clib_unix_warning ("segment chown [ok if client starts first]");
+
       time_left = 20;
       while (1)
        {
@@ -639,6 +666,9 @@ svm_map_region (svm_map_region_args_t * a)
          clib_warning ("mmap");
          return (0);
        }
+
+      CLIB_MEM_UNPOISON (rp, MMAP_PAGESIZE);
+
       /*
        * We lost the footrace to create this region; make sure
        * the winner has crossed the finish line.
@@ -673,6 +703,10 @@ svm_map_region (svm_map_region_args_t * a)
          return (0);
        }
 
+      close (svm_fd);
+
+      CLIB_MEM_UNPOISON (rp, a->size);
+
       if ((uword) rp != rp->virtual_base)
        {
          clib_warning ("mmap botch");
@@ -720,7 +754,7 @@ svm_map_region (svm_map_region_args_t * a)
       return ((void *) rp);
 
     }
-  return 0;                    /* NOTREACHED */
+  return 0;                    /* NOTREACHED *///NOSONAR
 }
 
 static void
@@ -729,7 +763,7 @@ svm_mutex_cleanup (void)
   int i;
   for (i = 0; i < nheld; i++)
     {
-      pthread_mutex_unlock (mutexes_held[i]);
+      pthread_mutex_unlock (mutexes_held[i]);  //NOSONAR
     }
 }
 
@@ -774,6 +808,8 @@ svm_region_init_internal (svm_map_region_args_t * a)
       vec_validate (mp, 0);
       mp->name_hash = hash_create_string (0, sizeof (uword));
       mp->root_path = a->root_path ? format (0, "%s%c", a->root_path, 0) : 0;
+      mp->uid = a->uid;
+      mp->gid = a->gid;
       rp->data_base = mp;
       svm_pop_heap (oldheap);
     }
@@ -788,10 +824,10 @@ svm_region_init (void)
 {
   svm_map_region_args_t _a, *a = &_a;
 
-  memset (a, 0, sizeof (*a));
+  clib_memset (a, 0, sizeof (*a));
   a->root_path = 0;
   a->name = SVM_GLOBAL_REGION_NAME;
-  a->baseva = SVM_GLOBAL_REGION_BASEVA;
+  a->baseva = svm_get_global_region_base_va ();
   a->size = SVM_GLOBAL_REGION_SIZE;
   a->flags = SVM_FLAGS_NODATA;
   a->uid = 0;
@@ -805,10 +841,10 @@ svm_region_init_chroot (const char *root_path)
 {
   svm_map_region_args_t _a, *a = &_a;
 
-  memset (a, 0, sizeof (*a));
+  clib_memset (a, 0, sizeof (*a));
   a->root_path = root_path;
   a->name = SVM_GLOBAL_REGION_NAME;
-  a->baseva = SVM_GLOBAL_REGION_BASEVA;
+  a->baseva = svm_get_global_region_base_va ();
   a->size = SVM_GLOBAL_REGION_SIZE;
   a->flags = SVM_FLAGS_NODATA;
   a->uid = 0;
@@ -822,10 +858,10 @@ svm_region_init_chroot_uid_gid (const char *root_path, int uid, int gid)
 {
   svm_map_region_args_t _a, *a = &_a;
 
-  memset (a, 0, sizeof (*a));
+  clib_memset (a, 0, sizeof (*a));
   a->root_path = root_path;
   a->name = SVM_GLOBAL_REGION_NAME;
-  a->baseva = SVM_GLOBAL_REGION_BASEVA;
+  a->baseva = svm_get_global_region_base_va ();
   a->size = SVM_GLOBAL_REGION_SIZE;
   a->flags = SVM_FLAGS_NODATA;
   a->uid = uid;
@@ -984,7 +1020,7 @@ svm_region_unlink (svm_region_t * rp)
  * a new region client showing up at the wrong moment.
  */
 void
-svm_region_unmap (void *rp_arg)
+svm_region_unmap_internal (void *rp_arg, u8 is_client)
 {
   int i, mypid = getpid ();
   int nclients_left;
@@ -1014,6 +1050,7 @@ svm_region_unmap (void *rp_arg)
   oldheap = svm_push_pvt_heap (rp);    /* nb vec_delete() in the loop */
 
   /* Remove the caller from the list of mappers */
+  CLIB_MEM_UNPOISON (rp->client_pids, vec_bytes (rp->client_pids));
   for (i = 0; i < vec_len (rp->client_pids); i++)
     {
       if (rp->client_pids[i] == mypid)
@@ -1084,7 +1121,12 @@ found:
       vec_free (name);
 
       region_unlock (rp);
-      svm_region_unlink (rp);
+
+      /* If a client asks for the cleanup, don't unlink the backing
+       * file since we can't tell if it has been recreated. */
+      if (!is_client)
+       svm_region_unlink (rp);
+
       munmap ((void *) virtual_base, virtual_size);
       region_unlock (root_rp);
       svm_pop_heap (oldheap);
@@ -1097,11 +1139,23 @@ found:
   munmap ((void *) virtual_base, virtual_size);
 }
 
+void
+svm_region_unmap (void *rp_arg)
+{
+  svm_region_unmap_internal (rp_arg, 0 /* is_client */ );
+}
+
+void
+svm_region_unmap_client (void *rp_arg)
+{
+  svm_region_unmap_internal (rp_arg, 1 /* is_client */ );
+}
+
 /*
  * svm_region_exit
  */
-void
-svm_region_exit ()
+static void
+svm_region_exit_internal (u8 is_client)
 {
   void *oldheap;
   int i, mypid = getpid ();
@@ -1129,6 +1183,7 @@ svm_region_exit ()
   virtual_base = root_rp->virtual_base;
   virtual_size = root_rp->virtual_size;
 
+  CLIB_MEM_UNPOISON (root_rp->client_pids, vec_bytes (root_rp->client_pids));
   for (i = 0; i < vec_len (root_rp->client_pids); i++)
     {
       if (root_rp->client_pids[i] == mypid)
@@ -1141,7 +1196,7 @@ svm_region_exit ()
 
 found:
 
-  if (vec_len (root_rp->client_pids) == 0)
+  if (!is_client && vec_len (root_rp->client_pids) == 0)
     svm_region_unlink (root_rp);
 
   region_unlock (root_rp);
@@ -1151,6 +1206,18 @@ found:
   munmap ((void *) virtual_base, virtual_size);
 }
 
+void
+svm_region_exit (void)
+{
+  svm_region_exit_internal (0 /* is_client */ );
+}
+
+void
+svm_region_exit_client (void)
+{
+  svm_region_exit_internal (1 /* is_client */ );
+}
+
 void
 svm_client_scan_this_region_nolock (svm_region_t * rp)
 {