Example #1
0
/* The task_finder_callback we use for ET_DYN targets.
   This just forces an unmap of everything as the process exits.
   (PR11151) */
static int stap_uprobe_process_munmap (struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {
  const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);
  if (! process_p) return 0; /* ignore threads */
  dbug_task_vma (1, "%cproc pid %d stf %p %p path %s\n", register_p?'+':'-', tsk->tgid, tgt, stf, stf->pathname);
  /* Covering 0->TASK_SIZE means "unmap everything" */
  if (!register_p)
    return stap_uprobe_change_minus (tsk, 0, TASK_SIZE, stf);
  return 0;
}
Example #2
0
/* The task_finder_mmap_callback */
static int
stap_uprobe_mmap_found (struct stap_task_finder_target *tgt,
                        struct task_struct *tsk, char *path,
                        struct dentry *dentry, unsigned long addr,
                        unsigned long length, unsigned long offset,
                        unsigned long vm_flags)
{
  int rc = 0;
  const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);
  /* 1 - shared libraries' executable segments load from offset 0
   *   - ld.so convention offset != 0 is now allowed
   *     so stap_uprobe_change_plus can set a semaphore,
   *     i.e. a static extern, in a shared object
   * 2 - the shared library we're interested in
   * 3 - mapping should be executable or writable (for semaphore in .so)
   *     NB: or both, on kernels that lack noexec mapping
   */
  if (path == NULL || strcmp (path, stf->pathname))
    return 0;

  /* Check non-writable, executable sections for probes. */
  if ((vm_flags & VM_EXEC) && !(vm_flags & VM_WRITE)) {
    dbug_task_vma (1,
               "+mmap X pid %d path %s addr %p length %u offset %p stf %p %p path %s\n",
               tsk->tgid, path, (void *) addr, (unsigned)length, (void*) offset,
               tgt, stf, stf->pathname);
    rc = stap_uprobe_change_plus (tsk, addr, length, stf, offset, vm_flags);
  }

  /* Check writable sections for semaphores.
   * NB: They may have also been executable for the check above, if we're
   *     running a kernel that lacks noexec mappings.  So long as there's
   *     no error (rc == 0), we need to look for semaphores too.
   */
  if ((rc == 0) && (vm_flags & VM_WRITE)) {
    dbug_task_vma (1,
               "+mmap W pid %d path %s addr %p length %u offset %p stf %p %p path %s\n",
               tsk->tgid, path, (void *) addr, (unsigned)length, (void*) offset,
               tgt, stf, stf->pathname);
    rc = stap_uprobe_change_semaphore_plus (tsk, addr, length, stf);
  }

  return rc;
}
Example #3
0
/* The task_finder_callback we use for ET_EXEC targets.
   We used to perform uprobe insertion/removal here, but not any more.
   (PR10524) */
static int stap_uprobe_process_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, int register_p, int process_p) {
  const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);
  if (! process_p) return 0; /* ignore threads */
  dbug_task_vma(1, "%cproc pid %d stf %p %p path %s\n", register_p?'+':'-', tsk->tgid, tgt, stf, stf->pathname);
  /* ET_EXEC events are like shlib events, but with 0 relocation bases */
  if (register_p) {
    int rc = stap_uprobe_change_plus (tsk, 0, TASK_SIZE, stf, 0, 0);
    stap_uprobe_change_semaphore_plus (tsk, 0, TASK_SIZE, stf);
    return rc;
  } else
    return stap_uprobe_change_minus (tsk, 0, TASK_SIZE, stf);
}
Example #4
0
/* exec callback, will try to match vdso for new process,
   will drop all vma maps for a process that disappears. */
