1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qelfparser_p.h"
6
7#ifdef Q_OF_ELF
8
9#include "qlibrary_p.h"
10
11#include <qloggingcategory.h>
12#include <qnumeric.h>
13#include <qsysinfo.h>
14
15#if __has_include(<elf.h>)
16# include <elf.h>
17#elif __has_include(<sys/elf.h>)
18# include <sys/elf.h>
19#else
20# error "Need ELF header to parse plugins."
21#endif
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27// ### Qt7: propagate the constant and eliminate dead code
28static constexpr bool ElfNotesAreMandatory = QT_VERSION >= QT_VERSION_CHECK(7,0,0);
29
30// Whether we include some extra validity checks
31// (checks to ensure we don't read out-of-bounds are always included)
32static constexpr bool IncludeValidityChecks = true;
33
34#ifdef QT_BUILD_INTERNAL
35# define QELFPARSER_DEBUG
36#endif
37#if defined(QELFPARSER_DEBUG)
38static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser")
39# define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
40#else
41# define qEDebug if (false) {} else QNoDebug()
42#endif
43
44#ifndef PT_GNU_EH_FRAME
45# define PT_GNU_EH_FRAME 0x6474e550
46#endif
47#ifndef PT_GNU_STACK
48# define PT_GNU_STACK 0x6474e551
49#endif
50#ifndef PT_GNU_RELRO
51# define PT_GNU_RELRO 0x6474e552
52#endif
53#ifndef PT_GNU_PROPERTY
54# define PT_GNU_PROPERTY 0x6474e553
55#endif
56
57QT_WARNING_PUSH
58QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
59
60namespace {
61template <QSysInfo::Endian Order> struct ElfEndianTraits
62{
63 static constexpr unsigned char DataOrder = ELFDATA2LSB;
64 template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
65};
66template <> struct ElfEndianTraits<QSysInfo::BigEndian>
67{
68 static constexpr unsigned char DataOrder = ELFDATA2MSB;
69 template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
70};
71
72template <typename EquivalentPointerType> struct ElfTypeTraits
73{
74 static constexpr unsigned char Class = ELFCLASS64;
75
76 // integer types
77 using Half = Elf64_Half;
78 using Word = Elf64_Word;
79 using Addr = Elf64_Addr;
80 using Off = Elf64_Off;
81
82 // structure types
83 using Ehdr = Elf64_Ehdr;
84 using Shdr = Elf64_Shdr;
85 using Phdr = Elf64_Phdr;
86 using Nhdr = Elf64_Nhdr;
87};
88template <> struct ElfTypeTraits<quint32>
89{
90 static constexpr unsigned char Class = ELFCLASS32;
91
92 // integer types
93 using Half = Elf32_Half;
94 using Word = Elf32_Word;
95 using Addr = Elf32_Addr;
96 using Off = Elf32_Off;
97
98 // structure types
99 using Ehdr = Elf32_Ehdr;
100 using Shdr = Elf32_Shdr;
101 using Phdr = Elf32_Phdr;
102 using Nhdr = Elf32_Nhdr;
103};
104
105struct ElfMachineCheck
106{
107 static const Elf32_Half ExpectedMachine =
108#if 0
109 // nothing
110#elif defined(Q_PROCESSOR_ALPHA)
111 EM_ALPHA
112#elif defined(Q_PROCESSOR_ARM_32)
113 EM_ARM
114#elif defined(Q_PROCESSOR_ARM_64)
115 EM_AARCH64
116#elif defined(Q_PROCESSOR_BLACKFIN)
117 EM_BLACKFIN
118#elif defined(Q_PROCESSOR_HPPA)
119 EM_PARISC
120#elif defined(Q_PROCESSOR_IA64)
121 EM_IA_64
122#elif defined(Q_PROCESSOR_LOONGARCH)
123 EM_LOONGARCH
124#elif defined(Q_PROCESSOR_M68K)
125 EM_68K
126#elif defined(Q_PROCESSOR_MIPS)
127 EM_MIPS
128#elif defined(Q_PROCESSOR_POWER_32)
129 EM_PPC
130#elif defined(Q_PROCESSOR_POWER_64)
131 EM_PPC64
132#elif defined(Q_PROCESSOR_RISCV)
133 EM_RISCV
134#elif defined(Q_PROCESSOR_S390)
135 EM_S390
136#elif defined(Q_PROCESSOR_SH)
137 EM_SH
138#elif defined(Q_PROCESSOR_SPARC_V9)
139 EM_SPARCV9
140#elif defined(Q_PROCESSOR_SPARC_64)
141 EM_SPARCV9
142#elif defined(Q_PROCESSOR_SPARC)
143 EM_SPARC
144#elif defined(Q_PROCESSOR_WASM)
145#elif defined(Q_PROCESSOR_X86_32)
146 EM_386
147#elif defined(Q_PROCESSOR_X86_64)
148 EM_X86_64
149#else
150# error "Unknown Q_PROCESSOR_xxx macro, please update."
151 EM_NONE
152#endif
153 ;
154};
155
156struct ElfHeaderCommonCheck
157{
158 static_assert(std::is_same_v<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
159 "e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
160
161 // bytes 0-3
162 static bool checkElfMagic(const uchar *ident)
163 {
164 return memcmp(s1: ident, ELFMAG, SELFMAG) == 0;
165 }
166
167 // byte 6
168 static bool checkElfVersion(const uchar *ident)
169 {
170 uchar elfversion = ident[EI_VERSION];
171 return elfversion == EV_CURRENT;
172 }
173
174 struct CommonHeader {
175 Elf32_Half type;
176 Elf32_Half machine;
177 Elf32_Word version;
178 };
179};
180
181template <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
182struct ElfHeaderCheck : public ElfHeaderCommonCheck
183{
184 using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
185 using EndianTraits = ElfEndianTraits<Order>;
186 using Ehdr = typename TypeTraits::Ehdr;
187
188 // byte 4
189 static bool checkClass(const uchar *ident)
190 {
191 uchar klass = ident[EI_CLASS];
192 return klass == TypeTraits::Class;
193 }
194
195 // byte 5
196 static bool checkDataOrder(const uchar *ident)
197 {
198 uchar data = ident[EI_DATA];
199 return data == EndianTraits::DataOrder;
200 }
201
202 // byte 7
203 static bool checkOsAbi(const uchar *ident)
204 {
205 uchar osabi = ident[EI_OSABI];
206 // we don't check
207 Q_UNUSED(osabi);
208 return true;
209 }
210
211 // byte 8
212 static bool checkAbiVersion(const uchar *ident)
213 {
214 uchar abiversion = ident[EI_ABIVERSION];
215 // we don't check (and I don't know anyone who uses this)
216 Q_UNUSED(abiversion);
217 return true;
218 }
219
220 // bytes 9-16
221 static bool checkPadding(const uchar *ident)
222 {
223 // why would we check this?
224 Q_UNUSED(ident);
225 return true;
226 }
227
228 static bool checkIdent(const Ehdr &header)
229 {
230 return checkElfMagic(ident: header.e_ident)
231 && checkClass(ident: header.e_ident)
232 && checkDataOrder(ident: header.e_ident)
233 && checkElfVersion(ident: header.e_ident)
234 && checkOsAbi(ident: header.e_ident)
235 && checkAbiVersion(ident: header.e_ident)
236 && checkPadding(ident: header.e_ident);
237 }
238
239 static bool checkType(const Ehdr &header)
240 {
241 return header.e_type == ET_DYN;
242 }
243
244 static bool checkMachine(const Ehdr &header)
245 {
246 return header.e_machine == ElfMachineCheck::ExpectedMachine;
247 }
248
249 static bool checkFileVersion(const Ehdr &header)
250 {
251 return header.e_version == EV_CURRENT;
252 }
253
254 static bool checkHeader(const Ehdr &header)
255 {
256 if (!checkIdent(header))
257 return false;
258 if (!IncludeValidityChecks)
259 return true;
260 return checkType(header)
261 && checkMachine(header)
262 && checkFileVersion(header);
263 }
264
265 Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header)
266 {
267 if (!checkElfMagic(ident: header.e_ident))
268 return QLibrary::tr(s: "invalid signature");
269 if (!checkClass(ident: header.e_ident))
270 return QLibrary::tr(s: "file is for a different word size");
271 if (!checkDataOrder(ident: header.e_ident))
272 return QLibrary::tr(s: "file is for the wrong endianness");
273 if (!checkElfVersion(ident: header.e_ident) || !checkFileVersion(header))
274 return QLibrary::tr(s: "file has an unknown ELF version");
275 if (!checkOsAbi(ident: header.e_ident) || !checkAbiVersion(ident: header.e_ident))
276 return QLibrary::tr(s: "file has an unexpected ABI");
277 if (!checkType(header))
278 return QLibrary::tr(s: "file is not a shared object");
279 if (!checkMachine(header))
280 return QLibrary::tr(s: "file is for a different processor");
281 return QString();
282 }
283
284 static CommonHeader extractCommonHeader(const uchar *data)
285 {
286 auto header = reinterpret_cast<const Ehdr *>(data);
287 CommonHeader r;
288 r.type = EndianTraits::fromEndian(header->e_type);
289 r.machine = EndianTraits::fromEndian(header->e_machine);
290 r.version = EndianTraits::fromEndian(header->e_version);
291 return r;
292 }
293};
294
295struct ElfHeaderDebug { const uchar *e_ident; };
296Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeaderDebug h)
297{
298 const uchar *e_ident = h.e_ident;
299 if (!ElfHeaderCommonCheck::checkElfMagic(ident: e_ident)) {
300 d << "Not an ELF file (invalid signature)";
301 return d;
302 }
303
304 QDebugStateSaver saver(d);
305 d.nospace();
306 quint8 elfclass = e_ident[EI_CLASS];
307 switch (elfclass) {
308 case ELFCLASSNONE:
309 default:
310 d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), ";
311 break;
312 case ELFCLASS32:
313 d << "ELF 32-bit ";
314 break;
315 case ELFCLASS64:
316 d << "ELF 64-bit ";
317 break;
318 }
319
320 quint8 dataorder = e_ident[EI_DATA];
321 switch (dataorder) {
322 case ELFDATANONE:
323 default:
324 d << "invalid endianness (" << e_ident[EI_DATA] << ')';
325 break;
326 case ELFDATA2LSB:
327 d << "LSB";
328 break;
329 case ELFDATA2MSB:
330 d << "MSB";
331 break;
332 }
333
334 switch (e_ident[EI_OSABI]) {
335 case ELFOSABI_SYSV: d << " (SYSV"; break;
336 case ELFOSABI_HPUX: d << " (HP-UX"; break;
337 case ELFOSABI_NETBSD: d << " (NetBSD"; break;
338 case ELFOSABI_LINUX: d << " (GNU/Linux"; break;
339 case ELFOSABI_SOLARIS: d << " (Solaris"; break;
340 case ELFOSABI_AIX: d << " (AIX"; break;
341 case ELFOSABI_IRIX: d << " (IRIX"; break;
342 case ELFOSABI_FREEBSD: d << " (FreeBSD"; break;
343 case ELFOSABI_OPENBSD: d << " (OpenBSD"; break;
344 default: d << " (OS ABI " << e_ident[EI_VERSION]; break;
345 }
346
347 if (e_ident[EI_ABIVERSION])
348 d << " v" << e_ident[EI_ABIVERSION];
349 d << ')';
350
351 if (e_ident[EI_VERSION] != 1) {
352 d << ", file version " << e_ident[EI_VERSION];
353 return d;
354 }
355
356 ElfHeaderCommonCheck::CommonHeader r;
357 if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
358 r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(data: e_ident);
359 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
360 r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(data: e_ident);
361 else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
362 r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(data: e_ident);
363 else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
364 r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(data: e_ident);
365 else
366 return d;
367
368 d << ", version " << r.version;
369
370 switch (r.type) {
371 case ET_NONE: d << ", no type"; break;
372 case ET_REL: d << ", relocatable"; break;
373 case ET_EXEC: d << ", executable"; break;
374 case ET_DYN: d << ", shared library or PIC executable"; break;
375 case ET_CORE: d << ", core dump"; break;
376 default: d << ", unknown type " << r.type; break;
377 }
378
379 switch (r.machine) {
380 // list definitely not exhaustive!
381 case EM_NONE: d << ", no machine"; break;
382 case EM_ALPHA: d << ", Alpha"; break;
383 case EM_68K: d << ", MC68000"; break;
384 case EM_ARM: d << ", ARM"; break;
385 case EM_AARCH64: d << ", AArch64"; break;
386#ifdef EM_BLACKFIN
387 case EM_BLACKFIN: d << ", Blackfin"; break;
388#endif
389 case EM_IA_64: d << ", IA-64"; break;
390#ifdef EM_LOONGARCH
391 case EM_LOONGARCH: d << ", LoongArch"; break;
392#endif
393 case EM_MIPS: d << ", MIPS"; break;
394 case EM_PARISC: d << ", HPPA"; break;
395 case EM_PPC: d << ", PowerPC"; break;
396 case EM_PPC64: d << ", PowerPC 64-bit"; break;
397#ifdef EM_RISCV
398 case EM_RISCV: d << ", RISC-V"; break;
399#endif
400#ifdef EM_S390
401 case EM_S390: d << ", S/390"; break;
402#endif
403 case EM_SH: d << ", SuperH"; break;
404 case EM_SPARC: d << ", SPARC"; break;
405 case EM_SPARCV9: d << ", SPARCv9"; break;
406 case EM_386: d << ", i386"; break;
407 case EM_X86_64: d << ", x86-64"; break;
408 default: d << ", other machine type " << r.machine; break;
409 }
410
411 return d;
412}
413
414struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
415Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
416{
417 // not exhaustive, just a few common things
418 QDebugStateSaver saver(d);
419 d << Qt::hex << Qt::showbase;
420 d << "type";
421 switch (s.shdr->sh_type) {
422 case SHT_NULL: d << "NULL"; break;
423 case SHT_PROGBITS: d << "PROGBITS"; break;
424 case SHT_SYMTAB: d << "SYMTAB"; break;
425 case SHT_STRTAB: d << "STRTAB"; break;
426 case SHT_RELA: d << "RELA"; break;
427 case SHT_HASH: d << "HASH"; break;
428 case SHT_DYNAMIC: d << "DYNAMIC"; break;
429 case SHT_NOTE: d << "NOTE"; break;
430 case SHT_NOBITS: d << "NOBITS"; break;
431 case SHT_DYNSYM: d << "DYNSYM"; break;
432 case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break;
433 case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break;
434 default: d << s.shdr->sh_type;
435 }
436
437 d << "flags";
438 d.nospace();
439 if (s.shdr->sh_flags & SHF_WRITE)
440 d << 'W';
441 if (s.shdr->sh_flags & SHF_ALLOC)
442 d << 'A';
443 if (s.shdr->sh_flags & SHF_EXECINSTR)
444 d << 'X';
445 if (s.shdr->sh_flags & SHF_STRINGS)
446 d << 'S';
447 if (s.shdr->sh_flags & SHF_TLS)
448 d << 'T';
449
450 d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size;
451 return d;
452}
453
454struct ElfProgramDebug { const ElfHeaderCheck<>::TypeTraits::Phdr *phdr; };
455Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfProgramDebug p)
456{
457 QDebugStateSaver saved(d);
458 d << Qt::hex << Qt::showbase << "program";
459 switch (p.phdr->p_type) {
460 case PT_NULL: d << "NULL"; break;
461 case PT_LOAD: d << "LOAD"; break;
462 case PT_DYNAMIC: d << "DYNAMIC"; break;
463 case PT_INTERP: d << "INTERP"; break;
464 case PT_NOTE: d << "NOTE"; break;
465 case PT_PHDR: d << "PHDR"; break;
466 case PT_TLS: d << "TLS"; break;
467 case PT_GNU_EH_FRAME: d << "GNU_EH_FRAME"; break;
468 case PT_GNU_STACK: d << "GNU_STACK"; break;
469 case PT_GNU_RELRO: d << "GNU_RELRO"; break;
470 case PT_GNU_PROPERTY: d << "GNU_PROPERTY"; break;
471 default: d << "type" << p.phdr->p_type; break;
472 }
473
474 d << "offset" << p.phdr->p_offset
475 << "virtaddr" << p.phdr->p_vaddr
476 << "filesz" << p.phdr->p_filesz
477 << "memsz" << p.phdr->p_memsz
478 << "align" << p.phdr->p_align
479 << "flags";
480
481 d.nospace();
482 if (p.phdr->p_flags & PF_R)
483 d << 'R';
484 if (p.phdr->p_flags & PF_W)
485 d << 'W';
486 if (p.phdr->p_flags & PF_X)
487 d << 'X';
488
489 return d;
490}
491
492struct ErrorMaker
493{
494 QString *errMsg;
495 constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
496
497
498 Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const
499 {
500 *errMsg = QLibrary::tr(s: "'%1' is not a valid ELF object (%2)").arg(args&: *errMsg, args: std::move(text));
501 return {};
502 }
503
504 Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation) const
505 {
506 *errMsg = QLibrary::tr(s: "'%1' is not a Qt plugin (%2)").arg(args&: *errMsg, args&: explanation);
507 return {};
508 }
509
510 Q_DECL_COLD_FUNCTION QLibraryScanResult notfound() const
511 {
512 return notplugin(explanation: QLibrary::tr(s: "metadata not found"));
513 }
514};
515} // unnamed namespace
516
517QT_WARNING_POP
518
519using T = ElfHeaderCheck<>::TypeTraits;
520
521template <typename F>
522static bool scanProgramHeaders(QByteArrayView data, const ErrorMaker &error, F f)
523{
524 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
525 Q_UNUSED(error);
526
527 auto phdr = reinterpret_cast<const T::Phdr *>(data.data() + header->e_phoff);
528 auto phdr_end = phdr + header->e_phnum;
529 for ( ; phdr != phdr_end; ++phdr) {
530 if (!f(phdr))
531 return false;
532 }
533 return true;
534}
535
536static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error)
537{
538 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
539
540 // first, validate the extent of the full program header table
541 T::Word e_phnum = header->e_phnum;
542 T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half
543 if (qAddOverflow(v1: offset, v2: header->e_phoff, r: &offset) || offset > size_t(data.size()))
544 return error(QLibrary::tr(s: "program header table extends past the end of the file")), false;
545
546 // confirm validity
547 bool hasCode = false;
548 auto checker = [&](const T::Phdr *phdr) {
549 qEDebug << ElfProgramDebug{.phdr: phdr};
550
551 if (T::Off end; qAddOverflow(v1: phdr->p_offset, v2: phdr->p_filesz, r: &end)
552 || end > size_t(data.size()))
553 return error(QLibrary::tr(s: "a program header entry extends past the end of the file")), false;
554
555 // this is not a validity check, it's to exclude debug symbol files
556 if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0 && (phdr->p_flags & PF_X))
557 hasCode = true;
558
559 // this probably applies to all segments, but we'll only apply it to notes
560 if (phdr->p_type == PT_NOTE && qPopulationCount(v: phdr->p_align) == 1
561 && phdr->p_offset & (phdr->p_align - 1)) {
562 return error(QLibrary::tr(s: "a note segment start is not properly aligned "
563 "(offset 0x%1, alignment %2)")
564 .arg(a: phdr->p_offset, fieldWidth: 6, base: 16, fillChar: QChar(u'0'))
565 .arg(a: phdr->p_align)), false;
566 }
567
568 return true;
569 };
570 if (!scanProgramHeaders(data, error, f: checker))
571 return false;
572 if (!hasCode)
573 return error.notplugin(explanation: QLibrary::tr(s: "file has no code")), false;
574 return true;
575}
576
577static QLibraryScanResult scanProgramHeadersForNotes(QByteArrayView data, const ErrorMaker &error)
578{
579 // minimum metadata payload is 2 bytes
580 constexpr size_t MinPayloadSize = sizeof(QPluginMetaData::Header) + 2;
581 constexpr qptrdiff MinNoteSize = sizeof(QPluginMetaData::ElfNoteHeader) + 2;
582 constexpr size_t NoteNameSize = sizeof(QPluginMetaData::ElfNoteHeader::name);
583 constexpr size_t NoteAlignment = alignof(QPluginMetaData::ElfNoteHeader);
584 constexpr qptrdiff PayloadStartDelta = offsetof(QPluginMetaData::ElfNoteHeader, header);
585 static_assert(MinNoteSize > PayloadStartDelta);
586 static_assert((PayloadStartDelta & (NoteAlignment - 1)) == 0);
587
588 QLibraryScanResult r = {};
589 auto noteFinder = [&](const T::Phdr *phdr) {
590 if (phdr->p_type != PT_NOTE || phdr->p_align != NoteAlignment)
591 return true;
592
593 // check for signed integer overflows, to avoid issues with the
594 // arithmetic below
595 if (qptrdiff(phdr->p_filesz) < 0) {
596 auto h = reinterpret_cast<const T::Ehdr *>(data.data());
597 auto segments = reinterpret_cast<const T::Phdr *>(data.data() + h->e_phoff);
598 qEDebug << "segment" << (phdr - segments) << "contains a note with size"
599 << Qt::hex << Qt::showbase << phdr->p_filesz
600 << "which is larger than half the virtual memory space";
601 return true;
602 }
603
604 // iterate over the notes in this segment
605 T::Off offset = phdr->p_offset;
606 const T::Off end_offset = offset + phdr->p_filesz;
607 while (qptrdiff(end_offset - offset) >= MinNoteSize) {
608 auto nhdr = reinterpret_cast<const T::Nhdr *>(data.data() + offset);
609 T::Word n_namesz = nhdr->n_namesz;
610 T::Word n_descsz = nhdr->n_descsz;
611 T::Word n_type = nhdr->n_type;
612
613 // overflow check: calculate where the next note will be, if it exists
614 T::Off next_offset = offset;
615 next_offset += sizeof(T::Nhdr); // can't overflow (we checked above)
616 next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
617 if (qAddOverflow<T::Off>(v1: next_offset, v2: n_namesz, r: &next_offset))
618 break;
619 next_offset &= -NoteAlignment;
620
621 next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
622 if (qAddOverflow<T::Off>(v1: next_offset, v2: n_descsz, r: &next_offset))
623 break;
624 next_offset &= -NoteAlignment;
625 if (next_offset > end_offset)
626 break;
627
628 if (n_namesz == NoteNameSize && n_descsz >= MinPayloadSize
629 && n_type == QPluginMetaData::ElfNoteHeader::NoteType
630 && memcmp(s1: nhdr + 1, s2: QPluginMetaData::ElfNoteHeader::NoteName, n: NoteNameSize) == 0) {
631 // yes, it's our note
632 r.pos = offset + PayloadStartDelta;
633 r.length = nhdr->n_descsz;
634 return false;
635 }
636 offset = next_offset;
637 }
638 return true;
639 };
640 scanProgramHeaders(data, error, f: noteFinder);
641
642 if (!r.length)
643 return r;
644
645 qEDebug << "found Qt metadata in ELF note at"
646 << Qt::hex << Qt::showbase << r.pos << "size" << Qt::reset << r.length;
647 return r;
648}
649
650static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
651{
652 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
653
654 // in order to find the .qtmetadata section, we need to:
655 // a) find the section table
656 // it's located at offset header->e_shoff
657 // validate it
658 T::Word e_shnum = header->e_shnum;
659 T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half
660 if (qAddOverflow(v1: offset, v2: header->e_shoff, r: &offset) || offset > size_t(data.size()))
661 return error(QLibrary::tr(s: "section table extends past the end of the file"));
662
663 // b) find the section entry for the section header string table (shstrab)
664 // it's a section whose entry is pointed by e_shstrndx
665 auto sections = reinterpret_cast<const T::Shdr *>(data.data() + header->e_shoff);
666 auto sections_end = sections + e_shnum;
667 auto shdr = sections + header->e_shstrndx;
668
669 // validate the shstrtab
670 offset = shdr->sh_offset;
671 T::Off shstrtab_size = shdr->sh_size;
672 qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size;
673 if (T::Off end; qAddOverflow<T::Off>(v1: offset, v2: shstrtab_size, r: &end)
674 || end > size_t(data.size()))
675 return error(QLibrary::tr(s: "section header string table extends past the end of the file"));
676
677 // c) iterate over the sections to find .qtmetadata
678 const char *shstrtab_start = data.data() + offset;
679 shdr = sections;
680 for (int section = 0; shdr != sections_end; ++section, ++shdr) {
681 QLatin1StringView name;
682 if (shdr->sh_name < shstrtab_size) {
683 const char *namestart = shstrtab_start + shdr->sh_name;
684 size_t len = qstrnlen(str: namestart, maxlen: shstrtab_size - shdr->sh_name);
685 name = QLatin1StringView(namestart, len);
686 }
687 qEDebug << "section" << section << "name" << name << ElfSectionDebug{.shdr: shdr};
688
689 // sanity check the section
690 if (name.isNull())
691 return error(QLibrary::tr(s: "a section name extends past the end of the file"));
692
693 // sections aren't allowed to extend past the end of the file, unless
694 // they are NOBITS sections
695 if (shdr->sh_type == SHT_NOBITS)
696 continue;;
697 if (T::Off end; qAddOverflow(v1: shdr->sh_offset, v2: shdr->sh_size, r: &end)
698 || end > size_t(data.size())) {
699 return error(QLibrary::tr(s: "section contents extend past the end of the file"));
700 }
701
702 if (name != ".qtmetadata"_L1)
703 continue;
704 qEDebug << "found .qtmetadata section";
705 if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
706 return error(QLibrary::tr(s: ".qtmetadata section is too small"));
707
708 if (IncludeValidityChecks) {
709 QByteArrayView expectedMagic = QByteArrayView::fromArray(data: QPluginMetaData::MagicString);
710 QByteArrayView actualMagic = data.sliced(pos: shdr->sh_offset, n: expectedMagic.size());
711 if (expectedMagic != actualMagic)
712 return error(QLibrary::tr(s: ".qtmetadata section has incorrect magic"));
713
714 if (shdr->sh_flags & SHF_WRITE)
715 return error(QLibrary::tr(s: ".qtmetadata section is writable"));
716 if (shdr->sh_flags & SHF_EXECINSTR)
717 return error(QLibrary::tr(s: ".qtmetadata section is executable"));
718 }
719
720 return { .pos: qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
721 .length: qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
722 }
723
724 // section .qtmetadata not found
725 return error.notfound();
726}
727
728QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg)
729{
730 ErrorMaker error(errMsg);
731 if (size_t(data.size()) < sizeof(T::Ehdr)) {
732 qEDebug << "file too small:" << size_t(data.size());
733 return error(QLibrary::tr(s: "file too small"));
734 }
735
736 qEDebug << ElfHeaderDebug{ .e_ident: reinterpret_cast<const uchar *>(data.data()) };
737
738 auto header = reinterpret_cast<const T::Ehdr *>(data.data());
739 if (!ElfHeaderCheck<>::checkHeader(header: *header))
740 return error(ElfHeaderCheck<>::explainCheckFailure(header: *header));
741
742 qEDebug << "contains" << header->e_phnum << "program headers of"
743 << header->e_phentsize << "bytes at offset" << header->e_phoff;
744 qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize
745 << "bytes at offset" << header->e_shoff
746 << "; section header string table (shstrtab) is entry" << header->e_shstrndx;
747
748 // some sanity checks
749 if constexpr (IncludeValidityChecks) {
750 if (header->e_phentsize != sizeof(T::Phdr))
751 return error(QLibrary::tr(s: "unexpected program header entry size (%1)")
752 .arg(a: header->e_phentsize));
753 }
754
755 if (!preScanProgramHeaders(data, error))
756 return {};
757
758 if (QLibraryScanResult r = scanProgramHeadersForNotes(data, error); r.length)
759 return r;
760
761 if (!ElfNotesAreMandatory) {
762 if constexpr (IncludeValidityChecks) {
763 if (header->e_shentsize != sizeof(T::Shdr))
764 return error(QLibrary::tr(s: "unexpected section entry size (%1)")
765 .arg(a: header->e_shentsize));
766 }
767 if (header->e_shoff == 0 || header->e_shnum == 0) {
768 // this is still a valid ELF file but we don't have a section table
769 qEDebug << "no section table present, not able to find Qt metadata";
770 return error.notfound();
771 }
772
773 if (header->e_shnum && header->e_shstrndx >= header->e_shnum)
774 return error(QLibrary::tr(s: "e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
775 .arg(a: header->e_shstrndx).arg(a: header->e_shnum));
776 return scanSections(data, error);
777 }
778 return error.notfound();
779}
780
781QT_END_NAMESPACE
782
783#endif // Q_OF_ELF
784

source code of qtbase/src/corelib/plugin/qelfparser_p.cpp