0001
0002
0003 #include <linux/buildid.h>
0004 #include <linux/cache.h>
0005 #include <linux/elf.h>
0006 #include <linux/kernel.h>
0007 #include <linux/pagemap.h>
0008
0009 #define BUILD_ID 3
0010
0011
0012
0013
0014
0015
0016 static int parse_build_id_buf(unsigned char *build_id,
0017 __u32 *size,
0018 const void *note_start,
0019 Elf32_Word note_size)
0020 {
0021 Elf32_Word note_offs = 0, new_offs;
0022
0023 while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
0024 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
0025
0026 if (nhdr->n_type == BUILD_ID &&
0027 nhdr->n_namesz == sizeof("GNU") &&
0028 !strcmp((char *)(nhdr + 1), "GNU") &&
0029 nhdr->n_descsz > 0 &&
0030 nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
0031 memcpy(build_id,
0032 note_start + note_offs +
0033 ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
0034 nhdr->n_descsz);
0035 memset(build_id + nhdr->n_descsz, 0,
0036 BUILD_ID_SIZE_MAX - nhdr->n_descsz);
0037 if (size)
0038 *size = nhdr->n_descsz;
0039 return 0;
0040 }
0041 new_offs = note_offs + sizeof(Elf32_Nhdr) +
0042 ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
0043 if (new_offs <= note_offs)
0044 break;
0045 note_offs = new_offs;
0046 }
0047
0048 return -EINVAL;
0049 }
0050
0051 static inline int parse_build_id(const void *page_addr,
0052 unsigned char *build_id,
0053 __u32 *size,
0054 const void *note_start,
0055 Elf32_Word note_size)
0056 {
0057
0058 if (note_start < page_addr || note_start + note_size < note_start)
0059 return -EINVAL;
0060
0061
0062 if (note_start + note_size > page_addr + PAGE_SIZE)
0063 return -EINVAL;
0064
0065 return parse_build_id_buf(build_id, size, note_start, note_size);
0066 }
0067
0068
0069 static int get_build_id_32(const void *page_addr, unsigned char *build_id,
0070 __u32 *size)
0071 {
0072 Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
0073 Elf32_Phdr *phdr;
0074 int i;
0075
0076
0077 if (ehdr->e_phnum >
0078 (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
0079 return -EINVAL;
0080
0081 phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
0082
0083 for (i = 0; i < ehdr->e_phnum; ++i) {
0084 if (phdr[i].p_type == PT_NOTE &&
0085 !parse_build_id(page_addr, build_id, size,
0086 page_addr + phdr[i].p_offset,
0087 phdr[i].p_filesz))
0088 return 0;
0089 }
0090 return -EINVAL;
0091 }
0092
0093
0094 static int get_build_id_64(const void *page_addr, unsigned char *build_id,
0095 __u32 *size)
0096 {
0097 Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
0098 Elf64_Phdr *phdr;
0099 int i;
0100
0101
0102 if (ehdr->e_phnum >
0103 (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
0104 return -EINVAL;
0105
0106 phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
0107
0108 for (i = 0; i < ehdr->e_phnum; ++i) {
0109 if (phdr[i].p_type == PT_NOTE &&
0110 !parse_build_id(page_addr, build_id, size,
0111 page_addr + phdr[i].p_offset,
0112 phdr[i].p_filesz))
0113 return 0;
0114 }
0115 return -EINVAL;
0116 }
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126 int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
0127 __u32 *size)
0128 {
0129 Elf32_Ehdr *ehdr;
0130 struct page *page;
0131 void *page_addr;
0132 int ret;
0133
0134
0135 if (!vma->vm_file)
0136 return -EINVAL;
0137
0138 page = find_get_page(vma->vm_file->f_mapping, 0);
0139 if (!page)
0140 return -EFAULT;
0141
0142 ret = -EINVAL;
0143 page_addr = kmap_atomic(page);
0144 ehdr = (Elf32_Ehdr *)page_addr;
0145
0146
0147 if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
0148 goto out;
0149
0150
0151 if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
0152 goto out;
0153
0154 if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
0155 ret = get_build_id_32(page_addr, build_id, size);
0156 else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
0157 ret = get_build_id_64(page_addr, build_id, size);
0158 out:
0159 kunmap_atomic(page_addr);
0160 put_page(page);
0161 return ret;
0162 }
0163
0164
0165
0166
0167
0168
0169
0170
0171
0172 int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size)
0173 {
0174 return parse_build_id_buf(build_id, NULL, buf, buf_size);
0175 }
0176
0177 #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_CRASH_CORE)
0178 unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init;
0179
0180
0181
0182
0183 void __init init_vmlinux_build_id(void)
0184 {
0185 extern const void __start_notes __weak;
0186 extern const void __stop_notes __weak;
0187 unsigned int size = &__stop_notes - &__start_notes;
0188
0189 build_id_parse_buf(&__start_notes, vmlinux_build_id, size);
0190 }
0191 #endif