Blame SOURCES/rhbz1847676,1857749.patch

ccdb4d
commit 11c39a7375bd2759b53b89236e755c91a4f5aad8
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Tue Jun 16 20:35:53 2020 -0400
ccdb4d
ccdb4d
    RHBZ1847676: uprobes-inode tweaks redux
ccdb4d
    
ccdb4d
    Added (back) a spinlock to manage the stapiu_consumer -> process_list
ccdb4d
    structure, since it is occasionally travered from uprobe pre-handlers,
ccdb4d
    which are sometimes entered in atomic context (e.g. on rhel7).  There,
ccdb4d
    the normal mutex_t is unsafe.  So restoring a spinlock_t just for
ccdb4d
    those shortlived traversals, rhel7 and rawhide are both happy.
ccdb4d
ccdb4d
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
ccdb4d
index 156360e..922c9f1 100644
ccdb4d
--- a/runtime/linux/uprobes-inode.c
ccdb4d
+++ b/runtime/linux/uprobes-inode.c
ccdb4d
@@ -143,7 +143,8 @@ struct stapiu_consumer {
ccdb4d
   struct list_head instance_list_head; // the resulting uprobe instances for this consumer
ccdb4d
 
ccdb4d
   struct list_head process_list_head; // the processes for this consumer
ccdb4d
-
ccdb4d
+  spinlock_t process_list_lock; // protect list; used briefly from even atomic contexts
ccdb4d
+        
ccdb4d
   // List of perf counters used by each probe
ccdb4d
   // This list is an index into struct stap_perf_probe,
ccdb4d
   long perf_counters_dim;
ccdb4d
@@ -174,16 +175,19 @@ stapiu_probe_prehandler (struct uprobe_consumer *inst, struct pt_regs *regs)
ccdb4d
 
ccdb4d
       // First find the related process, set by stapiu_change_plus.
ccdb4d
       // NB: This is a linear search performed for every probe hit!
ccdb4d
-      // This could be an algorithmic problem if the list gets large, but
ccdb4d
-      // we'll wait until this is demonstratedly a hotspot before optimizing.
ccdb4d
-      mutex_lock(&c->consumer_lock);
ccdb4d
+      // This could be an algorithmic problem if the list gets large,
ccdb4d
+      // but we'll wait until this is demonstratedly a hotspot before
ccdb4d
+      // optimizing.  NB: on rhel7 sometimes we're invoked from atomic
ccdb4d
+      // context, so must be careful to use the spinlock, not the
ccdb4d
+      // mutex.
ccdb4d
+      spin_lock(&c->process_list_lock);
ccdb4d
       list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
 	if (p->tgid == current->tgid) {
ccdb4d
 	  process = p;
ccdb4d
 	  break;
ccdb4d
 	}
ccdb4d
       }
ccdb4d
-      mutex_unlock(&c->consumer_lock);
ccdb4d
+      spin_unlock(&c->process_list_lock);
ccdb4d
       if (!process) {
ccdb4d
 #ifdef UPROBE_HANDLER_REMOVE
ccdb4d
 	/* Once we're past the starting phase, we can be sure that any
ccdb4d
@@ -344,7 +348,7 @@ static void
ccdb4d
 stapiu_decrement_semaphores(struct stapiu_consumer *consumers, size_t nconsumers)
ccdb4d
 {
ccdb4d
   size_t i;
ccdb4d
-  /* NB: no stapiu_process_slots_lock needed, as the task_finder engine is
ccdb4d
+  /* NB: no process_list_lock use needed as the task_finder engine is
ccdb4d
    * already stopped by now, so no one else will mess with us.  We need
ccdb4d
    * to be sleepable for access_process_vm.  */
ccdb4d
   for (i = 0; i < nconsumers; ++i) {
ccdb4d
@@ -433,7 +437,8 @@ stapiu_init(struct stapiu_consumer *consumers, size_t nconsumers)
ccdb4d
     INIT_LIST_HEAD(&c->instance_list_head);
ccdb4d
     INIT_LIST_HEAD(&c->process_list_head);
ccdb4d
     mutex_init(&c->consumer_lock);
ccdb4d
-
ccdb4d
+    spin_lock_init(&c->process_list_lock);
ccdb4d
+    
ccdb4d
     dbug_uprobes("registering task-finder for procname:%s buildid:%s\n",
ccdb4d
 		 ((char*)c->finder.procname ?: (char*)""),
ccdb4d
 		 ((char*)c->finder.build_id ?: (char*)""));
ccdb4d
@@ -560,7 +565,9 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
    * calls us in this case with relocation=offset=0, so
ccdb4d
    * we don't have to worry about it.  */
ccdb4d
   p->base = relocation - offset;
ccdb4d
+  spin_lock (&c->process_list_lock);
ccdb4d
   list_add(&p->process_list, &c->process_list_head);
ccdb4d
+  spin_unlock (&c->process_list_lock);
ccdb4d
 
ccdb4d
   rc = 0;
ccdb4d
   mutex_unlock(&c->consumer_lock);
ccdb4d
@@ -587,28 +594,40 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
 {
ccdb4d
   int rc = 0;
ccdb4d
   struct stapiu_process *p;
ccdb4d
+  int any_found;
ccdb4d
   
ccdb4d
   if (! c->sdt_sem_offset) // nothing to do
ccdb4d
     return 0;
ccdb4d
 
ccdb4d
-  /* NB: no lock after this point, as we need to be sleepable for
ccdb4d
-   * get/put_user semaphore action.  The given process should be frozen
ccdb4d
-   * while we're busy, so it's not an issue.
ccdb4d
-   */
ccdb4d
-
ccdb4d
-  mutex_lock(&c->consumer_lock);
ccdb4d
-
ccdb4d
+  // NB: we mustn't hold a lock while changing the task memory,
ccdb4d
+  // but we need a lock to protect the process_list from concurrent
ccdb4d
+  // add/delete.  So hold a spinlock during iteration until the first
ccdb4d
+  // hit, then unlock & process.  NB: We could in principle have multiple
ccdb4d
+  // instances of the same process in the list (e.g., if the process
ccdb4d
+  // somehow maps in the same solib multiple times).  We can't easily
ccdb4d
+  // both iterate this list (in a spinlock-protected safe way), and
ccdb4d
+  // relax the spinlock enough to do a safe stapiu_write_task_semaphore()
ccdb4d
+  // call within the loop.  So we will hit only the copy in our list.
ccdb4d
+  any_found = 0;
ccdb4d
+  spin_lock(&c->process_list_lock);
ccdb4d
   /* Look through all the consumer's processes and increment semaphores.  */
ccdb4d
   list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
     unsigned long addr = p->base + c->sdt_sem_offset;
ccdb4d
     if (addr >= relocation && addr < relocation + length) {
ccdb4d
-      int rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
+      int rc2;
ccdb4d
+      // unlock list and process write for this entry
ccdb4d
+      spin_unlock(&c->process_list_lock);
ccdb4d
+      any_found=1;
ccdb4d
+      rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
       if (!rc)
ccdb4d
-	rc = rc2;
ccdb4d
+        rc = rc2;
ccdb4d
+      break; // exit list_for_each loop
ccdb4d
     }
ccdb4d
   }
ccdb4d
-
ccdb4d
-  mutex_unlock(&c->consumer_lock);
ccdb4d
+  if (! any_found)
ccdb4d
+    spin_unlock(&c->process_list_lock);
ccdb4d
+  else
ccdb4d
+    ; // already unlocked
ccdb4d
 
ccdb4d
   return rc;
ccdb4d
 }
ccdb4d
@@ -635,8 +654,9 @@ stapiu_change_minus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   //   process is dying anyway
ccdb4d
   // - the stapiu_consumer's process_list linked list will have a record
ccdb4d
   //   of the dead process: well, not great, it'll be cleaned up eventually,
ccdb4d
-  //   and cleaning it up NOW is tricky - need some spin lock to protect the list,
ccdb4d
-  //   but not out sleepy mutex:
ccdb4d
+  //   and cleaning it up NOW is tricky - we could use the process_list_lock
ccdb4d
+  //   to protect the list (as done in stapiu_change_semaphore_plus),
ccdb4d
+  //   but not our sleepy mutex:
ccdb4d
   //
ccdb4d
   // [ 1955.410237]  ? stapiu_change_minus+0x38/0xf0 [stap_54a723c01c50d972590a5c901516849_15522]
ccdb4d
   // [ 1955.411583]  __mutex_lock+0x35/0x820
ccdb4d
ccdb4d
commit 4ccdfe4536d702612912e96d7b6278b169917eaa
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Mon Jul 6 13:27:46 2020 -0400
ccdb4d
ccdb4d
    RHBZ1847676 cont'd: more uprobes-inode/onthefly concurrency controls
ccdb4d
    
ccdb4d
    The systemtap.onthefly/*.exp tests had recently become hang-prone on
ccdb4d
    some kernels, for reasons still not completely understood.  This set
ccdb4d
    of patches adds:
ccdb4d
    
ccdb4d
    - irq*-block spinlocks into uprobes-invoked paths, in case there is
ccdb4d
      peculiar reentrancy (from irq-related tracepoints)
ccdb4d
    
ccdb4d
    - a mutex lock/unlock into the stapiu_exit() path, in case there is
ccdb4d
      a concurrent stapiu_refresh() invoked by onthefly machinery around
ccdb4d
      exit time
ccdb4d
    
ccdb4d
    - restrictions into the onthefly module_refresh() translator code to
ccdb4d
      preclude STAP_SESSION_STOPPING as a time to do any sort of refresh
ccdb4d
      operation.  Now probes that were disarmed will stay disarmed during
ccdb4d
      probe-end/error/etc. processing, which is always valid with the
ccdb4d
      spec, and avoids a class of late module-refresh ops
ccdb4d
    
ccdb4d
    Testing on rhel7 and rawhide indicates the reproducible hang is gone.
ccdb4d
    Our testsuite already tortures this code; invoke by hand via:
ccdb4d
    
ccdb4d
    % sudo make installcheck RUNTESTFLAGS="-v affection.exp hrtimer_onthefly.exp kprobes_onthefly.exp tracepoint_onthefly.exp uprobes_onthefly.exp"
ccdb4d
ccdb4d
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
ccdb4d
index 922c9f1..3de7281 100644
ccdb4d
--- a/runtime/linux/uprobes-inode.c
ccdb4d
+++ b/runtime/linux/uprobes-inode.c
ccdb4d
@@ -172,6 +172,7 @@ stapiu_probe_prehandler (struct uprobe_consumer *inst, struct pt_regs *regs)
ccdb4d
   if (_stp_target) // need we filter by pid at all?
ccdb4d
     {
ccdb4d
       struct stapiu_process *p, *process = NULL;
ccdb4d
+      unsigned long flags;
ccdb4d
 
ccdb4d
       // First find the related process, set by stapiu_change_plus.
ccdb4d
       // NB: This is a linear search performed for every probe hit!
ccdb4d
@@ -180,14 +181,14 @@ stapiu_probe_prehandler (struct uprobe_consumer *inst, struct pt_regs *regs)
ccdb4d
       // optimizing.  NB: on rhel7 sometimes we're invoked from atomic
ccdb4d
       // context, so must be careful to use the spinlock, not the
ccdb4d
       // mutex.
ccdb4d
-      spin_lock(&c->process_list_lock);
ccdb4d
+      spin_lock_irqsave(&c->process_list_lock, flags);
ccdb4d
       list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
 	if (p->tgid == current->tgid) {
ccdb4d
 	  process = p;
ccdb4d
 	  break;
ccdb4d
 	}
ccdb4d
       }
ccdb4d
-      spin_unlock(&c->process_list_lock);
ccdb4d
+      spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
       if (!process) {
ccdb4d
 #ifdef UPROBE_HANDLER_REMOVE
ccdb4d
 	/* Once we're past the starting phase, we can be sure that any
ccdb4d
@@ -398,7 +399,7 @@ static void
ccdb4d
 stapiu_consumer_refresh(struct stapiu_consumer *c)
ccdb4d
 {
ccdb4d
   struct stapiu_instance *inst;
ccdb4d
-
ccdb4d
+  
ccdb4d
   mutex_lock(& c->consumer_lock);
ccdb4d
 
ccdb4d
   list_for_each_entry(inst, &c->instance_list_head, instance_list) {
ccdb4d
@@ -420,7 +421,10 @@ stapiu_exit(struct stapiu_consumer *consumers, size_t nconsumers)
ccdb4d
   stapiu_decrement_semaphores(consumers, nconsumers);
ccdb4d
   for (i = 0; i < nconsumers; ++i) {
ccdb4d
     struct stapiu_consumer *c = &consumers[i];
ccdb4d
+    // protect against conceivable stapiu_refresh() at same time
ccdb4d
+    mutex_lock(& c->consumer_lock);
ccdb4d
     stapiu_consumer_unreg(c);
ccdb4d
+    mutex_unlock(& c->consumer_lock);
ccdb4d
     /* NB: task_finder needs no unregister. */
ccdb4d
   }
