Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0
2 :
3 : #include <linux/buildid.h>
4 : #include <linux/elf.h>
5 : #include <linux/pagemap.h>
6 :
7 : #define BUILD_ID 3
8 : /*
9 : * Parse build id from the note segment. This logic can be shared between
10 : * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
11 : * identical.
12 : */
13 0 : static inline int parse_build_id(void *page_addr,
14 : unsigned char *build_id,
15 : __u32 *size,
16 : void *note_start,
17 : Elf32_Word note_size)
18 : {
19 0 : Elf32_Word note_offs = 0, new_offs;
20 :
21 : /* check for overflow */
22 0 : if (note_start < page_addr || note_start + note_size < note_start)
23 : return -EINVAL;
24 :
25 : /* only supports note that fits in the first page */
26 0 : if (note_start + note_size > page_addr + PAGE_SIZE)
27 : return -EINVAL;
28 :
29 0 : while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
30 0 : Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
31 :
32 0 : if (nhdr->n_type == BUILD_ID &&
33 0 : nhdr->n_namesz == sizeof("GNU") &&
34 0 : nhdr->n_descsz > 0 &&
35 : nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
36 0 : memcpy(build_id,
37 : note_start + note_offs +
38 0 : ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
39 : nhdr->n_descsz);
40 0 : memset(build_id + nhdr->n_descsz, 0,
41 0 : BUILD_ID_SIZE_MAX - nhdr->n_descsz);
42 0 : if (size)
43 0 : *size = nhdr->n_descsz;
44 0 : return 0;
45 : }
46 0 : new_offs = note_offs + sizeof(Elf32_Nhdr) +
47 0 : ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
48 0 : if (new_offs <= note_offs) /* overflow */
49 : break;
50 : note_offs = new_offs;
51 : }
52 : return -EINVAL;
53 : }
54 :
55 : /* Parse build ID from 32-bit ELF */
56 0 : static int get_build_id_32(void *page_addr, unsigned char *build_id,
57 : __u32 *size)
58 : {
59 0 : Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
60 0 : Elf32_Phdr *phdr;
61 0 : int i;
62 :
63 : /* only supports phdr that fits in one page */
64 0 : if (ehdr->e_phnum >
65 : (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
66 : return -EINVAL;
67 :
68 0 : phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
69 :
70 0 : for (i = 0; i < ehdr->e_phnum; ++i) {
71 0 : if (phdr[i].p_type == PT_NOTE &&
72 0 : !parse_build_id(page_addr, build_id, size,
73 0 : page_addr + phdr[i].p_offset,
74 : phdr[i].p_filesz))
75 : return 0;
76 : }
77 : return -EINVAL;
78 : }
79 :
80 : /* Parse build ID from 64-bit ELF */
81 0 : static int get_build_id_64(void *page_addr, unsigned char *build_id,
82 : __u32 *size)
83 : {
84 0 : Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
85 0 : Elf64_Phdr *phdr;
86 0 : int i;
87 :
88 : /* only supports phdr that fits in one page */
89 0 : if (ehdr->e_phnum >
90 : (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
91 : return -EINVAL;
92 :
93 0 : phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
94 :
95 0 : for (i = 0; i < ehdr->e_phnum; ++i) {
96 0 : if (phdr[i].p_type == PT_NOTE &&
97 0 : !parse_build_id(page_addr, build_id, size,
98 0 : page_addr + phdr[i].p_offset,
99 0 : phdr[i].p_filesz))
100 : return 0;
101 : }
102 : return -EINVAL;
103 : }
104 :
105 : /*
106 : * Parse build ID of ELF file mapped to vma
107 : * @vma: vma object
108 : * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
109 : * @size: returns actual build id size in case of success
110 : *
111 : * Returns 0 on success, otherwise error (< 0).
112 : */
113 0 : int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
114 : __u32 *size)
115 : {
116 0 : Elf32_Ehdr *ehdr;
117 0 : struct page *page;
118 0 : void *page_addr;
119 0 : int ret;
120 :
121 : /* only works for page backed storage */
122 0 : if (!vma->vm_file)
123 : return -EINVAL;
124 :
125 0 : page = find_get_page(vma->vm_file->f_mapping, 0);
126 0 : if (!page)
127 : return -EFAULT; /* page not mapped */
128 :
129 0 : ret = -EINVAL;
130 0 : page_addr = kmap_atomic(page);
131 0 : ehdr = (Elf32_Ehdr *)page_addr;
132 :
133 : /* compare magic x7f "ELF" */
134 0 : if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
135 0 : goto out;
136 :
137 : /* only support executable file and shared object file */
138 0 : if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
139 0 : goto out;
140 :
141 0 : if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
142 0 : ret = get_build_id_32(page_addr, build_id, size);
143 0 : else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
144 0 : ret = get_build_id_64(page_addr, build_id, size);
145 0 : out:
146 0 : kunmap_atomic(page_addr);
147 0 : put_page(page);
148 0 : return ret;
149 : }
|