static int _stp_vma_exec_cb(struct stap_task_finder_target *tgt,
			    struct task_struct *tsk,
			    int register_p,
			    int process_p)
{
  dbug_task_vma(1,
	    "tsk %d:%d , register_p: %d, process_p: %d\n",
	    tsk->pid, tsk->tgid, register_p, process_p);
  if (process_p)
    {
      if (register_p)
	_stp_vma_match_vdso(tsk);
      else
	stap_drop_vma_maps(tsk);
    }

  return 0;
}
Example #5
0
/* Initializes the vma tracker. */
static int _stp_vma_init(void)
{
        int rc = 0;
#ifdef HAVE_TASK_FINDER
        static struct stap_task_finder_target vmcb = {
                // NB: no .pid, no .procname filters here.
                // This means that we get a system-wide mmap monitoring
                // widget while the script is running. (The
                // system-wideness may be restricted by stap -c or
                // -x.)  But this seems to be necessary if we want to
                // to stack tracebacks through arbitrary shared libraries.
                //
                // XXX: There may be an optimization opportunity
                // for executables (for which the main task-finder
                // callback should be sufficient).
                .pid = 0,
                .procname = NULL,
                .purpose = "vma tracking",
                .callback = &_stp_vma_exec_cb,
                .mmap_callback = &_stp_vma_mmap_cb,
                .munmap_callback = &_stp_vma_munmap_cb,
                .mprotect_callback = NULL
        };
	rc = stap_initialize_vma_map ();
	if (rc != 0) {
		_stp_error("Couldn't initialize vma map: %d\n", rc);
		return rc;
	}
	dbug_task_vma(1,
		  "registering vmcb (_stap_target: %d)\n", _stp_target);
	rc = stap_register_task_finder_target (& vmcb);
	if (rc != 0)
		_stp_error("Couldn't register task finder target: %d\n", rc);
#endif
	return rc;
}