ccdb4d
 }
ccdb4d
@@ -480,6 +484,7 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   struct stapiu_instance *inst = NULL;
ccdb4d
   struct stapiu_process *p;
ccdb4d
   int j;
ccdb4d
+  unsigned long flags;
ccdb4d
 
ccdb4d
   if (! inode) {
ccdb4d
       rc = -EINVAL;
ccdb4d
@@ -565,9 +570,9 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
    * calls us in this case with relocation=offset=0, so
ccdb4d
    * we don't have to worry about it.  */
ccdb4d
   p->base = relocation - offset;
ccdb4d
-  spin_lock (&c->process_list_lock);
ccdb4d
+  spin_lock_irqsave (&c->process_list_lock, flags);
ccdb4d
   list_add(&p->process_list, &c->process_list_head);
ccdb4d
-  spin_unlock (&c->process_list_lock);
ccdb4d
+  spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
 
ccdb4d
   rc = 0;
ccdb4d
   mutex_unlock(&c->consumer_lock);
ccdb4d
@@ -595,6 +600,7 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
   int rc = 0;
ccdb4d
   struct stapiu_process *p;
ccdb4d
   int any_found;
ccdb4d
+  unsigned long flags;
ccdb4d
   
ccdb4d
   if (! c->sdt_sem_offset) // nothing to do
ccdb4d
     return 0;
ccdb4d
@@ -609,14 +615,14 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
   // relax the spinlock enough to do a safe stapiu_write_task_semaphore()
ccdb4d
   // call within the loop.  So we will hit only the copy in our list.
ccdb4d
   any_found = 0;
ccdb4d
-  spin_lock(&c->process_list_lock);
ccdb4d
+  spin_lock_irqsave(&c->process_list_lock, flags);
ccdb4d
   /* Look through all the consumer's processes and increment semaphores.  */
ccdb4d
   list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
     unsigned long addr = p->base + c->sdt_sem_offset;
ccdb4d
     if (addr >= relocation && addr < relocation + length) {
ccdb4d
       int rc2;
ccdb4d
       // unlock list and process write for this entry
ccdb4d
-      spin_unlock(&c->process_list_lock);
ccdb4d
+      spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
       any_found=1;
ccdb4d
       rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
       if (!rc)
ccdb4d
@@ -625,7 +631,7 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
     }
ccdb4d
   }
ccdb4d
   if (! any_found)
ccdb4d
-    spin_unlock(&c->process_list_lock);
ccdb4d
+    spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
   else
ccdb4d
     ; // already unlocked
ccdb4d
 
ccdb4d
diff --git a/translate.cxx b/translate.cxx
ccdb4d
index 10b3d32..b10af5a 100644
ccdb4d
--- a/translate.cxx
ccdb4d
+++ b/translate.cxx
ccdb4d
@@ -2144,19 +2144,13 @@ c_unparser::emit_module_refresh ()
ccdb4d
     o->newline() << "mutex_lock(&module_refresh_mutex);";
ccdb4d
 
ccdb4d
   /* If we're not in STARTING/RUNNING state, don't try doing any work.
ccdb4d
-     PR16766 */
ccdb4d
+     PR16766.  We don't want to run refresh ops during e.g. STOPPING,
ccdb4d
+     so as to possibly activate uprobes near shutdown. */
ccdb4d
   o->newline() << "state = atomic_read (session_state());";
ccdb4d
-  o->newline() << "if (state != STAP_SESSION_RUNNING && state != STAP_SESSION_STARTING && state != STAP_SESSION_ERROR) {";
ccdb4d
-  // cannot _stp_warn etc. since we're not in probe context
ccdb4d
-  o->newline(1) << "#if defined(__KERNEL__)";
ccdb4d
-  o->newline() << "if (state != STAP_SESSION_STOPPING)";
ccdb4d
-  o->newline(1) << "printk (KERN_ERR \"stap module notifier triggered in unexpected state %d\\n\", state);";
ccdb4d
-  o->indent(-1);
ccdb4d
-  o->newline() << "#endif";
ccdb4d
-
ccdb4d
+  o->newline() << "if (state != STAP_SESSION_RUNNING && state != STAP_SESSION_STARTING) {";
ccdb4d
+  o->newline(1);
ccdb4d
   if (!session->runtime_usermode_p())
ccdb4d
     o->newline() << "mutex_unlock(&module_refresh_mutex);";
ccdb4d
-
ccdb4d
   o->newline() << "return;";
ccdb4d
   o->newline(-1) << "}";
ccdb4d
 
ccdb4d
ccdb4d
commit 046fa017d2ab7fea1a4ba2295c31f768c072855e
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Sun Jul 12 09:57:15 2020 -0400
ccdb4d
ccdb4d
    RHBZ1847676 cont'd: one more uprobes-inode/onthefly concurrency control
ccdb4d
    
ccdb4d
    In uprobes-inode.c (stapiu_change_plus), the runtime can react to
ccdb4d
    arrivals of new mappings of a solib or executable by registering new
ccdb4d
    uprobes.  Due to an assumption that this could not happen at
ccdb4d
    inconvenient times (such as a stapiu_refresh or near shutdown times),
ccdb4d
    the actual uprobes registration operation was done outside the
ccdb4d
    consumer_lock mutex being held.  But it appears this can happen at bad
ccdb4d
    times, so the mutex needs to be held, just like within
ccdb4d
    stapiu_consumer_refresh().
ccdb4d
    
ccdb4d
    The onthefly tests now survive iterating testing on rawhide+lockdep
ccdb4d
    and rhel7+lockdep.
ccdb4d
ccdb4d
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
ccdb4d
index 3de7281..01c8a07 100644
ccdb4d
--- a/runtime/linux/uprobes-inode.c
ccdb4d
+++ b/runtime/linux/uprobes-inode.c
ccdb4d
@@ -575,12 +575,10 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
 
ccdb4d
   rc = 0;
ccdb4d
-  mutex_unlock(&c->consumer_lock);
ccdb4d
-  
ccdb4d
   // Register actual uprobe if cond_enabled right now
ccdb4d
   if (c->probe->cond_enabled)
ccdb4d
     (void) stapiu_register(inst, c);
ccdb4d
-  goto out;
ccdb4d
+  goto out1;
ccdb4d
 
ccdb4d
  out2:
ccdb4d
   _stp_kfree(inst);
ccdb4d
ccdb4d
commit a9a0131eb59e8abc197d3d2a553a86bcdec3dd70
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Fri Jul 17 22:33:04 2020 -0400
ccdb4d
ccdb4d
    rhbz1857749: uprobes-inode regression in sdt semaphore setting
ccdb4d
    
ccdb4d
    Previous code neglected to set sdt.h semaphores for more than the
