perf symbols: Validate kcore module addresses
authorAdrian Hunter <adrian.hunter@intel.com>
Wed, 9 Oct 2013 12:01:11 +0000 (15:01 +0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 14 Oct 2013 13:28:58 +0000 (10:28 -0300)
Before using kcore we need to check that modules are in memory at the
same addresses that they were when data was recorded.

This is done because, while we could remap symbols to different
addresses, the object code linkages would still be different which would
provide an erroneous view of the object code.

Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1381320078-16497-2-git-send-email-adrian.hunter@intel.com
[ Rename basename to base_name to avoid shadowing libgen's basename in fedora 12 ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/symbol.c

index 5fd9513..b2f60dd 100644 (file)
@@ -798,51 +798,201 @@ bool symbol__restricted_filename(const char *filename,
        return restricted;
 }
 
-struct kcore_mapfn_data {
-       struct dso *dso;
-       enum map_type type;
-       struct list_head maps;
+struct module_info {
+       struct rb_node rb_node;
+       char *name;
+       u64 start;
 };
 
-static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
+static void add_module(struct module_info *mi, struct rb_root *modules)
 {
-       struct kcore_mapfn_data *md = data;
-       struct map *map;
+       struct rb_node **p = &modules->rb_node;
+       struct rb_node *parent = NULL;
+       struct module_info *m;
 
-       map = map__new2(start, md->dso, md->type);
-       if (map == NULL)
+       while (*p != NULL) {
+               parent = *p;
+               m = rb_entry(parent, struct module_info, rb_node);
+               if (strcmp(mi->name, m->name) < 0)
+                       p = &(*p)->rb_left;
+               else
+                       p = &(*p)->rb_right;
+       }
+       rb_link_node(&mi->rb_node, parent, p);
+       rb_insert_color(&mi->rb_node, modules);
+}
+
+static void delete_modules(struct rb_root *modules)
+{
+       struct module_info *mi;
+       struct rb_node *next = rb_first(modules);
+
+       while (next) {
+               mi = rb_entry(next, struct module_info, rb_node);
+               next = rb_next(&mi->rb_node);
+               rb_erase(&mi->rb_node, modules);
+               free(mi->name);
+               free(mi);
+       }
+}
+
+static struct module_info *find_module(const char *name,
+                                      struct rb_root *modules)
+{
+       struct rb_node *n = modules->rb_node;
+
+       while (n) {
+               struct module_info *m;
+               int cmp;
+
+               m = rb_entry(n, struct module_info, rb_node);
+               cmp = strcmp(name, m->name);
+               if (cmp < 0)
+                       n = n->rb_left;
+               else if (cmp > 0)
+                       n = n->rb_right;
+               else
+                       return m;
+       }
+
+       return NULL;
+}
+
+static int __read_proc_modules(void *arg, const char *name, u64 start)
+{
+       struct rb_root *modules = arg;
+       struct module_info *mi;
+
+       mi = zalloc(sizeof(struct module_info));
+       if (!mi)
                return -ENOMEM;
 
-       map->end = map->start + len;
-       map->pgoff = pgoff;
+       mi->name = strdup(name);
+       mi->start = start;
 
-       list_add(&map->node, &md->maps);
+       if (!mi->name) {
+               free(mi);
+               return -ENOMEM;
+       }
+
+       add_module(mi, modules);
+
+       return 0;
+}
+
+static int read_proc_modules(const char *filename, struct rb_root *modules)
+{
+       if (symbol__restricted_filename(filename, "/proc/modules"))
+               return -1;
+
+       if (modules__parse(filename, modules, __read_proc_modules)) {
+               delete_modules(modules);
+               return -1;
+       }
 
        return 0;
 }
 
+static int do_validate_kcore_modules(const char *filename, struct map *map,
+                                 struct map_groups *kmaps)
+{
+       struct rb_root modules = RB_ROOT;
+       struct map *old_map;
+       int err;
+
+       err = read_proc_modules(filename, &modules);
+       if (err)
+               return err;
+
+       old_map = map_groups__first(kmaps, map->type);
+       while (old_map) {
+               struct map *next = map_groups__next(old_map);
+               struct module_info *mi;
+
+               if (old_map == map || old_map->start == map->start) {
+                       /* The kernel map */
+                       old_map = next;
+                       continue;
+               }
+
+               /* Module must be in memory at the same address */
+               mi = find_module(old_map->dso->short_name, &modules);
+               if (!mi || mi->start != old_map->start) {
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               old_map = next;
+       }
+out:
+       delete_modules(&modules);
+       return err;
+}
+
 /*
- * If kallsyms is referenced by name then we look for kcore in the same
+ * If kallsyms is referenced by name then we look for filename in the same
  * directory.
  */
-static bool kcore_filename_from_kallsyms_filename(char *kcore_filename,
-                                                 const char *kallsyms_filename)
+static bool filename_from_kallsyms_filename(char *filename,
+                                           const char *base_name,
+                                           const char *kallsyms_filename)
 {
        char *name;
 
-       strcpy(kcore_filename, kallsyms_filename);
-       name = strrchr(kcore_filename, '/');
+       strcpy(filename, kallsyms_filename);
+       name = strrchr(filename, '/');
        if (!name)
                return false;
 
-       if (!strcmp(name, "/kallsyms")) {
-               strcpy(name, "/kcore");
+       name += 1;
+
+       if (!strcmp(name, "kallsyms")) {
+               strcpy(name, base_name);
                return true;
        }
 
        return false;
 }
 
+static int validate_kcore_modules(const char *kallsyms_filename,
+                                 struct map *map)
+{
+       struct map_groups *kmaps = map__kmap(map)->kmaps;
+       char modules_filename[PATH_MAX];
+
+       if (!filename_from_kallsyms_filename(modules_filename, "modules",
+                                            kallsyms_filename))
+               return -EINVAL;
+
+       if (do_validate_kcore_modules(modules_filename, map, kmaps))
+               return -EINVAL;
+
+       return 0;
+}
+
+struct kcore_mapfn_data {
+       struct dso *dso;
+       enum map_type type;
+       struct list_head maps;
+};
+
+static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
+{
+       struct kcore_mapfn_data *md = data;
+       struct map *map;
+
+       map = map__new2(start, md->dso, md->type);
+       if (map == NULL)
+               return -ENOMEM;
+
+       map->end = map->start + len;
+       map->pgoff = pgoff;
+
+       list_add(&map->node, &md->maps);
+
+       return 0;
+}
+
 static int dso__load_kcore(struct dso *dso, struct map *map,
                           const char *kallsyms_filename)
 {
@@ -859,8 +1009,12 @@ static int dso__load_kcore(struct dso *dso, struct map *map,
        if (map != machine->vmlinux_maps[map->type])
                return -EINVAL;
 
-       if (!kcore_filename_from_kallsyms_filename(kcore_filename,
-                                                  kallsyms_filename))
+       if (!filename_from_kallsyms_filename(kcore_filename, "kcore",
+                                            kallsyms_filename))
+               return -EINVAL;
+
+       /* All modules must be present at their original addresses */
+       if (validate_kcore_modules(kallsyms_filename, map))
                return -EINVAL;
 
        md.dso = dso;