/* Get rid of the vma tracker (memory). */
static void _stp_vma_done(void)
{
#if defined(CONFIG_UTRACE)
	stap_destroy_vma_map();
#endif
}
Example #6
0
/* The task_finder_munmap_callback */
static int stap_uprobe_munmap_found (struct stap_task_finder_target *tgt, struct task_struct *tsk, unsigned long addr, unsigned long length) {
  const struct stap_uprobe_tf *stf = container_of(tgt, struct stap_uprobe_tf, finder);
  dbug_task_vma (1, "-mmap pid %d addr %p length %lu stf %p %p path %s\n", tsk->tgid, (void *) addr, length, tgt, stf, stf->pathname);
  return stap_uprobe_change_minus (tsk, addr, length, stf);
}
Example #7
0
static void _stp_vma_match_vdso(struct task_struct *tsk)
{
/* vdso is arch specific */
#if defined(STAPCONF_MM_CONTEXT_VDSO) || defined(STAPCONF_MM_CONTEXT_VDSO_BASE)
  int i, j;
  if (tsk->mm)
    {
      struct _stp_module *found = NULL;

#ifdef STAPCONF_MM_CONTEXT_VDSO
      unsigned long vdso_addr = (unsigned long) tsk->mm->context.vdso;
#else
      unsigned long vdso_addr = tsk->mm->context.vdso_base;
#endif

      dbug_task_vma(1,"tsk: %d vdso: 0x%lx\n", tsk->pid, vdso_addr);

      for (i = 0; i < _stp_num_modules && found == NULL; i++) {
	struct _stp_module *m = _stp_modules[i];
	if (m->path[0] == '/'
	    && m->num_sections == 1
	    && strncmp(m->name, "vdso", 4) == 0)
	  {
	    unsigned long notes_addr;
	    int all_ok = 1;
	    notes_addr = vdso_addr + m->build_id_offset;
	    dbug_task_vma(1,"notes_addr %s: 0x%lx + 0x%lx = 0x%lx (len: %x)\n", m->path,
		  vdso_addr, m->build_id_offset, notes_addr, m->build_id_len);
	    for (j = 0; j < m->build_id_len; j++)
	      {
		int rc;
		unsigned char b;

		/*
		 * Why check CONFIG_UTRACE here? If we're using real
		 * in-kernel utrace, we can always just call
		 * get_user() (since tsk == current).
		 *
		 * Since we're only reading here, we can call
		 * __access_process_vm_noflush(), which only calls
		 * things that are exported.
		 */
#ifdef CONFIG_UTRACE
		rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
#else
		if (tsk == current)
		  {
		    rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
		  }
		else
		  {
		    rc = (__access_process_vm_noflush(tsk, (notes_addr + j),
						      &b, 1, 0) != 1);
		  }
#endif
		if (rc || b != m->build_id_bits[j])
		  {
		    dbug_task_vma(1,"darn, not equal (rc=%d) at %d (0x%x != 0x%x)\n",
			  rc, j, b, m->build_id_bits[j]);
		    all_ok = 0;
		    break;
		  }
	      }
	    if (all_ok)
	      found = m;
	  }
      }
      if (found != NULL)
	{
	  stap_add_vma_map_info(tsk, vdso_addr,
				vdso_addr + found->sections[0].size,
				"vdso", found);
	  dbug_task_vma(1,"found vdso: %s\n", found->path);
	}
    }
#endif /* STAPCONF_MM_CONTEXT_VDSO */
}
Example #8
0
/* mmap callback, will match new vma with _stp_module or register vma name. */
static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt,
			    struct task_struct *tsk,
			    char *path, struct dentry *dentry,
			    unsigned long addr,
			    unsigned long length,
			    unsigned long offset,
			    unsigned long vm_flags)
{
	int i, res;
	struct _stp_module *module = NULL;
	const char *name = ((dentry != NULL) ? (char *)dentry->d_name.name
			    : NULL);
        
        if (path == NULL || *path == '\0') /* unknown? */
                path = (char *)name; /* we'll copy this soon, in ..._add_vma_... */

	dbug_task_vma(1,
		  "mmap_cb: tsk %d:%d path %s, addr 0x%08lx, length 0x%08lx, offset 0x%lx, flags 0x%lx\n",
		  tsk->pid, tsk->tgid, path, addr, length, offset, vm_flags);
	// We are only interested in the first load of the whole module that
	// is executable. We register whether or not we know the module,
	// so we can later lookup the name given an address for this task.
	if (path != NULL && offset == 0 && (vm_flags & VM_EXEC)
	    && stap_find_vma_map_info(tsk, addr, NULL, NULL, NULL, NULL) != 0) {
		for (i = 0; i < _stp_num_modules; i++) {
			if (strcmp(path, _stp_modules[i]->path) == 0)
			{
			  unsigned long vm_start = 0;
			  unsigned long vm_end = 0;
			  dbug_task_vma(1,
				    "vm_cb: matched path %s to module (sec: %s)\n",
				    path, _stp_modules[i]->sections[0].name);
			  module = _stp_modules[i];
			  /* Make sure we really don't know about this module
			     yet.  If we do know, we might want to extend
			     the coverage. */
			  res = stap_find_vma_map_info_user(tsk->group_leader,
							    module,
							    &vm_start, &vm_end,
							    NULL);
			  if (res == -ESRCH)
			    res = stap_add_vma_map_info(tsk->group_leader,
						        addr, addr + length,
						        path, module);
			  else if (res == 0 && vm_end + 1 == addr)
			    res = stap_extend_vma_map_info(tsk->group_leader,
							   vm_start,
							   addr + length);
			  /* VMA entries are allocated dynamically, this is fine,
			   * since we are in a task_finder callback, which is in
			   * user context. */
			  if (res != 0) {
				_stp_error ("Couldn't register module '%s' for pid %d (%d)\n", _stp_modules[i]->path, tsk->group_leader->pid, res);
			  }
			  return 0;
			}
		}

		/* None of the tracked modules matched, register without,
		 * to make sure we can lookup the name later. Ignore errors,
		 * we will just report unknown when asked and tables were
		 * full. Restrict to target process when given to preserve
		 * vma_map entry slots. */
		if (_stp_target == 0
		    || _stp_target == tsk->group_leader->pid)
		  {
		    res = stap_add_vma_map_info(tsk->group_leader, addr,
						addr + length, path, NULL);
		    dbug_task_vma(1,
			      "registered '%s' for %d (res:%d) [%lx-%lx]\n",
			      path, tsk->group_leader->pid,
			      res, addr, addr + length);
		  }

	} else if (path != NULL) {
		// Once registered, we may want to extend an earlier
		// registered region. A segment might be mapped with
		// different flags for different offsets. If so we want
		// to record the extended range so we can address more
		// precisely to module names and symbols.
		res = stap_extend_vma_map_info(tsk->group_leader,
					       addr, addr + length);
		dbug_task_vma(1,
			  "extended '%s' for %d (res:%d) [%lx-%lx]\n",
			  path, tsk->group_leader->pid,
			  res, addr, addr + length);
	}
	return 0;
}