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