brd: Support discard
[platform/kernel/linux-rpi.git] / lib / buildid.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <linux/buildid.h>
4 #include <linux/cache.h>
5 #include <linux/elf.h>
6 #include <linux/kernel.h>
7 #include <linux/pagemap.h>
8
9 #define BUILD_ID 3
10
11 /*
12  * Parse build id from the note segment. This logic can be shared between
13  * 32-bit and 64-bit system, because Elf32_Nhdr and Elf64_Nhdr are
14  * identical.
15  */
16 static int parse_build_id_buf(unsigned char *build_id,
17                               __u32 *size,
18                               const void *note_start,
19                               Elf32_Word note_size)
20 {
21         Elf32_Word note_offs = 0, new_offs;
22
23         while (note_offs + sizeof(Elf32_Nhdr) < note_size) {
24                 Elf32_Nhdr *nhdr = (Elf32_Nhdr *)(note_start + note_offs);
25
26                 if (nhdr->n_type == BUILD_ID &&
27                     nhdr->n_namesz == sizeof("GNU") &&
28                     !strcmp((char *)(nhdr + 1), "GNU") &&
29                     nhdr->n_descsz > 0 &&
30                     nhdr->n_descsz <= BUILD_ID_SIZE_MAX) {
31                         memcpy(build_id,
32                                note_start + note_offs +
33                                ALIGN(sizeof("GNU"), 4) + sizeof(Elf32_Nhdr),
34                                nhdr->n_descsz);
35                         memset(build_id + nhdr->n_descsz, 0,
36                                BUILD_ID_SIZE_MAX - nhdr->n_descsz);
37                         if (size)
38                                 *size = nhdr->n_descsz;
39                         return 0;
40                 }
41                 new_offs = note_offs + sizeof(Elf32_Nhdr) +
42                         ALIGN(nhdr->n_namesz, 4) + ALIGN(nhdr->n_descsz, 4);
43                 if (new_offs <= note_offs)  /* overflow */
44                         break;
45                 note_offs = new_offs;
46         }
47
48         return -EINVAL;
49 }
50
51 static inline int parse_build_id(const void *page_addr,
52                                  unsigned char *build_id,
53                                  __u32 *size,
54                                  const void *note_start,
55                                  Elf32_Word note_size)
56 {
57         /* check for overflow */
58         if (note_start < page_addr || note_start + note_size < note_start)
59                 return -EINVAL;
60
61         /* only supports note that fits in the first page */
62         if (note_start + note_size > page_addr + PAGE_SIZE)
63                 return -EINVAL;
64
65         return parse_build_id_buf(build_id, size, note_start, note_size);
66 }
67
68 /* Parse build ID from 32-bit ELF */
69 static int get_build_id_32(const void *page_addr, unsigned char *build_id,
70                            __u32 *size)
71 {
72         Elf32_Ehdr *ehdr = (Elf32_Ehdr *)page_addr;
73         Elf32_Phdr *phdr;
74         int i;
75
76         /* only supports phdr that fits in one page */
77         if (ehdr->e_phnum >
78             (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
79                 return -EINVAL;
80
81         phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
82
83         for (i = 0; i < ehdr->e_phnum; ++i) {
84                 if (phdr[i].p_type == PT_NOTE &&
85                     !parse_build_id(page_addr, build_id, size,
86                                     page_addr + phdr[i].p_offset,
87                                     phdr[i].p_filesz))
88                         return 0;
89         }
90         return -EINVAL;
91 }
92
93 /* Parse build ID from 64-bit ELF */
94 static int get_build_id_64(const void *page_addr, unsigned char *build_id,
95                            __u32 *size)
96 {
97         Elf64_Ehdr *ehdr = (Elf64_Ehdr *)page_addr;
98         Elf64_Phdr *phdr;
99         int i;
100
101         /* only supports phdr that fits in one page */
102         if (ehdr->e_phnum >
103             (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
104                 return -EINVAL;
105
106         phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
107
108         for (i = 0; i < ehdr->e_phnum; ++i) {
109                 if (phdr[i].p_type == PT_NOTE &&
110                     !parse_build_id(page_addr, build_id, size,
111                                     page_addr + phdr[i].p_offset,
112                                     phdr[i].p_filesz))
113                         return 0;
114         }
115         return -EINVAL;
116 }
117
118 /*
119  * Parse build ID of ELF file mapped to vma
120  * @vma:      vma object
121  * @build_id: buffer to store build id, at least BUILD_ID_SIZE long
122  * @size:     returns actual build id size in case of success
123  *
124  * Return: 0 on success, -EINVAL otherwise
125  */
126 int build_id_parse(struct vm_area_struct *vma, unsigned char *build_id,
127                    __u32 *size)
128 {
129         Elf32_Ehdr *ehdr;
130         struct page *page;
131         void *page_addr;
132         int ret;
133
134         /* only works for page backed storage  */
135         if (!vma->vm_file)
136                 return -EINVAL;
137
138         page = find_get_page(vma->vm_file->f_mapping, 0);
139         if (!page)
140                 return -EFAULT; /* page not mapped */
141
142         ret = -EINVAL;
143         page_addr = kmap_atomic(page);
144         ehdr = (Elf32_Ehdr *)page_addr;
145
146         /* compare magic x7f "ELF" */
147         if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
148                 goto out;
149
150         /* only support executable file and shared object file */
151         if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
152                 goto out;
153
154         if (ehdr->e_ident[EI_CLASS] == ELFCLASS32)
155                 ret = get_build_id_32(page_addr, build_id, size);
156         else if (ehdr->e_ident[EI_CLASS] == ELFCLASS64)
157                 ret = get_build_id_64(page_addr, build_id, size);
158 out:
159         kunmap_atomic(page_addr);
160         put_page(page);
161         return ret;
162 }
163
164 /**
165  * build_id_parse_buf - Get build ID from a buffer
166  * @buf:      Elf note section(s) to parse
167  * @buf_size: Size of @buf in bytes
168  * @build_id: Build ID parsed from @buf, at least BUILD_ID_SIZE_MAX long
169  *
170  * Return: 0 on success, -EINVAL otherwise
171  */
172 int build_id_parse_buf(const void *buf, unsigned char *build_id, u32 buf_size)
173 {
174         return parse_build_id_buf(build_id, NULL, buf, buf_size);
175 }
176
177 #if IS_ENABLED(CONFIG_STACKTRACE_BUILD_ID) || IS_ENABLED(CONFIG_CRASH_CORE)
178 unsigned char vmlinux_build_id[BUILD_ID_SIZE_MAX] __ro_after_init;
179
180 /**
181  * init_vmlinux_build_id - Compute and stash the running kernel's build ID
182  */
183 void __init init_vmlinux_build_id(void)
184 {
185         extern const void __start_notes __weak;
186         extern const void __stop_notes __weak;
187         unsigned int size = &__stop_notes - &__start_notes;
188
189         build_id_parse_buf(&__start_notes, vmlinux_build_id, size);
190 }
191 #endif