ccdb4d
    first process systemtap happened to encounter.  This was from a
ccdb4d
    mistaken understanding of what it meant for stapiu_change_plus() to be
ccdb4d
    called with the same inode/consumer combination.  Even though uprobes
ccdb4d
    are automatically shared, each new process still needs its perfctr and
ccdb4d
    sdt-semaphores individually set, so we do that now (as before the
ccdb4d
    rework of this code).  Mechanized testing incoming shortly.
ccdb4d
ccdb4d
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
ccdb4d
index 01c8a07..de81839 100644
ccdb4d
--- a/runtime/linux/uprobes-inode.c
ccdb4d
+++ b/runtime/linux/uprobes-inode.c
ccdb4d
@@ -190,6 +190,10 @@ stapiu_probe_prehandler (struct uprobe_consumer *inst, struct pt_regs *regs)
ccdb4d
       }
ccdb4d
       spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
       if (!process) {
ccdb4d
+        /* We know that we're in -c/-x mode, but this process is not
ccdb4d
+           in the process hierarchy, so the uprobe should be ignored
ccdb4d
+           and future hits prevented.  PR15278
ccdb4d
+        */
ccdb4d
 #ifdef UPROBE_HANDLER_REMOVE
ccdb4d
 	/* Once we're past the starting phase, we can be sure that any
ccdb4d
 	 * processes which are executing code in a mapping have already
ccdb4d
@@ -242,8 +246,8 @@ stapiu_register (struct stapiu_instance* inst, struct stapiu_consumer* c)
ccdb4d
 	       (unsigned long) inst->inode->i_ino,
ccdb4d
 	       (void*) (uintptr_t) c->offset,
ccdb4d
 	       c->probe->index,
ccdb4d
-	       ((char*)c->finder.procname ?: (char*)""),
ccdb4d
-	       ((char*)c->finder.build_id ?: (char*)""));
ccdb4d
+	       ((char*)c->finder.procname ?: ((char*)c->solib_pathname ?: "")),
ccdb4d
+               ((char*)c->finder.build_id ?: ((char*)c->solib_build_id ?: "")));
ccdb4d
 
ccdb4d
   if (!c->return_p) {
ccdb4d
     inst->kconsumer.handler = stapiu_probe_prehandler;
ccdb4d
@@ -444,8 +448,8 @@ stapiu_init(struct stapiu_consumer *consumers, size_t nconsumers)
ccdb4d
     spin_lock_init(&c->process_list_lock);
ccdb4d
     
ccdb4d
     dbug_uprobes("registering task-finder for procname:%s buildid:%s\n",
ccdb4d
-		 ((char*)c->finder.procname ?: (char*)""),
ccdb4d
-		 ((char*)c->finder.build_id ?: (char*)""));
ccdb4d
+                 ((char*)c->finder.procname ?: ""),
ccdb4d
+                 ((char*)c->finder.build_id ?: ""));
ccdb4d
 
ccdb4d
     ret = stap_register_task_finder_target(&c->finder);
ccdb4d
     if (ret != 0) {
ccdb4d
@@ -499,22 +503,22 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   if (rc)
ccdb4d
     goto out;
ccdb4d
 
ccdb4d
-  dbug_uprobes("notified for inode-offset u%sprobe "
ccdb4d
+  dbug_uprobes("notified for inode-offset arrival u%sprobe "
ccdb4d
 	       "%lu:%p pidx %zu target procname:%s buildid:%s\n",
ccdb4d
 	       c->return_p ? "ret" : "",
ccdb4d
 	       (unsigned long) inode->i_ino,
ccdb4d
 	       (void*) (uintptr_t) c->offset,
ccdb4d
 	       c->probe->index,
ccdb4d
-	       ((char*)c->finder.procname ?: (char*)""),
ccdb4d
-	       ((char*)c->finder.build_id ?: (char*)""));
ccdb4d
+	       ((char*)c->finder.procname ?: ((char*)c->solib_pathname ?: "")),
ccdb4d
+               ((char*)c->finder.build_id ?: ((char*)c->solib_build_id ?: "")));
ccdb4d
 
ccdb4d
   /* Check the buildid of the target (if we haven't already). We
ccdb4d
    * lock the target so we don't have concurrency issues. */
ccdb4d
   mutex_lock(&c->consumer_lock);
ccdb4d
 
ccdb4d
-  // Check if we already have an instance for this inode, as though we
ccdb4d
-  // were called twice by task-finder mishap, or (hypothetically) the
ccdb4d
-  // shlib was mmapped twice.
ccdb4d
+  // Check if we already have an instance for this inode.  This is normal:
ccdb4d
+  // if a different process maps the same solib, or forks into the same
ccdb4d
+  // executable.  In this case, we must not re-register the same uprobe.
ccdb4d
   list_for_each_entry(i, &c->instance_list_head, instance_list) {
ccdb4d
     if (i->inode == inode) {
ccdb4d
       inst = i;
ccdb4d
@@ -522,28 +526,33 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
     }
ccdb4d
   }
ccdb4d
 
ccdb4d
-  if (inst) { // wouldn't expect a re-notification
ccdb4d
-    if (inst->registered_p != c->probe->cond_enabled)
ccdb4d
-      // ... this should not happen
ccdb4d
-      ;
ccdb4d
-    goto out1;
ccdb4d
-  }
ccdb4d
-
ccdb4d
-  // Normal case: need a new one.
ccdb4d
-  inst = _stp_kzalloc(sizeof(struct stapiu_instance));
ccdb4d
-  if (! inst) {
ccdb4d
-    rc = -ENOMEM;
ccdb4d
-    goto out1;
ccdb4d
-  }
ccdb4d
+  if (!inst) { // new instance; need new uprobe etc.
ccdb4d
+    // Normal case: need a new one.
ccdb4d
+    inst = _stp_kzalloc(sizeof(struct stapiu_instance));
ccdb4d
+    if (! inst) {
ccdb4d
+      rc = -ENOMEM;
ccdb4d
+      goto out1;
ccdb4d
+    }
ccdb4d
 
ccdb4d
-  inst->sconsumer = c; // back link essential; that's how we go from uprobe *handler callback
ccdb4d
+    inst->sconsumer = c; // back link essential; that's how we go from uprobe *handler callback
ccdb4d
+          
ccdb4d
+    /* Grab the inode first (to prevent TOCTTOU problems). */
ccdb4d
+    inst->inode = igrab(inode);
ccdb4d
+    if (!inst->inode) {
ccdb4d
+      rc = -EINVAL;
ccdb4d
+      goto out2;
ccdb4d
+    }
ccdb4d
+  
ccdb4d
+    // Add the inode/instance to the list
ccdb4d
+    list_add(&inst->instance_list, &c->instance_list_head);
ccdb4d
 
ccdb4d
-  /* Grab the inode first (to prevent TOCTTOU problems). */
ccdb4d
-  inst->inode = igrab(inode);
ccdb4d
-  if (!inst->inode) {
ccdb4d
-    rc = -EINVAL;
ccdb4d
-    goto out2;
ccdb4d
+    // Register the actual uprobe if cond_enabled already
ccdb4d
+    if (c->probe->cond_enabled)
ccdb4d
+      (void) stapiu_register(inst, c);
ccdb4d
   }
ccdb4d
+
ccdb4d
+  // ... but we may have to do per-process work anyway: perfctr
ccdb4d
+  // initialization and sdt.h semaphore manipulation!
ccdb4d
   
ccdb4d
   // Perform perfctr registration if required
ccdb4d
   for (j=0; j < c->perf_counters_dim; j++) {
ccdb4d
@@ -551,12 +560,10 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
       (void) _stp_perf_read_init ((c->perf_counters)[j], task);
ccdb4d
   }
ccdb4d
 
ccdb4d
-  // Add the inode/instance to the list
ccdb4d
-  list_add(&inst->instance_list, &c->instance_list_head);
ccdb4d
-
ccdb4d
   // Associate this consumer with this process.  If we encounter
ccdb4d
   // resource problems here, we don't really have to undo the uprobe
ccdb4d
-  // registrations etc. already in effect.
ccdb4d
+  // registrations etc. already in effect.  It may break correct
ccdb4d
+  // tracking of process hierarchy in -c/-x operation, but too bad.
ccdb4d
   p = _stp_kzalloc(sizeof(struct stapiu_process));
ccdb4d
   if (! p) {
ccdb4d
     rc = -ENOMEM;
ccdb4d
@@ -573,11 +580,10 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   spin_lock_irqsave (&c->process_list_lock, flags);
ccdb4d
   list_add(&p->process_list, &c->process_list_head);
ccdb4d
   spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
-
ccdb4d
+  // NB: actual semaphore value bumping is done later
ccdb4d
+  
ccdb4d
   rc = 0;
ccdb4d
   // Register actual uprobe if cond_enabled right now
ccdb4d
-  if (c->probe->cond_enabled)
ccdb4d
-    (void) stapiu_register(inst, c);
ccdb4d
   goto out1;
ccdb4d
 
ccdb4d
  out2:
ccdb4d
@@ -617,11 +623,21 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
   /* Look through all the consumer's processes and increment semaphores.  */
ccdb4d
   list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
     unsigned long addr = p->base + c->sdt_sem_offset;
ccdb4d
+    if (p->tgid != task->tgid) // skip other processes in the list
ccdb4d
+      continue;
ccdb4d
     if (addr >= relocation && addr < relocation + length) {
ccdb4d
       int rc2;
ccdb4d
       // unlock list and process write for this entry
ccdb4d
       spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
       any_found=1;
ccdb4d
+
ccdb4d
+      dbug_uprobes("incrementing semaphore (u%sprobe) pid %ld "
ccdb4d
+                   "pidx %zu address %lx\n",
ccdb4d
+                   c->return_p ? "ret" : "",
ccdb4d
+                   (long) task->tgid,
ccdb4d
+                   c->probe->index,
ccdb4d
+                   (unsigned long) addr);
ccdb4d
+
ccdb4d
       rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
       if (!rc)
ccdb4d
         rc = rc2;
ccdb4d
@@ -641,15 +657,8 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
  * about the semaphores, so we can just release the process slot.  */
ccdb4d
 static int
ccdb4d
 stapiu_change_minus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
-		    unsigned long relocation, unsigned long length)
ccdb4d
+		    unsigned long addr, unsigned long length)
ccdb4d
 {
ccdb4d
-  dbug_uprobes("notified for inode-offset departure u%sprobe "
ccdb4d
-	       "pidx %zu target procname:%s buildid:%s\n",
ccdb4d
-	       c->return_p ? "ret" : "",
ccdb4d
-	       c->probe->index,
ccdb4d
-	       ((char*)c->finder.procname ?: (char*)""),
ccdb4d
-	       ((char*)c->finder.build_id ?: (char*)""));
ccdb4d
-  
ccdb4d
   // We don't need do anything really.
ccdb4d
   // A process going away means:
ccdb4d
   // - its uprobes will no longer fire: no problem, the uprobe inode
ccdb4d
@@ -674,6 +683,36 @@ stapiu_change_minus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   // [ 1955.436334]  ? __x64_sys_execve+0x27/0x30
ccdb4d
   // [ 1955.437700]  ? do_syscall_64+0x5c/0xa0
ccdb4d
 
ccdb4d
+  // But as an optimization - to avoid having them build up indefinitely,
ccdb4d
+  // and make semaphore operations go slowly, we will nuke matching entries anyway.
ccdb4d
+  unsigned long flags;
ccdb4d
+  struct stapiu_process *p, *tmp;
ccdb4d
+  unsigned nmatch=0;
ccdb4d
+  
ccdb4d
+  spin_lock_irqsave(&c->process_list_lock, flags);
ccdb4d
+  list_for_each_entry_safe(p, tmp, &c->process_list_head, process_list) {
ccdb4d
+    // we nuke by matching semaphore address (where ..._semaphore_plus wrote)
ccdb4d
+    // against the address range being unmapped
ccdb4d
+    unsigned long semaddr = p->base + c->sdt_sem_offset;
ccdb4d
+    if (p->tgid != task->tgid) // skip other processes in the list
ccdb4d
+      continue;
ccdb4d
+    if (semaddr >= addr && semaddr < addr + length) {
ccdb4d
+      list_del(&p->process_list);
ccdb4d
+      _stp_kfree (p);
ccdb4d
+      nmatch ++;
ccdb4d
+    }
ccdb4d
+  }
ccdb4d
+  spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
+
ccdb4d
+  if (nmatch > 0)
ccdb4d
+    dbug_uprobes("notified for inode-offset departure u%sprobe "
ccdb4d
+                 "pidx %zu matches:%u procname:%s buildid:%s\n",
ccdb4d
+                 c->return_p ? "ret" : "",
ccdb4d
+                 c->probe->index,
ccdb4d
+                 nmatch,
ccdb4d
+                 ((char*)c->finder.procname ?: ((char*)c->solib_pathname ?: "")),
ccdb4d
+                 ((char*)c->finder.build_id ?: ((char*)c->solib_build_id ?: "")));
ccdb4d
+
ccdb4d
   return 0;
ccdb4d
 }
ccdb4d
 
ccdb4d
ccdb4d
commit e90530877ee21cffa2a9d53567ba5b5de1dd9b32
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Mon Jul 27 07:58:30 2020 -0400
ccdb4d
ccdb4d
    PR25568 / RHBZ1857749: buildid/uprobes/inode rework, task_finder etc. side
ccdb4d
    
ccdb4d
    During work on a new stress tests for build-id based probes (coming in
ccdb4d
    next commit), it was found that the task_finder2 logic for buildid
ccdb4d
    verification didn't, well, work, because it was never run (due to an
ccdb4d
    erroneous pathlen conditional), and couldn't be safely run where it
ccdb4d
    was (because it was under spinlock but would have done
ccdb4d
    access_process_vm).  Reworked the relevant bits of task_finder2 to
ccdb4d
    perform build-id verification for processes later - during the quiesce
ccdb4d
    callback periods.  (Buildid verification for solibs is already done
ccdb4d
    in the task_finder2 consumer uprobes-inode.c.)
ccdb4d
    
ccdb4d
    Testing with sdt_misc indicated a case where a preexisting process
ccdb4d
    (with solib sdt.h semaphores) was being attached to by a new stap
ccdb4d
    binary.  task_finder2's enumeration of the preexising processes'
ccdb4d
    memory map segments violated assumptions by recent code related to
ccdb4d
    tracking in stapiu_process[] lists.  (It did not mirror the temporal
ccdb4d
    ld.so mmap sequence.)  Changed this tracking to use the inode* as the
ccdb4d
    key, and stop trying to track mapping lengths, to make positive
ccdb4d
    matches and eliminate duplicate stapiu_process[] entries for the same
ccdb4d
    (process,solib) permutation.  Reworked stapiu_process[] accumulation
ccdb4d
    generally to move to the two immediate task_finder callbacks, out of
ccdb4d
    stapiu_change_plus().
ccdb4d
    
ccdb4d
    Added lots of commentary and diagnostics throughout.  stap
ccdb4d
    -DDEBUG_UPROBES give meaningful info about uprobes & sdt semaphores;
ccdb4d
    with -DDEBUG_TASK_FINDER, more but not overwhelming relevant info
ccdb4d
    appears.
ccdb4d
ccdb4d
diff --git a/runtime/linux/task_finder2.c b/runtime/linux/task_finder2.c
ccdb4d
index 9777efb..8b8057a 100644
ccdb4d
--- a/runtime/linux/task_finder2.c
ccdb4d
+++ b/runtime/linux/task_finder2.c
ccdb4d
@@ -652,8 +652,8 @@ __verify_build_id(struct task_struct *tsk, unsigned long addr,
ccdb4d
 	tsk_build_id[build_id_len] = '\0';
ccdb4d
 
ccdb4d
 	if (strcmp(build_id, tsk_build_id)) {
ccdb4d
-		dbug_task(2, "target build-id not matched: [%s] != [%s]\n",
ccdb4d
-			  build_id, tsk_build_id);
ccdb4d
+		dbug_task(2, "target build-id not matched: [%s] @ 0x%lx != [%s]\n",
ccdb4d
+			  build_id, addr, tsk_build_id);
ccdb4d
 		return false;
ccdb4d
 	}
ccdb4d
 
ccdb4d
@@ -884,16 +884,9 @@ __stp_utrace_attach_match_filename(struct task_struct *tsk,
ccdb4d
 		// procname/build-id and match an "all thread" probe.
ccdb4d
 		if (tgt == NULL)
ccdb4d
 			continue;
ccdb4d
-		/* buildid-based target */
ccdb4d
-		else if (tgt->build_id_len > 0 && tgt->procname > 0
ccdb4d
-			 && !__verify_build_id(tsk,
ccdb4d
-					       tgt->build_id_vaddr,
ccdb4d
-					       tgt->build_id,
ccdb4d
-					       tgt->build_id_len))
ccdb4d
-		{
ccdb4d
-			continue;
ccdb4d
-		}
ccdb4d
-		else if (tgt->build_id_len == 0 && tgt->pathlen > 0
ccdb4d
+                /* buildid-based target ... gets checked in __stp_tf_quiesce_worker */
ccdb4d
+                /* procname-based target */
ccdb4d
+		else if (tgt->pathlen > 0
ccdb4d
 			 && (tgt->pathlen != filelen
ccdb4d
 			     || strcmp(tgt->procname, filename) != 0))
ccdb4d
 		{
ccdb4d
@@ -1341,6 +1334,34 @@ __stp_tf_quiesce_worker(struct task_work *work)
ccdb4d
 		return;
ccdb4d
 	}
ccdb4d
 
ccdb4d
+        /* If we had a build-id based executable probe (so we have a
ccdb4d
+         * tgt->build_id) set, we could not check it back in
ccdb4d
+         * __stp_utrace_attach_* because we can't do sleepy
ccdb4d
+         * access_process_vm() calls from there.  BUt now that we're
ccdb4d
+         * in process context, quiesced, finally we can check.  If we
ccdb4d
+         * were build-id based, and the build-id does not match, then
ccdb4d
+         * we UTRACE_DETACH from this process and skip the callbacks.
ccdb4d
+         *
ccdb4d
+         * XXX: For processes that do match, we redo this check every
ccdb4d
+         * time this callbacks is encountered somehow.  That's
ccdb4d
+         * probably unnecessary.
ccdb4d
+         */
ccdb4d
+        if (tgt->build_id_len > 0) {
ccdb4d
+                int ok = __verify_build_id(current,
ccdb4d
+                                           tgt->build_id_vaddr,
ccdb4d
+                                           tgt->build_id,
ccdb4d
+                                           tgt->build_id_len);
ccdb4d
+
ccdb4d
+                dbug_task(2, "verified buildid-target process pid=%ld ok=%d\n",
ccdb4d
+                          (long) current->tgid, ok);
ccdb4d
+                if (!ok) {
ccdb4d
+                        // stap_utrace_detach (current, & tgt->ops);
ccdb4d
+                        /* Remember that this task_work_func is finished. */
ccdb4d
+                        stp_task_work_func_done();
ccdb4d
+                        return;
ccdb4d
+                }
ccdb4d
+        } 
ccdb4d
+        
ccdb4d
 	__stp_tf_handler_start();
ccdb4d
 
ccdb4d
 	/* NB make sure we run mmap callbacks before other callbacks
ccdb4d
@@ -1434,6 +1455,21 @@ __stp_utrace_task_finder_target_quiesce(u32 action,
ccdb4d
 		}
ccdb4d
 	}
ccdb4d
 	else {
ccdb4d
+                /* Like in __stp_tf_quiesce_worker(), verify build-id now if belated. */
ccdb4d
+                if (tgt->build_id_len > 0) {
ccdb4d
+                        int ok = __verify_build_id(current,
ccdb4d
+                                                   tgt->build_id_vaddr,
ccdb4d
+                                                   tgt->build_id,
ccdb4d
+                                                   tgt->build_id_len);
ccdb4d
+                        
ccdb4d
+                        dbug_task(2, "verified2 buildid-target process pid=%ld ok=%d\n",
ccdb4d
+                                  (long) current->tgid, ok);
ccdb4d
+                        if (!ok) {
ccdb4d
+                                __stp_tf_handler_end();
ccdb4d
+                                return UTRACE_RESUME; // NB: not _DETACH; that interferes with other engines
ccdb4d
+                        }
ccdb4d
+                } 
ccdb4d
+                
ccdb4d
 		/* NB make sure we run mmap callbacks before other callbacks
ccdb4d
 		 * like 'probe process.begin' handlers so that the vma tracker
ccdb4d
 		 * is already initialized in the latter contexts */
ccdb4d
@@ -1797,15 +1833,7 @@ stap_start_task_finder(void)
ccdb4d
 					 struct stap_task_finder_target, list);
ccdb4d
 			if (tgt == NULL)
ccdb4d
 				continue;
ccdb4d
-			/* buildid-based target */
ccdb4d
-			else if (tgt->build_id_len > 0 && tgt->procname > 0
ccdb4d
-				 && !__verify_build_id(tsk,
ccdb4d
-						       tgt->build_id_vaddr,
ccdb4d
-						       tgt->build_id,
ccdb4d
-						       tgt->build_id_len))
ccdb4d
-			{
ccdb4d
-				continue;
ccdb4d
-			}
ccdb4d
+			/* buildid-based target ... gets checked in __stp_tf_quiesce_worker */
ccdb4d
 			/* procname-based target */
ccdb4d
 			else if (tgt->build_id == 0 && tgt->pathlen > 0
ccdb4d
 				 && (tgt->pathlen != mmpathlen
ccdb4d
diff --git a/runtime/linux/uprobes-inode.c b/runtime/linux/uprobes-inode.c
ccdb4d
index de81839..757da30 100644
ccdb4d
--- a/runtime/linux/uprobes-inode.c
ccdb4d
+++ b/runtime/linux/uprobes-inode.c
ccdb4d
@@ -76,7 +76,7 @@ struct stapiu_instance {
ccdb4d
   struct list_head instance_list;   // to find other instances e.g. during shutdown
ccdb4d
 
ccdb4d
   struct uprobe_consumer kconsumer; // the kernel-side struct for uprobe callbacks etc.
ccdb4d
-  struct inode *inode;              // XXX: refcount?
ccdb4d
+  struct inode *inode;              // refcounted
ccdb4d
   unsigned registered_p:1;          // whether the this kconsumer is registered (= armed, live)
ccdb4d
 
ccdb4d
   struct stapiu_consumer *sconsumer; // whose instance are we
ccdb4d
@@ -86,10 +86,14 @@ struct stapiu_instance {
ccdb4d
 /* A snippet to record the per-process vm where a particular
ccdb4d
    executable/solib was mapped.  Used for sdt semaphore setting, and
ccdb4d
    for identifying processes of our interest (vs. disinterest) for
ccdb4d
-   uprobe hits.  This object is owned by a stapiu_consumer. */
ccdb4d
+   uprobe hits.  This object is owned by a stapiu_consumer.  We use
ccdb4d
+   the same inode* as the stapiu_instance, and have the same lifespan,
ccdb4d
+   so don't bother separately refcount it. 
ccdb4d
+*/
ccdb4d
 struct stapiu_process {
ccdb4d
   struct list_head process_list;    // to find other processes
ccdb4d
 
ccdb4d
+  struct inode *inode;              // the inode* for solib or executable
ccdb4d
   unsigned long relocation;         // the mmap'ed .text address
ccdb4d
   unsigned long base;               // the address to apply sdt offsets against
ccdb4d
   pid_t tgid;                       // pid
ccdb4d
@@ -392,6 +396,7 @@ stapiu_consumer_unreg(struct stapiu_consumer *c)
ccdb4d
   // multiple times in the list.  Don't break after the first.
ccdb4d
   list_for_each_entry_safe(p, tmp, &c->process_list_head, process_list) {
ccdb4d
     list_del(&p->process_list);
ccdb4d
+    // no refcount used for the inode field
ccdb4d
     _stp_kfree (p);
ccdb4d
   }
ccdb4d
 }
ccdb4d
@@ -498,6 +503,8 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   /* Do the buildid check.  NB: on F29+, offset may not equal
ccdb4d
      0 for LOADable "R E" segments, because the read-only .note.*
ccdb4d
      stuff may have been loaded earlier, separately.  PR23890. */
ccdb4d
+  // NB: this is not really necessary for buildid-based probes,
ccdb4d
+  // which had this verified already.
ccdb4d
   rc = _stp_usermodule_check(task, c->module_name,
ccdb4d
 			     relocation - offset);
ccdb4d
   if (rc)
ccdb4d
@@ -527,7 +534,6 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
   }
ccdb4d
 
ccdb4d
   if (!inst) { // new instance; need new uprobe etc.
ccdb4d
-    // Normal case: need a new one.
ccdb4d
     inst = _stp_kzalloc(sizeof(struct stapiu_instance));
ccdb4d
     if (! inst) {
ccdb4d
       rc = -ENOMEM;
ccdb4d
@@ -560,30 +566,9 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
       (void) _stp_perf_read_init ((c->perf_counters)[j], task);
ccdb4d
   }
ccdb4d
 
ccdb4d
-  // Associate this consumer with this process.  If we encounter
ccdb4d
-  // resource problems here, we don't really have to undo the uprobe
ccdb4d
-  // registrations etc. already in effect.  It may break correct
ccdb4d
-  // tracking of process hierarchy in -c/-x operation, but too bad.
ccdb4d
-  p = _stp_kzalloc(sizeof(struct stapiu_process));
ccdb4d
-  if (! p) {
ccdb4d
-    rc = -ENOMEM;
ccdb4d
-    goto out1;
ccdb4d
-  }
ccdb4d
-  p->tgid = task->tgid;
ccdb4d
-  p->relocation = relocation;
ccdb4d
-  /* The base is used for relocating semaphores.  If the
ccdb4d
-   * probe is in an ET_EXEC binary, then that offset
ccdb4d
-   * already is a real address.  But stapiu_process_found
ccdb4d
-   * calls us in this case with relocation=offset=0, so
ccdb4d
-   * we don't have to worry about it.  */
ccdb4d
-  p->base = relocation - offset;
ccdb4d
-  spin_lock_irqsave (&c->process_list_lock, flags);
ccdb4d
-  list_add(&p->process_list, &c->process_list_head);
ccdb4d
-  spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
-  // NB: actual semaphore value bumping is done later
ccdb4d
+  // NB: process_list[] already extended up in stapiu_mmap_found().
ccdb4d
   
ccdb4d
   rc = 0;
ccdb4d
-  // Register actual uprobe if cond_enabled right now
ccdb4d
   goto out1;
ccdb4d
 
ccdb4d
  out2:
ccdb4d
@@ -599,7 +584,7 @@ stapiu_change_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
  * Increment the semaphore now.  */
ccdb4d
 static int
ccdb4d
 stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task,
ccdb4d
-			     unsigned long relocation, unsigned long length)
ccdb4d
+			     unsigned long relocation, struct inode* inode)
ccdb4d
 {
ccdb4d
   int rc = 0;
ccdb4d
   struct stapiu_process *p;
ccdb4d
@@ -609,6 +594,13 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
   if (! c->sdt_sem_offset) // nothing to do
ccdb4d
     return 0;
ccdb4d
 
ccdb4d
+  dbug_uprobes("considering semaphore (u%sprobe) pid %ld inode 0x%lx"
ccdb4d
+               "pidx %zu\n",
ccdb4d
+               c->return_p ? "ret" : "",
ccdb4d
+               (long) task->tgid,
ccdb4d
+               (unsigned long) inode,
ccdb4d
+               c->probe->index);
ccdb4d
+  
ccdb4d
   // NB: we mustn't hold a lock while changing the task memory,
ccdb4d
   // but we need a lock to protect the process_list from concurrent
ccdb4d
   // add/delete.  So hold a spinlock during iteration until the first
ccdb4d
@@ -617,32 +609,31 @@ stapiu_change_semaphore_plus(struct stapiu_consumer* c, struct task_struct *task
ccdb4d
   // somehow maps in the same solib multiple times).  We can't easily
ccdb4d
   // both iterate this list (in a spinlock-protected safe way), and
ccdb4d
   // relax the spinlock enough to do a safe stapiu_write_task_semaphore()
ccdb4d
-  // call within the loop.  So we will hit only the copy in our list.
ccdb4d
+  // call within the loop.  So we will hit only the first copy in our list.
ccdb4d
   any_found = 0;
ccdb4d
   spin_lock_irqsave(&c->process_list_lock, flags);
ccdb4d
   /* Look through all the consumer's processes and increment semaphores.  */
ccdb4d
   list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
     unsigned long addr = p->base + c->sdt_sem_offset;
ccdb4d
-    if (p->tgid != task->tgid) // skip other processes in the list
ccdb4d
-      continue;
ccdb4d
-    if (addr >= relocation && addr < relocation + length) {
ccdb4d
-      int rc2;
ccdb4d
-      // unlock list and process write for this entry
ccdb4d
-      spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
-      any_found=1;
ccdb4d
-
ccdb4d
-      dbug_uprobes("incrementing semaphore (u%sprobe) pid %ld "
ccdb4d
-                   "pidx %zu address %lx\n",
ccdb4d
-                   c->return_p ? "ret" : "",
ccdb4d
-                   (long) task->tgid,
ccdb4d
-                   c->probe->index,
ccdb4d
-                   (unsigned long) addr);
ccdb4d
-
ccdb4d
-      rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
-      if (!rc)
ccdb4d
-        rc = rc2;
ccdb4d
-      break; // exit list_for_each loop
ccdb4d
-    }
ccdb4d
+    int rc2;
ccdb4d
+    if (p->tgid != task->tgid) continue; // skip other processes in the list
ccdb4d
+    if (p->inode != inode) continue; // skip other inodes
ccdb4d
+
ccdb4d
+    // unlock list and process write for this entry
ccdb4d
+    spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
+    any_found=1;
ccdb4d
+
ccdb4d
+    dbug_uprobes("incrementing semaphore (u%sprobe) pid %ld "
ccdb4d
+                 "pidx %zu address 0x%lx\n",
ccdb4d
+                 c->return_p ? "ret" : "",
ccdb4d
+                 (long) task->tgid,
ccdb4d
+                 c->probe->index,
ccdb4d
+                 (unsigned long) addr);
ccdb4d
+
ccdb4d
+    rc2 = stapiu_write_task_semaphore(task, addr, +1);
ccdb4d
+    if (!rc)
ccdb4d
+            rc = rc2;
ccdb4d
+    break; // exit list_for_each loop
ccdb4d
   }
ccdb4d
   if (! any_found)
ccdb4d
     spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
@@ -755,17 +746,41 @@ stapiu_process_found(struct stap_task_finder_target *tf_target,
ccdb4d
   
ccdb4d
   if (!process_p)
ccdb4d
     return 0; /* ignore threads */
ccdb4d
-  
ccdb4d
+
ccdb4d
+  dbug_uprobes("process_found pid=%ld f.p=%s f.b=%s c.p=%s c.b=%s\n",
ccdb4d
+               (long)task->tgid,
ccdb4d
+	       ((char*)c->finder.procname ?: ""),
ccdb4d
+               ((char*)c->finder.build_id ?: ""),
ccdb4d
+	       ((char*)c->solib_pathname ?: ""),
ccdb4d
+               ((char*)c->solib_build_id ?: ""));
ccdb4d
+
ccdb4d
   /* ET_EXEC events are like shlib events, but with 0 relocation bases */
ccdb4d
   if (register_p) {
ccdb4d
     int rc = -EINVAL;
ccdb4d
     struct inode *inode = stapiu_get_task_inode(task);
ccdb4d
     
ccdb4d
     if (inode) {
ccdb4d
-      rc = stapiu_change_plus(c, task, 0, TASK_SIZE,
ccdb4d
-			      0, 0, inode);
ccdb4d
-      stapiu_change_semaphore_plus(c, task, 0,
ccdb4d
-				   TASK_SIZE);
ccdb4d
+      // Add a stapiu_process record to the consumer, so that
ccdb4d
+      // the semaphore increment logic will accept this task.
ccdb4d
+      struct stapiu_process* p;
ccdb4d
+      unsigned long flags;
ccdb4d
+      p = _stp_kzalloc(sizeof(struct stapiu_process));
ccdb4d
+      if (p) {
ccdb4d
+        p->tgid = task->tgid;
ccdb4d
+        p->relocation = 0;
ccdb4d
+        p->inode = inode;
ccdb4d
+        p->base = 0;
ccdb4d
+        spin_lock_irqsave (&c->process_list_lock, flags);
ccdb4d
+        list_add(&p->process_list, &c->process_list_head);
ccdb4d
+        spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
+      } else {
ccdb4d
+         _stp_warn("out of memory tracking executable in process %ld\n",
ccdb4d
+                   (long) task->tgid);
ccdb4d
+      }
ccdb4d
+            
ccdb4d
+      rc = stapiu_change_plus(c, task, 0, TASK_SIZE, 0, 0, inode);
ccdb4d
+      
ccdb4d
+      stapiu_change_semaphore_plus(c, task, 0, inode);
ccdb4d
     }
ccdb4d
     return rc;
ccdb4d
   } else
ccdb4d
@@ -776,6 +791,8 @@ stapiu_process_found(struct stap_task_finder_target *tf_target,
ccdb4d
 bool
ccdb4d
 __verify_build_id (struct task_struct *tsk, unsigned long addr,
ccdb4d
 		   unsigned const char *build_id, int build_id_len);
ccdb4d
+// defined in task_finder2.c
ccdb4d
+
ccdb4d
 
ccdb4d
 
ccdb4d
 /* The task_finder_mmap_callback.  These callbacks are NOT
ccdb4d
@@ -791,28 +808,119 @@ stapiu_mmap_found(struct stap_task_finder_target *tf_target,
ccdb4d
   struct stapiu_consumer *c =
ccdb4d
     container_of(tf_target, struct stapiu_consumer, finder);
ccdb4d
   int rc = 0;
ccdb4d
+  struct stapiu_process* p;
ccdb4d
+  int known_mapping_p;
ccdb4d
+  unsigned long flags;
ccdb4d
 
ccdb4d
-  /* The file path or build-id must match. The build-id address
ccdb4d
-   * is calculated using start address of this vma, the file
ccdb4d
-   * offset of the vma start address and the file offset of
ccdb4d
-   * the build-id. */
ccdb4d
-  if (c->solib_pathname && path && strcmp (path, c->solib_pathname))
ccdb4d
-    return 0;
ccdb4d
-  if (c->solib_build_id_len > 0 && !__verify_build_id(task,
ccdb4d
-						      addr - offset + c->solib_build_id_vaddr,
ccdb4d
-						      c->solib_build_id,
ccdb4d
-						      c->solib_build_id_len))
ccdb4d
-    return 0;
ccdb4d
+  /*
ccdb4d
+  We need to verify that this file/mmap corresponds to the given stapiu_consumer.
ccdb4d
+  One could compare (inode) file name, but that won't work with buildid-based
ccdb4d
+  uprobes.  For those, one cannot just
ccdb4d
+ 
ccdb4d
+  __verify_build_id(... addr - offset + c->solib_build_id_vaddr ...)
ccdb4d
+ 
ccdb4d
+  because dlopen()ing a shared library involves multiple mmaps, including
ccdb4d
+  some at repeating/offset addresses.  See glibc _dl_map_segments() in various
ccdb4d
+  versions.  So by the fourth call (!) on modern glibc's, we get a VM_WRITE-able
ccdb4d
+  data segment mapped, but that's at a load/mapping address that is offset by a
ccdb4d
+  page from the base (file offset=0) mapping.
ccdb4d
+
ccdb4d
+  e.g. on Fedora 32 / glibc 2.31, with testsuite/libsdt_buildid.so:
ccdb4d
+
ccdb4d
+  Program Headers:
ccdb4d
+  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
ccdb4d
+  LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0004b8 0x0004b8 R   0x1000
ccdb4d
+  LOAD           0x001000 0x0000000000001000 0x0000000000001000 0x000161 0x000161 R E 0x1000
ccdb4d
+  LOAD           0x002000 0x0000000000002000 0x0000000000002000 0x0000cc 0x0000cc R   0x1000
ccdb4d
+  LOAD           0x002df8 0x0000000000003df8 0x0000000000003df8 0x000232 0x000238 RW  0x1000
ccdb4d
+  DYNAMIC        0x002e10 0x0000000000003e10 0x0000000000003e10 0x0001d0 0x0001d0 RW  0x8
ccdb4d
+
ccdb4d
+  strace:
ccdb4d
+  openat(AT_FDCWD, ".../libsdt_buildid.so", O_RDONLY|O_CLOEXEC) = 3
ccdb4d
+  mmap(NULL, 16432, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x148c764ac000
ccdb4d
+  mmap(0x148c764ad000, 4096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1000) = 0x148c764ad000
ccdb4d
+  mmap(0x148c764ae000, 4096, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x148c764ae000
ccdb4d
+  mmap(0x148c764af000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0x148c764af000
ccdb4d
+
ccdb4d
+  Note how the virtual mapping for the fourth mmap (also) maps file-offset 0x2000 at
ccdb4d
+  vm offset 0x3000.
ccdb4d
+
ccdb4d
+  So what we do is rely on the name/buildid validation tests being run
ccdb4d
+  -earlier- in the dlopen/mmap sequence to validate near-future
ccdb4d
+  mmap()s.  We search the c->process_list[] for a mapping that already
ccdb4d
+  overlaps the new range, and if so, consider it validated ... whether
ccdb4d
+  for the solib_pathname or the solib_build_id case.
ccdb4d
+
ccdb4d
+  This is complicated for startup-time traversal of processes/mmaps,
ccdb4d
+  where it seems sometimes we get notifications out of temporal sequence.
ccdb4d
+  */
ccdb4d
   
ccdb4d
-  /* 1 - shared libraries' executable segments load from offset 0
ccdb4d
-   *   - ld.so convention offset != 0 is now allowed
ccdb4d
-   *     so stap_uprobe_change_plus can set a semaphore,
ccdb4d
-   *     i.e. a static extern, in a shared object
ccdb4d
-   * 2 - the shared library we're interested in
ccdb4d
-   * 3 - mapping should be executable or writeable (for
ccdb4d
-   *     semaphore in .so)
ccdb4d
-   *     NB: or both, on kernels that lack noexec mapping
ccdb4d
-   */
ccdb4d
+  known_mapping_p = 0;
ccdb4d
+  spin_lock_irqsave(&c->process_list_lock, flags);
ccdb4d
+  list_for_each_entry(p, &c->process_list_head, process_list) {
ccdb4d
+    if (p->tgid != task->tgid) continue;
ccdb4d
+    if (p->inode != dentry->d_inode) continue;
ccdb4d
+    known_mapping_p = 1;
ccdb4d
+    break;
ccdb4d
+  }
ccdb4d
+  spin_unlock_irqrestore(&c->process_list_lock, flags);
ccdb4d
+
ccdb4d
+
ccdb4d
+  // Check if this mapping (solib) is of interest: whether we expect
ccdb4d
+  // it by buildid or name.
ccdb4d
+  
ccdb4d
+  if (! known_mapping_p) {
ccdb4d
+    /* The file path or build-id must match. The build-id address
ccdb4d
+     * is calculated using start address of this vma, the file
ccdb4d
+     * offset of the vma start address and the file offset of
ccdb4d
+     * the build-id. */
ccdb4d
+    if (c->solib_pathname && path && strcmp (path, c->solib_pathname))
ccdb4d
+      return 0;
ccdb4d
+    if (c->solib_build_id_len > 0 && !__verify_build_id(task,
ccdb4d
+  						        addr - offset + c->solib_build_id_vaddr,
ccdb4d
+  						        c->solib_build_id,
ccdb4d
+						        c->solib_build_id_len))
ccdb4d
+      return 0;
ccdb4d
+  }
ccdb4d
+
ccdb4d
+  // If we made it this far, we have an interesting solib.
ccdb4d
+
ccdb4d
+  dbug_uprobes("mmap_found pid=%ld path=%s addr=0x%lx length=%lu offset=%lu flags=0x%lx known=%d\n",
ccdb4d
+               (long) task->tgid, path, addr, length, offset, vm_flags, known_mapping_p);
ccdb4d
+  
ccdb4d
+  if (! known_mapping_p) {
ccdb4d
+    // OK, let's add it.  The first mapping should be a VM_READ mapping
ccdb4d
+    // of the entire solib file, which will also serve as the apprx.
ccdb4d
+    // outer bounds of the repeatedly-mapped segments.
ccdb4d
+
ccdb4d
+#if 0
ccdb4d
+    // Consider an assumption about the dlopen/mmap sequence
ccdb4d
+    // If it comes out of sequence, we could get length/base wrong in the stored
ccdb4d
+    // stapiu_process, which could lead us to miscalculate semaphore addresses.
ccdb4d
+    //
ccdb4d
+    // However, this has been observed on task-finder initial-enumeration case,
ccdb4d
+    // (sdt_misc.exp, where a solib test is already running when stap starts).
ccdb4d
+    if (offset != 0)
ccdb4d
+      return 0;
ccdb4d
+#endif
ccdb4d
+    
ccdb4d
+    // Associate this consumer with this process.  If we encounter
ccdb4d
+    // resource problems here, we don't really have to undo the uprobe
ccdb4d
+    // registrations etc. already in effect.  It may break correct
ccdb4d
+    // tracking of process hierarchy in -c/-x operation, but too bad.
ccdb4d
+    p = _stp_kzalloc(sizeof(struct stapiu_process));
ccdb4d
+    if (p) {
ccdb4d
+      p->tgid = task->tgid;
ccdb4d
+      p->relocation = addr;
ccdb4d
+      p->inode = dentry->d_inode;
ccdb4d
+      p->base = addr-offset; // ... in case caught this during the second mmap
ccdb4d
+      spin_lock_irqsave (&c->process_list_lock, flags);
ccdb4d
+      list_add(&p->process_list, &c->process_list_head);
ccdb4d
+      spin_unlock_irqrestore (&c->process_list_lock, flags);
ccdb4d
+    } else
ccdb4d
+      _stp_warn("out of memory tracking solib %s in process %ld\n",
ccdb4d
+                path, (long) task->tgid);
ccdb4d
+  }
ccdb4d
 
ccdb4d
   /* Check non-writable, executable sections for probes. */
ccdb4d
   if ((vm_flags & VM_EXEC) && !(vm_flags & VM_WRITE))
ccdb4d
@@ -827,7 +935,7 @@ stapiu_mmap_found(struct stap_task_finder_target *tf_target,
ccdb4d
    */
ccdb4d
 
ccdb4d
   if ((rc == 0) && (vm_flags & VM_WRITE))
ccdb4d
-    rc = stapiu_change_semaphore_plus(c, task, addr, length);
ccdb4d
+    rc = stapiu_change_semaphore_plus(c, task, addr, dentry->d_inode);
ccdb4d
 
ccdb4d
   return rc;
ccdb4d
 }
ccdb4d
diff --git a/runtime/sym.c b/runtime/sym.c
ccdb4d
index be09ec8..21d820a 100644
ccdb4d
--- a/runtime/sym.c
ccdb4d
+++ b/runtime/sym.c
ccdb4d
@@ -713,9 +713,10 @@ static int _stp_build_id_check (struct _stp_module *m,
ccdb4d
 	  // NB: It is normal for different binaries with the same file path
ccdb4d
 	  // coexist in the same system via chroot or namespaces, therefore
ccdb4d
 	  // we make sure below is really a warning.
ccdb4d
-          _stp_warn ("Build-id mismatch [man warning::buildid]: \"%s\" address "
ccdb4d
+          _stp_warn ("Build-id mismatch [man warning::buildid]: \"%s\" pid %ld address "
ccdb4d
 		     "%#lx, expected %s actual %s\n",
ccdb4d
-                     m->path, notes_addr, hexstring_theory, hexstring_practice);
ccdb4d
+                     m->path, (long) tsk->tgid,
ccdb4d
+                     notes_addr, hexstring_theory, hexstring_practice);
ccdb4d
       return 1;
ccdb4d
   }
ccdb4d
   
ccdb4d
ccdb4d
commit 5e1ef9d7f2a5ea6e5511ef5228cf05dda1c570b3
ccdb4d
Author: Frank Ch. Eigler <fche@redhat.com>
ccdb4d
Date:   Mon Jul 27 07:58:30 2020 -0400
ccdb4d
ccdb4d
    PR25568 / RHBZ1857749: sdt_buildid.exp test case
ccdb4d
    
ccdb4d
    Add new test that checks for combinations of buildid and pathname
ccdb4d
    based uprobes for executables and shared libraries.
ccdb4d
ccdb4d
diff --git a/testsuite/systemtap.base/sdt_buildid.c b/testsuite/systemtap.base/sdt_buildid.c
ccdb4d
new file mode 100644
ccdb4d
index 0000000..ccbb2f2
ccdb4d
--- /dev/null
ccdb4d
+++ b/testsuite/systemtap.base/sdt_buildid.c
ccdb4d
@@ -0,0 +1,26 @@
ccdb4d
+#include <unistd.h>
ccdb4d
+#include <stdlib.h>
ccdb4d
+#include <stdio.h>
ccdb4d
+
ccdb4d
+void bar ();
ccdb4d
+
ccdb4d
+#ifndef ONLY_MAIN
ccdb4d
+#include "sdt_buildid_.h"
ccdb4d
+
ccdb4d
+void
ccdb4d
+bar ()
ccdb4d
+{
ccdb4d
+  printf("%s=%ld\n", "test_probe_0_semaphore", SDT_BUILDID_TEST_PROBE_0_ENABLED());
ccdb4d
+  if (SDT_BUILDID_TEST_PROBE_0_ENABLED())
ccdb4d
+    SDT_BUILDID_TEST_PROBE_0();
ccdb4d
+}
ccdb4d
+#endif
ccdb4d
+
ccdb4d
+#ifndef NO_MAIN
ccdb4d
+int
ccdb4d
+main ()
ccdb4d
+{
ccdb4d
+  bar();
ccdb4d
+  return 0;
ccdb4d
+}
ccdb4d
+#endif
ccdb4d
diff --git a/testsuite/systemtap.base/sdt_buildid.exp b/testsuite/systemtap.base/sdt_buildid.exp
ccdb4d
new file mode 100644
ccdb4d
index 0000000..3141fd6
ccdb4d
--- /dev/null
ccdb4d
+++ b/testsuite/systemtap.base/sdt_buildid.exp
ccdb4d
@@ -0,0 +1,214 @@
ccdb4d
+set test "sdt_buildid"
ccdb4d
+
ccdb4d
+set pbtype_flags {{additional_flags=-g} {} {}}
ccdb4d
+set fail_count 0
ccdb4d
+
ccdb4d
+# Compile a C program to use as the user-space probing target
ccdb4d
+set stap_path $env(SYSTEMTAP_PATH)/stap
ccdb4d
+set sup_dpath "[pwd]/sdt_buildid_.d"
ccdb4d
+set sup_hpath "[pwd]/sdt_buildid_.h"
ccdb4d
+set sup_opath "[pwd]/sdt_buildid_.o"
ccdb4d
+
ccdb4d
+# Run dtrace
ccdb4d
+if {[installtest_p]} {
ccdb4d
+    set dtrace $env(SYSTEMTAP_PATH)/dtrace
ccdb4d
+} else {
ccdb4d
+    set dtrace ../dtrace
ccdb4d
+}
ccdb4d
+
ccdb4d
+verbose -log "$dtrace --types -h -s $srcdir/$subdir/sdt_buildid_.d"
ccdb4d
+if {[catch {exec $dtrace --types -h -s \
ccdb4d
+                $srcdir/$subdir/sdt_buildid_.d} res]} {
ccdb4d
+    verbose -log "unable to run $dtrace: $res"
ccdb4d
+}
ccdb4d
+verbose -log "$dtrace --types -G -s $srcdir/$subdir/sdt_buildid_.d"
ccdb4d
+if {[catch {exec $dtrace --types -G -s \
ccdb4d
+                $srcdir/$subdir/sdt_buildid_.d} res]} {
ccdb4d
+    verbose -log "unable to run $dtrace: $res"
ccdb4d
+}
ccdb4d
+if {[file exists $sup_hpath] && [file exists $sup_opath]} then {
ccdb4d
+    pass "$test dtrace"
ccdb4d
+} else {
ccdb4d
+    incr fail_count
ccdb4d
+    fail "$test dtrace"
ccdb4d
+    return
ccdb4d
+}
ccdb4d
+    
ccdb4d
+set sup_flags [sdt_includes]
ccdb4d
+set sup_flags "$sup_flags additional_flags=-Wall"
ccdb4d
+set sup_flags "$sup_flags additional_flags=-Werror"
ccdb4d
+set sup_flags "$sup_flags additional_flags=$sup_opath"
ccdb4d
+set sup_flags "$sup_flags additional_flags=-I."
ccdb4d
+set sup_exepath "[pwd]/sdt_buildid.x"
ccdb4d
+
ccdb4d
+set res [target_compile $srcdir/$subdir/sdt_buildid.c $sup_exepath \
ccdb4d
+             executable $sup_flags]
ccdb4d
+if { $res != "" } {
ccdb4d
+    incr fail_count
ccdb4d
+    verbose "target_compile failed: $res" 2
ccdb4d
+    fail "$test compiling"
ccdb4d
+    return
ccdb4d
+} else {
ccdb4d
+    pass "$test compiling"
ccdb4d
+}
ccdb4d
+
ccdb4d
+
ccdb4d
+set sup41_flags "$sup_flags additional_flags=-shared"
ccdb4d
+set sup41_flags "$sup41_flags additional_flags=-fPIC"
ccdb4d
+set sup41_flags "$sup41_flags additional_flags=-DNO_MAIN"
ccdb4d
+set sup_sopath "[pwd]/libsdt_buildid.so"
ccdb4d
+set sup_exe2path "[pwd]/sdt_buildid_shared.x"
ccdb4d
+set res0 [target_compile $srcdir/$subdir/sdt_buildid.c $sup_sopath \
ccdb4d
+		  executable $sup41_flags ]
ccdb4d
+set sup42_flags "additional_flags=-Wl,-rpath,[pwd]"
ccdb4d
+set sup42_flags "$sup42_flags additional_flags=-L[pwd] additional_flags=-lsdt_buildid"
ccdb4d
+set sup42_flags "$sup42_flags additional_flags=-DONLY_MAIN"
ccdb4d
+set res [target_compile $srcdir/$subdir/sdt_buildid.c $sup_exe2path \
ccdb4d
+             executable $sup42_flags ]
ccdb4d
+if { $res0 != "" || $res != "" } {
ccdb4d
+    incr fail_count
ccdb4d
+    verbose "target_compile failed: $res0 $res" 2
ccdb4d
+    fail "$test compiling -shared"
ccdb4d
+    return
ccdb4d
+} else {
ccdb4d
+    pass "$test compiling -shared"
ccdb4d
+}
ccdb4d
+
ccdb4d
+catch { exec eu-readelf -n $sup_exepath | grep Build.ID | awk "{print \$NF}" } bid1
ccdb4d
+catch { exec eu-readelf -n $sup_sopath | grep Build.ID | awk "{print \$NF}" } bidso
ccdb4d
+catch { exec eu-readelf -n $sup_exe2path | grep Build.ID | awk "{print \$NF}" } bid2
ccdb4d
+verbose -log "buildid: $sup_exepath $bid1"
ccdb4d
+verbose -log "buildid: $sup_sopath $bidso"
ccdb4d
+verbose -log "buildid: $sup_exe2path $bid2"
ccdb4d
+# though we won't use the $bid2
ccdb4d
+
ccdb4d
+if {![installtest_p]} {
ccdb4d
+    untested $test
ccdb4d
+    return
ccdb4d
+}
ccdb4d
+
ccdb4d
+# To test via build-id, we need a debuginfod server to scan the testsuite build
ccdb4d
+# directory.
ccdb4d
+
ccdb4d
+
ccdb4d
+if [catch {exec /usr/bin/which debuginfod} debuginfod] then {
ccdb4d
+    untested "$test debuginfod"
ccdb4d
+} else {
ccdb4d
+    set port [expr {10000 + int(rand()*10000)}]
ccdb4d
+    spawn $debuginfod -p $port -d :memory: -F .
ccdb4d
+    set debuginfod_pid [exp_pid $spawn_id]
ccdb4d
+    # give it time to scan the build directory
ccdb4d
+    sleep 10
ccdb4d
+    # XXX: we could expect some verbose traffic
ccdb4d
+    set env(DEBUGINFOD_URLS) "http://localhost:$port $env(DEBUGINFOD_URLS)"
ccdb4d
+    verbose -log "started debuginfod on port $port"
ccdb4d
+
ccdb4d
+    set subtest "$test debuginfod buildid-exe buildid-solib"
ccdb4d
+    spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $bid1 $bidso
ccdb4d
+    set ok 0
ccdb4d
+    expect {
ccdb4d
+        -timeout 240
ccdb4d
+        -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+        -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+        eof { }
ccdb4d
+        timeout { }
ccdb4d
+    }
ccdb4d
+    catch {close}; catch {wait}
ccdb4d
+    if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+    set subtest "$test debuginfod buildid-exe path-solib"
ccdb4d
+    spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $bid1 $sup_sopath
ccdb4d
+    set ok 0
ccdb4d
+    expect {
ccdb4d
+        -timeout 240
ccdb4d
+        -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+        -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+        eof { }
ccdb4d
+        timeout { }
ccdb4d
+    }
ccdb4d
+    catch {close}; catch {wait}
ccdb4d
+    if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+    set subtest "$test debuginfod path-exe buildid-solib"
ccdb4d
+    spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $sup_exepath $bidso
ccdb4d
+    set ok 0
ccdb4d
+    expect {
ccdb4d
+        -timeout 240
ccdb4d
+        -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+        -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+        eof { }
ccdb4d
+        timeout { }
ccdb4d
+    }
ccdb4d
+    catch {close}; catch {wait}
ccdb4d
+    if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+    set subtest "$test debuginfod buildid-solib"
ccdb4d
+    spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $bidso
ccdb4d
+    set ok 0
ccdb4d
+    expect {
ccdb4d
+        -timeout 240
ccdb4d
+        -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+        -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+        eof { }
ccdb4d
+        timeout { }
ccdb4d
+    }
ccdb4d
+    catch {close}; catch {wait}
ccdb4d
+    if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+    set subtest "$test debuginfod buildid-exe"
ccdb4d
+    spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $bid1
ccdb4d
+    set ok 0
ccdb4d
+    expect {
ccdb4d
+        -timeout 240
ccdb4d
+        -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+        -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+        eof { }
ccdb4d
+        timeout { }
ccdb4d
+    }
ccdb4d
+    catch {close}; catch {wait}
ccdb4d
+    if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+    kill -INT $debuginfod_pid
ccdb4d
+}
ccdb4d
+
ccdb4d
+
ccdb4d
+set subtest "$test non-buildid both"
ccdb4d
+spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $sup_exepath $sup_sopath
ccdb4d
+set ok 0
ccdb4d
+expect {
ccdb4d
+    -timeout 240
ccdb4d
+    -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+    -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+    eof { }
ccdb4d
+    timeout { }
ccdb4d
+}
ccdb4d
+catch {close}; catch {wait}
ccdb4d
+if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+set subtest "$test non-buildid exe"
ccdb4d
+spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $sup_exepath
ccdb4d
+set ok 0
ccdb4d
+expect {
ccdb4d
+    -timeout 240
ccdb4d
+    -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+    -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exepath; exp_continue }
ccdb4d
+    eof { }
ccdb4d
+    timeout { }
ccdb4d
+}
ccdb4d
+catch {close}; catch {wait}
ccdb4d
+if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+set subtest "$test non-buildid solib"
ccdb4d
+spawn $stap_path $srcdir/$subdir/sdt_buildid.stp $sup_sopath
ccdb4d
+set ok 0
ccdb4d
+expect {
ccdb4d
+    -timeout 240
ccdb4d
+    -re {^Count [0-9]*[02468][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+    -re {^Count [0-9]*[13579][^\r\n]*\r\n} { incr ok; exec $sup_exe2path; exp_continue }
ccdb4d
+    eof { }
ccdb4d
+    timeout { }
ccdb4d
+}
ccdb4d
+catch {close}; catch {wait}
ccdb4d
+if {$ok > 6} then { pass $subtest } else { fail $subtest }
ccdb4d
+
ccdb4d
+return
ccdb4d
diff --git a/testsuite/systemtap.base/sdt_buildid.stp b/testsuite/systemtap.base/sdt_buildid.stp
ccdb4d
new file mode 100644
ccdb4d
index 0000000..a26d183
ccdb4d
--- /dev/null
ccdb4d
+++ b/testsuite/systemtap.base/sdt_buildid.stp
ccdb4d
@@ -0,0 +1,19 @@
ccdb4d
+global count
ccdb4d
+
ccdb4d
+function trace () {
ccdb4d
+  printf ("Count %d [%d] %s %s\n", count++, pid(), $$name, pp())
ccdb4d
+}
ccdb4d
+
ccdb4d
+probe process(@1).mark("test_probe_0") { trace() }
ccdb4d
+%( $# > 1 %? probe process(@2).mark("test_probe_0") { trace() } %)
ccdb4d
+
ccdb4d
+probe begin
ccdb4d
+{
ccdb4d
+  printf ("Count %d\n", count++)
ccdb4d
+}
ccdb4d
+
ccdb4d
+probe timer.s(1) // exit quickly after enough marks fire
ccdb4d
+{
ccdb4d
+  if (count > 10) exit()
ccdb4d
+}
ccdb4d
+
ccdb4d
diff --git a/testsuite/systemtap.base/sdt_buildid_.d b/testsuite/systemtap.base/sdt_buildid_.d
ccdb4d
new file mode 100644
ccdb4d
index 0000000..ebfca55
ccdb4d
--- /dev/null
ccdb4d
+++ b/testsuite/systemtap.base/sdt_buildid_.d
ccdb4d
@@ -0,0 +1,4 @@
ccdb4d
+provider sdt_buildid {
ccdb4d
+        probe test_probe_0 ();
ccdb4d
+};
ccdb4d
+