1bd7525daSJiri Olsa // SPDX-License-Identifier: GPL-2.0
2bd7525daSJiri Olsa
3bd7525daSJiri Olsa #include <linux/buildid.h>
483cc6fa0SStephen Boyd #include <linux/cache.h>
5bd7525daSJiri Olsa #include <linux/elf.h>
67eaf3cf3SStephen Boyd #include <linux/kernel.h>
7bd7525daSJiri Olsa #include <linux/pagemap.h>
8bd7525daSJiri Olsa
9bd7525daSJiri Olsa #define BUILD_ID 3
107eaf3cf3SStephen Boyd
11bd7525daSJiri Olsa /*
12bd7525daSJiri Olsa * Parse build id from the note segment. This logic can be shared between
13bd7525daSJiri Olsa * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
14bd7525daSJiri Olsa * identical.
15bd7525daSJiri Olsa */
parse_build_id_buf(unsigned char * build_id,__u32 * size,const void * note_start,Elf32_Word note_size)167eaf3cf3SStephen Boyd static int parse_build_id_buf(unsigned char *build_id,
17921f88fcSJiri Olsa __u32 *size,
187eaf3cf3SStephen Boyd const void *note_start,
19bd7525daSJiri Olsa Elf32_Word note_size)
20bd7525daSJiri Olsa {
21c83a80d8SAndrii Nakryiko const char note_name[] = "GNU";
22c83a80d8SAndrii Nakryiko const size_t note_name_sz = sizeof(note_name);
23c83a80d8SAndrii Nakryiko u64 note_off = 0, new_off, name_sz, desc_sz;
24c83a80d8SAndrii Nakryiko const char *data;
25bd7525daSJiri Olsa
26c83a80d8SAndrii Nakryiko while (note_off + sizeof(Elf32_Nhdr) < note_size &&
27c83a80d8SAndrii Nakryiko note_off + sizeof(Elf32_Nhdr) > note_off /* overflow */) {
28c83a80d8SAndrii Nakryiko Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_off);
29c83a80d8SAndrii Nakryiko
30c83a80d8SAndrii Nakryiko name_sz = READ_ONCE(nhdr->n_namesz);
31c83a80d8SAndrii Nakryiko desc_sz = READ_ONCE(nhdr->n_descsz);
32c83a80d8SAndrii Nakryiko
33c83a80d8SAndrii Nakryiko new_off = note_off + sizeof(Elf32_Nhdr);
34c83a80d8SAndrii Nakryiko if (check_add_overflow(new_off, ALIGN(name_sz, 4), &new_off) ||
35c83a80d8SAndrii Nakryiko check_add_overflow(new_off, ALIGN(desc_sz, 4), &new_off) ||
36c83a80d8SAndrii Nakryiko new_off > note_size)
37c83a80d8SAndrii Nakryiko break;
38bd7525daSJiri Olsa
39bd7525daSJiri Olsa if (nhdr->n_type == BUILD_ID &&
40c83a80d8SAndrii Nakryiko name_sz == note_name_sz &&
41c83a80d8SAndrii Nakryiko memcmp(nhdr + 1, note_name, note_name_sz) == 0 &&
42c83a80d8SAndrii Nakryiko desc_sz > 0 && desc_sz <= BUILD_ID_SIZE_MAX) {
43*efb258ecSJiri Olsa data = note_start + note_off + sizeof(Elf32_Nhdr) + ALIGN(note_name_sz, 4);
44c83a80d8SAndrii Nakryiko memcpy(build_id, data, desc_sz);
45c83a80d8SAndrii Nakryiko memset(build_id + desc_sz, 0, BUILD_ID_SIZE_MAX - desc_sz);
46921f88fcSJiri Olsa if (size)
47c83a80d8SAndrii Nakryiko *size = desc_sz;
48bd7525daSJiri Olsa return 0;
49bd7525daSJiri Olsa }
50c83a80d8SAndrii Nakryiko
51c83a80d8SAndrii Nakryiko note_off = new_off;
52bd7525daSJiri Olsa }
537eaf3cf3SStephen Boyd
54bd7525daSJiri Olsa return -EINVAL;
55bd7525daSJiri Olsa }
56bd7525daSJiri Olsa
parse_build_id(const void * page_addr,unsigned char * build_id,__u32 * size,const void * note_start,Elf32_Word note_size)5760eec326SStephen Boyd static inline int parse_build_id(const void *page_addr,
587eaf3cf3SStephen Boyd unsigned char *build_id,
597eaf3cf3SStephen Boyd __u32 *size,
6060eec326SStephen Boyd const void *note_start,
617eaf3cf3SStephen Boyd Elf32_Word note_size)
627eaf3cf3SStephen Boyd {
637eaf3cf3SStephen Boyd /* check for overflow */
647eaf3cf3SStephen Boyd if (note_start < page_addr || note_start + note_size < note_start)
657eaf3cf3SStephen Boyd return -EINVAL;
667eaf3cf3SStephen Boyd
677eaf3cf3SStephen Boyd /* only supports note that fits in the first page */
687eaf3cf3SStephen Boyd if (note_start + note_size > page_addr + PAGE_SIZE)
697eaf3cf3SStephen Boyd return -EINVAL;
707eaf3cf3SStephen Boyd
717eaf3cf3SStephen Boyd return parse_build_id_buf(build_id, size, note_start, note_size);
727eaf3cf3SStephen Boyd }
737eaf3cf3SStephen Boyd
74bd7525daSJiri Olsa /* Parse build ID from 32-bit ELF */
get_build_id_32(const void * page_addr,unsigned char * build_id,__u32 * size)7560eec326SStephen Boyd static int get_build_id_32(const void *page_addr, unsigned char *build_id,
76921f88fcSJiri Olsa __u32 *size)
77bd7525daSJiri Olsa {
78bd7525daSJiri Olsa Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
79bd7525daSJiri Olsa Elf32_Phdr *phdr;
80c83a80d8SAndrii Nakryiko __u32 i, phnum;
81bd7525daSJiri Olsa
82f941d779SAlexey Dobriyan /*
83f941d779SAlexey Dobriyan * FIXME
84f941d779SAlexey Dobriyan * Neither ELF spec nor ELF loader require that program headers
85f941d779SAlexey Dobriyan * start immediately after ELF header.
86f941d779SAlexey Dobriyan */
87f941d779SAlexey Dobriyan if (ehdr->e_phoff != sizeof(Elf32_Ehdr))
88f941d779SAlexey Dobriyan return -EINVAL;
89c83a80d8SAndrii Nakryiko
90c83a80d8SAndrii Nakryiko phnum = READ_ONCE(ehdr->e_phnum);
91bd7525daSJiri Olsa /* only supports phdr that fits in one page */
92c83a80d8SAndrii Nakryiko if (phnum > (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
93bd7525daSJiri Olsa return -EINVAL;
94bd7525daSJiri Olsa
95bd7525daSJiri Olsa phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
96bd7525daSJiri Olsa
97c83a80d8SAndrii Nakryiko for (i = 0; i < phnum; ++i) {
98bd7525daSJiri Olsa if (phdr[i].p_type == PT_NOTE &&
99921f88fcSJiri Olsa !parse_build_id(page_addr, build_id, size,
100c83a80d8SAndrii Nakryiko page_addr + READ_ONCE(phdr[i].p_offset),
101c83a80d8SAndrii Nakryiko READ_ONCE(phdr[i].p_filesz)))
102bd7525daSJiri Olsa return 0;
103bd7525daSJiri Olsa }
104bd7525daSJiri Olsa return -EINVAL;
105bd7525daSJiri Olsa }
106bd7525daSJiri Olsa
107bd7525daSJiri Olsa /* Parse build ID from 64-bit ELF */
get_build_id_64(const void * page_addr,unsigned char * build_id,__u32 * size)10860eec326SStephen Boyd static int get_build_id_64(const void *page_addr, unsigned char *build_id,
109921f88fcSJiri Olsa __u32 *size)
110bd7525daSJiri Olsa {
111bd7525daSJiri Olsa Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
112bd7525daSJiri Olsa Elf64_Phdr *phdr;
113c83a80d8SAndrii Nakryiko __u32 i, phnum;
114bd7525daSJiri Olsa
115f941d779SAlexey Dobriyan /*
116f941d779SAlexey Dobriyan * FIXME
117f941d779SAlexey Dobriyan * Neither ELF spec nor ELF loader require that program headers
118f941d779SAlexey Dobriyan * start immediately after ELF header.
119f941d779SAlexey Dobriyan */
120f941d779SAlexey Dobriyan if (ehdr->e_phoff != sizeof(Elf64_Ehdr))
121f941d779SAlexey Dobriyan return -EINVAL;
122c83a80d8SAndrii Nakryiko
123c83a80d8SAndrii Nakryiko phnum = READ_ONCE(ehdr->e_phnum);
124bd7525daSJiri Olsa /* only supports phdr that fits in one page */
125c83a80d8SAndrii Nakryiko if (phnum > (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
126bd7525daSJiri Olsa return -EINVAL;
127bd7525daSJiri Olsa
128bd7525daSJiri Olsa phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
129bd7525daSJiri Olsa
130c83a80d8SAndrii Nakryiko for (i = 0; i < phnum; ++i) {
131bd7525daSJiri Olsa if (phdr[i].p_type == PT_NOTE &&
132921f88fcSJiri Olsa !parse_build_id(page_addr, build_id, size,
133c83a80d8SAndrii Nakryiko page_addr + READ_ONCE(phdr[i].p_offset),
134c83a80d8SAndrii Nakryiko READ_ONCE(phdr[i].p_filesz)))
135bd7525daSJiri Olsa return 0;
136bd7525daSJiri Olsa }
137bd7525daSJiri Olsa return -EINVAL;
138bd7525daSJiri Olsa }
139bd7525daSJiri Olsa
140921f88fcSJiri Olsa /*
141921f88fcSJiri Olsa * Parse build ID of ELF file mapped to vma
142921f88fcSJiri Olsa * @vma: vma object
143921f88fcSJiri Olsa * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
144921f88fcSJiri Olsa * @size: returns actual build id size in case of success
145921f88fcSJiri Olsa *
1463f14d029SStephen Boyd * Return: 0 on success, -EINVAL otherwise
147921f88fcSJiri Olsa */
build_id_parse(struct vm_area_struct * vma,unsigned char * build_id,__u32 * size)148921f88fcSJiri Olsa int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
149921f88fcSJiri Olsa __u32 *size)
150bd7525daSJiri Olsa {
151bd7525daSJiri Olsa Elf32_Ehdr *ehdr;
152bd7525daSJiri Olsa struct page *page;
153bd7525daSJiri Olsa void *page_addr;
154bd7525daSJiri Olsa int ret;
155bd7525daSJiri Olsa
156bd7525daSJiri Olsa /* only works for page backed storage */
157bd7525daSJiri Olsa if (!vma->vm_file)
158bd7525daSJiri Olsa return -EINVAL;
159bd7525daSJiri Olsa
160bd7525daSJiri Olsa page = find_get_page(vma->vm_file->f_mapping, 0);
161bd7525daSJiri Olsa if (!page)
162bd7525daSJiri Olsa return -EFAULT; /* page not mapped */
163c83a80d8SAndrii Nakryiko if (!PageUptodate(page)) {
164c83a80d8SAndrii Nakryiko put_page(page);
165c83a80d8SAndrii Nakryiko return -EFAULT;
166c83a80d8SAndrii Nakryiko }
167bd7525daSJiri Olsa
168bd7525daSJiri Olsa ret = -EINVAL;
169bd7525daSJiri Olsa page_addr = kmap_atomic(page);
170bd7525daSJiri Olsa ehdr = (Elf32_Ehdr *)page_addr;
171bd7525daSJiri Olsa
172bd7525daSJiri Olsa /* compare magic x7f "ELF" */
173bd7525daSJiri Olsa if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
174bd7525daSJiri Olsa goto out;
175bd7525daSJiri Olsa
176bd7525daSJiri Olsa /* only support executable file and shared object file */
177bd7525daSJiri Olsa if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
178bd7525daSJiri Olsa goto out;
179bd7525daSJiri Olsa
180bd7525daSJiri Olsa if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
181921f88fcSJiri Olsa ret = get_build_id_32(page_addr, build_id, size);
182bd7525daSJiri Olsa else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
183921f88fcSJiri Olsa ret = get_build_id_64(page_addr, build_id, size);
184bd7525daSJiri Olsa out:
185bd7525daSJiri Olsa kunmap_atomic(page_addr);
186bd7525daSJiri Olsa put_page(page);
187bd7525daSJiri Olsa return ret;
188bd7525daSJiri Olsa }
1897eaf3cf3SStephen Boyd
1907eaf3cf3SStephen Boyd /**
1917eaf3cf3SStephen Boyd * build_id_parse_buf - Get build ID from a buffer
19270e79866SAlexey Dobriyan * @buf: ELF note section(s) to parse
1937eaf3cf3SStephen Boyd * @buf_size: Size of @buf in bytes
1947eaf3cf3SStephen Boyd * @build_id: Build ID parsed from @buf, at least BUILD_ID_SIZE_MAX long
1957eaf3cf3SStephen Boyd *
1967eaf3cf3SStephen Boyd * Return: 0 on success, -EINVAL otherwise
1977eaf3cf3SStephen Boyd */
build_id_parse_buf(const void * buf,unsigned char * build_id,u32 buf_size)1987eaf3cf3SStephen Boyd int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size)
1997eaf3cf3SStephen Boyd {
2007eaf3cf3SStephen Boyd return parse_build_id_buf(build_id, NULL, buf, buf_size);
2017eaf3cf3SStephen Boyd }
20283cc6fa0SStephen Boyd
20344e8a5e9SStephen Boyd #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_CRASH_CORE)
20483cc6fa0SStephen Boyd unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init;
20583cc6fa0SStephen Boyd
20683cc6fa0SStephen Boyd /**
20783cc6fa0SStephen Boyd * init_vmlinux_build_id - Compute and stash the running kernel's build ID
20883cc6fa0SStephen Boyd */
init_vmlinux_build_id(void)20983cc6fa0SStephen Boyd void __init init_vmlinux_build_id(void)
21083cc6fa0SStephen Boyd {
21183cc6fa0SStephen Boyd extern const void __start_notes __weak;
21283cc6fa0SStephen Boyd extern const void __stop_notes __weak;
21383cc6fa0SStephen Boyd unsigned int size = &__stop_notes - &__start_notes;
21483cc6fa0SStephen Boyd
21583cc6fa0SStephen Boyd build_id_parse_buf(&__start_notes, vmlinux_build_id, size);
21683cc6fa0SStephen Boyd }
21722f4e66dSStephen Boyd #endif
218