perf symbols: Validate kcore module addresses
[platform/adaptation/renesas_rcar/renesas_kernel.git] / tools / perf / util / symbol.c
index 7eb0362..b2f60dd 100644 (file)
@@ -51,6 +51,7 @@ static enum dso_binary_type binary_type_symtab[] = {
        DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
        DSO_BINARY_TYPE__GUEST_KMODULE,
        DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+       DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
        DSO_BINARY_TYPE__NOT_FOUND,
 };
 
@@ -499,6 +500,64 @@ out_failure:
        return -1;
 }
 
+int modules__parse(const char *filename, void *arg,
+                  int (*process_module)(void *arg, const char *name,
+                                        u64 start))
+{
+       char *line = NULL;
+       size_t n;
+       FILE *file;
+       int err = 0;
+
+       file = fopen(filename, "r");
+       if (file == NULL)
+               return -1;
+
+       while (1) {
+               char name[PATH_MAX];
+               u64 start;
+               char *sep;
+               ssize_t line_len;
+
+               line_len = getline(&line, &n, file);
+               if (line_len < 0) {
+                       if (feof(file))
+                               break;
+                       err = -1;
+                       goto out;
+               }
+
+               if (!line) {
+                       err = -1;
+                       goto out;
+               }
+
+               line[--line_len] = '\0'; /* \n */
+
+               sep = strrchr(line, 'x');
+               if (sep == NULL)
+                       continue;
+
+               hex2u64(sep + 1, &start);
+
+               sep = strchr(line, ' ');
+               if (sep == NULL)
+                       continue;
+
+               *sep = '\0';
+
+               scnprintf(name, sizeof(name), "[%s]", line);
+
+               err = process_module(arg, name, start);
+               if (err)
+                       break;
+       }
+out:
+       free(line);
+       fclose(file);
+       return err;
+}
+
 struct process_kallsyms_args {
        struct map *map;
        struct dso *dso;
@@ -739,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)
 {
@@ -800,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;
@@ -1214,7 +1427,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                goto do_kallsyms;
        }
 
-       if (symbol_conf.vmlinux_name != NULL) {
+       if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) {
                err = dso__load_vmlinux(dso, map,
                                        symbol_conf.vmlinux_name, filter);
                if (err > 0) {
@@ -1226,7 +1439,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                return err;
        }
 
-       if (vmlinux_path != NULL) {
+       if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) {
                err = dso__load_vmlinux_path(dso, map, filter);
                if (err > 0)
                        return err;