]> git.ipfire.org Git - thirdparty/kmod.git/blob - libkmod/libkmod-elf.c
elf: fix typo that resulted in invalid uint reads for big-endian platforms.
[thirdparty/kmod.git] / libkmod / libkmod-elf.c
1 /*
2 * libkmod - interface to kernel module operations
3 *
4 * Copyright (C) 2011 ProFUSION embedded systems
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <elf.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25 #include <errno.h>
26
27 #include "libkmod.h"
28 #include "libkmod-private.h"
29
30 enum kmod_elf_class {
31 KMOD_ELF_32 = (1 << 1),
32 KMOD_ELF_64 = (1 << 2),
33 KMOD_ELF_LSB = (1 << 3),
34 KMOD_ELF_MSB = (1 << 4)
35 };
36
37 #ifdef WORDS_BIGENDIAN
38 static const enum kmod_elf_class native_endianess = KMOD_ELF_MSB;
39 #else
40 static const enum kmod_elf_class native_endianess = KMOD_ELF_LSB;
41 #endif
42
43 struct kmod_elf {
44 const uint8_t *memory;
45 uint8_t *changed;
46 uint64_t size;
47 enum kmod_elf_class class;
48 struct kmod_elf_header {
49 struct {
50 uint64_t offset;
51 uint16_t count;
52 uint16_t entry_size;
53 } section;
54 struct {
55 uint16_t section; /* index of the strings section */
56 uint64_t size;
57 uint64_t offset;
58 uint32_t nameoff; /* offset in strings itself */
59 } strings;
60 } header;
61 };
62
63 //#define ENABLE_ELFDBG 1
64
65 #if defined(ENABLE_LOGGING) && defined(ENABLE_ELFDBG)
66 #define ELFDBG(elf, ...) \
67 _elf_dbg(elf, __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__);
68
69 static inline void _elf_dbg(const struct kmod_elf *elf, const char *fname, unsigned line, const char *func, const char *fmt, ...)
70 {
71 va_list args;
72
73 fprintf(stderr, "ELFDBG-%d%c: %s:%u %s() ",
74 (elf->class & KMOD_ELF_32) ? 32 : 64,
75 (elf->class & KMOD_ELF_MSB) ? 'M' : 'L',
76 fname, line, func);
77 va_start(args, fmt);
78 vfprintf(stderr, fmt, args);
79 va_end(args);
80 }
81 #else
82 #define ELFDBG(elf, ...)
83 #endif
84
85
86 static int elf_identify(const void *memory, uint64_t size)
87 {
88 const uint8_t *p = memory;
89 int class = 0;
90
91 if (size <= EI_NIDENT || memcmp(p, ELFMAG, SELFMAG) != 0)
92 return -ENOEXEC;
93
94 switch (p[EI_CLASS]) {
95 case ELFCLASS32:
96 if (size <= sizeof(Elf32_Ehdr))
97 return -EINVAL;
98 class |= KMOD_ELF_32;
99 break;
100 case ELFCLASS64:
101 if (size <= sizeof(Elf64_Ehdr))
102 return -EINVAL;
103 class |= KMOD_ELF_64;
104 break;
105 default:
106 return -EINVAL;
107 }
108
109 switch (p[EI_DATA]) {
110 case ELFDATA2LSB:
111 class |= KMOD_ELF_LSB;
112 break;
113 case ELFDATA2MSB:
114 class |= KMOD_ELF_MSB;
115 break;
116 default:
117 return -EINVAL;
118 }
119
120 return class;
121 }
122
123 static inline uint64_t elf_get_uint(const struct kmod_elf *elf, uint64_t offset, uint16_t size)
124 {
125 const uint8_t *p;
126 uint64_t ret = 0;
127 size_t i;
128
129 assert(size <= sizeof(uint64_t));
130 assert(offset + size <= elf->size);
131 if (offset + size > elf->size) {
132 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
133 offset, size, offset + size, elf->size);
134 return (uint64_t)-1;
135 }
136
137 p = elf->memory + offset;
138 if (elf->class & KMOD_ELF_MSB) {
139 for (i = 0; i < size; i++)
140 ret = (ret << 8) | p[i];
141 } else {
142 for (i = 1; i <= size; i++)
143 ret = (ret << 8) | p[size - i];
144 }
145
146 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64"\n",
147 size, offset, ret);
148
149 return ret;
150 }
151
152 static inline int elf_set_uint(struct kmod_elf *elf, uint64_t offset, uint64_t size, uint64_t value)
153 {
154 uint8_t *p;
155 size_t i;
156
157 ELFDBG(elf, "size=%"PRIu16" offset=%"PRIu64" value=%"PRIu64" write memory=%p\n",
158 size, offset, value, elf->changed);
159
160 assert(size <= sizeof(uint64_t));
161 assert(offset + size <= elf->size);
162 if (offset + size > elf->size) {
163 ELFDBG(elf, "out of bounds: %"PRIu64" + %"PRIu16" = %"PRIu64"> %"PRIu64" (ELF size)\n",
164 offset, size, offset + size, elf->size);
165 return -1;
166 }
167
168 if (elf->changed == NULL) {
169 elf->changed = malloc(elf->size);
170 if (elf->changed == NULL)
171 return -errno;
172 memcpy(elf->changed, elf->memory, elf->size);
173 elf->memory = elf->changed;
174 ELFDBG(elf, "copied memory to allow writing.\n");
175 }
176
177 p = elf->changed + offset;
178 if (elf->class & KMOD_ELF_MSB) {
179 for (i = 1; i <= size; i++) {
180 p[size - i] = value & 0xff;
181 value = (value & 0xffffffffffffff00) >> 8;
182 }
183 } else {
184 for (i = 0; i < size; i++) {
185 p[i] = value & 0xff;
186 value = (value & 0xffffffffffffff00) >> 8;
187 }
188 }
189
190 return 0;
191 }
192
193 static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t offset)
194 {
195 assert(offset < elf->size);
196 if (offset >= elf->size) {
197 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
198 offset, elf->size);
199 return NULL;
200 }
201 return elf->memory + offset;
202 }
203
204 static inline const void *elf_get_section_header(const struct kmod_elf *elf, uint16_t idx)
205 {
206 assert(idx != SHN_UNDEF);
207 assert(idx < elf->header.section.count);
208 if (idx == SHN_UNDEF || idx >= elf->header.section.count) {
209 ELFDBG(elf, "invalid section number: %"PRIu16", last=%"PRIu16"\n",
210 idx, elf->header.section.count);
211 return NULL;
212 }
213 return elf_get_mem(elf, elf->header.section.offset +
214 idx * elf->header.section.entry_size);
215 }
216
217 static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
218 {
219 const uint8_t *p = elf_get_section_header(elf, idx);
220 uint64_t min_size, off = p - elf->memory;
221
222 if (p == NULL) {
223 ELFDBG(elf, "no section at %"PRIu16"\n", idx);
224 *offset = 0;
225 *size = 0;
226 *nameoff = 0;
227 return -EINVAL;
228 }
229
230 #define READV(field) \
231 elf_get_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field))
232
233 if (elf->class & KMOD_ELF_32) {
234 const Elf32_Shdr *hdr = (const Elf32_Shdr *)p;
235 *size = READV(sh_size);
236 *offset = READV(sh_offset);
237 *nameoff = READV(sh_name);
238 } else {
239 const Elf64_Shdr *hdr = (const Elf64_Shdr *)p;
240 *size = READV(sh_size);
241 *offset = READV(sh_offset);
242 *nameoff = READV(sh_name);
243 }
244 #undef READV
245
246 min_size = *offset + *size;
247 if (min_size >= elf->size) {
248 ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF size)\n",
249 min_size, elf->size);
250 return -EINVAL;
251 }
252
253 ELFDBG(elf, "section=%"PRIu16" is: offset=%"PRIu64" size=%"PRIu64" nameoff=%"PRIu32"\n",
254 idx, *offset, *size, *nameoff);
255
256 return 0;
257 }
258
259 static const char *elf_get_strings_section(const struct kmod_elf *elf, uint64_t *size)
260 {
261 *size = elf->header.strings.size;
262 return elf_get_mem(elf, elf->header.strings.offset);
263 }
264
265 struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
266 {
267 struct kmod_elf *elf;
268 size_t hdr_size, shdr_size, min_size;
269 int class;
270
271 assert(sizeof(uint16_t) == sizeof(Elf32_Half));
272 assert(sizeof(uint16_t) == sizeof(Elf64_Half));
273 assert(sizeof(uint32_t) == sizeof(Elf32_Word));
274 assert(sizeof(uint32_t) == sizeof(Elf64_Word));
275
276 class = elf_identify(memory, size);
277 if (class < 0) {
278 errno = -class;
279 return NULL;
280 }
281
282 elf = malloc(sizeof(struct kmod_elf));
283 if (elf == NULL) {
284 return NULL;
285 }
286
287 elf->memory = memory;
288 elf->changed = NULL;
289 elf->size = size;
290 elf->class = class;
291
292 #define READV(field) \
293 elf_get_uint(elf, offsetof(typeof(*hdr), field), sizeof(hdr->field))
294
295 #define LOAD_HEADER \
296 elf->header.section.offset = READV(e_shoff); \
297 elf->header.section.count = READV(e_shnum); \
298 elf->header.section.entry_size = READV(e_shentsize); \
299 elf->header.strings.section = READV(e_shstrndx)
300 if (elf->class & KMOD_ELF_32) {
301 const Elf32_Ehdr *hdr = elf_get_mem(elf, 0);
302 LOAD_HEADER;
303 hdr_size = sizeof(Elf32_Ehdr);
304 shdr_size = sizeof(Elf32_Shdr);
305 } else {
306 const Elf64_Ehdr *hdr = elf_get_mem(elf, 0);
307 LOAD_HEADER;
308 hdr_size = sizeof(Elf64_Ehdr);
309 shdr_size = sizeof(Elf64_Shdr);
310 }
311 #undef LOAD_HEADER
312 #undef READV
313
314 ELFDBG(elf, "section: offset=%"PRIu64" count=%"PRIu16" entry_size=%"PRIu16" strings index=%"PRIu16"\n",
315 elf->header.section.offset,
316 elf->header.section.count,
317 elf->header.section.entry_size,
318 elf->header.strings.section);
319
320 if (elf->header.section.entry_size != shdr_size) {
321 ELFDBG(elf, "unexpected section entry size: %"PRIu16", expected %"PRIu16"\n",
322 elf->header.section.entry_size, shdr_size);
323 goto invalid;
324 }
325 min_size = hdr_size + shdr_size * elf->header.section.count;
326 if (min_size >= elf->size) {
327 ELFDBG(elf, "file is too short to hold sections\n");
328 goto invalid;
329 }
330
331 if (elf_get_section_info(elf, elf->header.strings.section,
332 &elf->header.strings.offset,
333 &elf->header.strings.size,
334 &elf->header.strings.nameoff) < 0) {
335 ELFDBG(elf, "could not get strings section\n");
336 goto invalid;
337 } else {
338 uint64_t slen;
339 const char *s = elf_get_strings_section(elf, &slen);
340 if (slen == 0 || s[slen - 1] != '\0') {
341 ELFDBG(elf, "strings section does not ends with \\0\n");
342 goto invalid;
343 }
344 }
345
346 return elf;
347
348 invalid:
349 free(elf);
350 errno = EINVAL;
351 return NULL;
352 }
353
354 void kmod_elf_unref(struct kmod_elf *elf)
355 {
356 free(elf->changed);
357 free(elf);
358 }
359
360 const void *kmod_elf_get_memory(const struct kmod_elf *elf)
361 {
362 return elf->memory;
363 }
364
365 static int kmod_elf_get_section(const struct kmod_elf *elf, const char *section, const void **buf, size_t *buf_size)
366 {
367 uint64_t nameslen;
368 const char *names = elf_get_strings_section(elf, &nameslen);
369 uint16_t i;
370
371 *buf = NULL;
372 *buf_size = 0;
373
374 for (i = 1; i < elf->header.section.count; i++) {
375 uint64_t off, size;
376 uint32_t nameoff;
377 const char *n;
378 int err = elf_get_section_info(elf, i, &off, &size, &nameoff);
379 if (err < 0)
380 continue;
381 if (nameoff >= nameslen)
382 continue;
383 n = names + nameoff;
384 if (!streq(section, n))
385 continue;
386
387 *buf = elf_get_mem(elf, off);
388 *buf_size = size;
389 return 0;
390 }
391
392 return -ENOENT;
393 }
394
395 /* array will be allocated with strings in a single malloc, just free *array */
396 int kmod_elf_get_strings(const struct kmod_elf *elf, const char *section, char ***array)
397 {
398 uint64_t i, last, size;
399 const void *buf;
400 const char *strings;
401 char *itr, **a;
402 int count, err;
403
404 *array = NULL;
405
406 err = kmod_elf_get_section(elf, section, &buf, &size);
407 if (err < 0)
408 return err;
409 strings = buf;
410 if (strings == NULL || size == 0)
411 return 0;
412
413 /* skip zero padding */
414 while (strings[0] == '\0' && size > 1) {
415 strings++;
416 size--;
417 }
418 if (size <= 1)
419 return 0;
420
421 last = 0;
422 for (i = 0, count = 0; i < size; i++) {
423 if (strings[i] == '\0') {
424 if (last == i) {
425 last = i + 1;
426 continue;
427 }
428 count++;
429 last = i + 1;
430 }
431 }
432 if (strings[i - 1] != '\0')
433 count++;
434
435 *array = a = malloc(size + 1 + sizeof(char *) * count);
436 if (*array == NULL)
437 return -errno;
438
439 itr = (char *)(a + count);
440 last = 0;
441 for (i = 0, count = 0; i < size; i++) {
442 if (strings[i] == '\0') {
443 size_t slen = i - last;
444 if (last == i) {
445 last = i + 1;
446 continue;
447 }
448 a[count] = itr;
449 memcpy(itr, strings + last, slen);
450 itr[slen] = '\0';
451 itr += slen + 1;
452 count++;
453 last = i + 1;
454 }
455 }
456 if (strings[i - 1] != '\0') {
457 size_t slen = i - last;
458 a[count] = itr;
459 memcpy(itr, strings + last, slen);
460 itr[slen] = '\0';
461 itr += slen + 1;
462 count++;
463 }
464
465 return count;
466 }
467
468 /* array will be allocated with strings in a single malloc, just free *array */
469 int kmod_elf_get_modversions(const struct kmod_elf *elf, struct kmod_modversion **array)
470 {
471 uint64_t size, secsize, slen, off;
472 struct kmod_modversion *a;
473 const void *buf;
474 char *itr;
475 int i, count, err;
476 struct kmod_modversion32 {
477 uint32_t crc;
478 char name[64 - sizeof(uint32_t)];
479 };
480 struct kmod_modversion64 {
481 uint64_t crc;
482 char name[64 - sizeof(uint64_t)];
483 };
484
485 *array = NULL;
486
487 err = kmod_elf_get_section(elf, "__versions", &buf, &size);
488 if (err < 0)
489 return err;
490 if (buf == NULL || size == 0)
491 return 0;
492
493 if (elf->class & KMOD_ELF_32)
494 secsize = sizeof(struct kmod_modversion32);
495 else
496 secsize = sizeof(struct kmod_modversion64);
497
498 if (size % secsize != 0)
499 return -EINVAL;
500 count = size / secsize;
501
502 off = (const uint8_t *)buf - elf->memory;
503 slen = 0;
504 for (i = 0; i < count; i++, off += secsize) {
505 const char *symbol;
506 if (elf->class & KMOD_ELF_32) {
507 struct kmod_modversion32 *mv;
508 symbol = elf_get_mem(elf, off + sizeof(mv->crc));
509 } else {
510 struct kmod_modversion64 *mv;
511 symbol = elf_get_mem(elf, off + sizeof(mv->crc));
512 }
513 slen += strlen(symbol) + 1;
514 }
515
516 *array = a = malloc(sizeof(struct kmod_modversion) * count + slen);
517 if (*array == NULL)
518 return -errno;
519
520 itr = (char *)(a + count);
521 off = (const uint8_t *)buf - elf->memory;
522 for (i = 0; i < count; i++, off += secsize) {
523 uint64_t crc;
524 const char *symbol;
525 size_t symbollen;
526 if (elf->class & KMOD_ELF_32) {
527 struct kmod_modversion32 *mv;
528 crc = elf_get_uint(elf, off, sizeof(mv->crc));
529 symbol = elf_get_mem(elf, off + sizeof(mv->crc));
530 } else {
531 struct kmod_modversion64 *mv;
532 crc = elf_get_uint(elf, off, sizeof(mv->crc));
533 symbol = elf_get_mem(elf, off + sizeof(mv->crc));
534 }
535
536 a[i].crc = crc;
537 a[i].symbol = itr;
538 symbollen = strlen(symbol) + 1;
539 memcpy(itr, symbol, symbollen);
540 itr += symbollen;
541 }
542
543 return count;
544 }
545
546 int kmod_elf_strip_section(struct kmod_elf *elf, const char *section)
547 {
548 uint64_t size, off;
549 const void *buf;
550 int err = kmod_elf_get_section(elf, section, &buf, &size);
551 if (err < 0)
552 return err;
553
554 off = (const uint8_t *)buf - elf->memory;
555
556 #define WRITEV(field, value) \
557 elf_set_uint(elf, off + offsetof(typeof(*hdr), field), sizeof(hdr->field), value)
558 if (elf->class & KMOD_ELF_32) {
559 const Elf32_Shdr *hdr = buf;
560 uint32_t val = ~(uint32_t)SHF_ALLOC;
561 return WRITEV(sh_flags, val);
562 } else {
563 const Elf64_Shdr *hdr = buf;
564 uint64_t val = ~(uint64_t)SHF_ALLOC;
565 return WRITEV(sh_flags, val);
566 }
567 #undef WRITEV
568 }
569
570 int kmod_elf_strip_vermagic(struct kmod_elf *elf)
571 {
572 uint64_t i, size;
573 const void *buf;
574 const char *strings;
575 int err;
576
577 err = kmod_elf_get_section(elf, ".modinfo", &buf, &size);
578 if (err < 0)
579 return err;
580 strings = buf;
581 if (strings == NULL || size == 0)
582 return 0;
583
584 /* skip zero padding */
585 while (strings[0] == '\0' && size > 1) {
586 strings++;
587 size--;
588 }
589 if (size <= 1)
590 return 0;
591
592 for (i = 0; i < size; i++) {
593 const char *s;
594 size_t off, len;
595
596 if (strings[i] == '\0')
597 continue;
598 if (i + 1 >= size)
599 continue;
600
601 s = strings + i;
602 len = sizeof("vermagic=") - 1;
603 if (i + len >= size)
604 continue;
605 if (strncmp(s, "vermagic=", len) != 0) {
606 i += strlen(s);
607 continue;
608 }
609 s += len;
610 off = (const uint8_t *)s - elf->memory;
611
612 if (elf->changed == NULL) {
613 elf->changed = malloc(elf->size);
614 if (elf->changed == NULL)
615 return -errno;
616 memcpy(elf->changed, elf->memory, elf->size);
617 elf->memory = elf->changed;
618 ELFDBG(elf, "copied memory to allow writing.\n");
619 }
620
621 len = strlen(s);
622 ELFDBG(elf, "clear .modinfo vermagic \"%s\" (%zd bytes)\n",
623 s, len);
624 memset(elf->changed + off, '\0', len);
625 return 0;
626 }
627
628 ELFDBG(elf, "no vermagic found in .modinfo\n");
629 return -ENOENT;
630 }