QElfParser: rewrite using elf.h

This rewrite uses the actual structures supplied by the system's C
library, so it should be easier to read. It removes hardcoded constants
with little evident meaning in favor of sizeof() and the macros from
that header. It also removes advancing the data pointer in favor of
having absolute offsets.

The resulting implementation is stricter than the original, checking
more fields in the header. Because the QPluginLoader and QFactoryLoader
users may make decisions based on availability of plugins before
attempting to load them, it's better to be stricter here than to fail
later when trying to dlopen() them.

Debugging and testing are much improved. Instead of stored artifacts, I
added a routine to modify a valid plugin to make it invalid, given the
conditions we've found so far.

If you turn debugging on for this category, you'll see things like:

 not-elf.fcqdMq.so : Not an ELF file (invalid signature)
 wrong-word-size.QrnSAx.so : ELF 32-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 invalid-word-size.bOkXvp.so : Invalid ELF file (class 0), LSB (GNU/Linux)
 unknown-word-size.ogYKeF.so : Invalid ELF file (class 66), LSB (GNU/Linux)
 wrong-endian.owiElX.so : ELF 64-bit MSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 invalid-endian.FRxClR.so : ELF 64-bit invalid endianness (0) (GNU/Linux)
 unknown-endian.FfvRrP.so : ELF 64-bit invalid endianness (65) (GNU/Linux)
 elf-version-0.gPTdpQ.so : ELF 64-bit LSB (GNU/Linux), file version 0
 elf-version-2.jlIUUg.so : ELF 64-bit LSB (GNU/Linux), file version 2
 executable.LlXiFp.so : ELF 64-bit LSB (GNU/Linux), version 1, executable, x86-64
 relocatable.UsOYuy.so : ELF 64-bit LSB (GNU/Linux), version 1, relocatable, x86-64
 core-file.hqvNRz.so : ELF 64-bit LSB (GNU/Linux), version 1, core dump, x86-64
 invalid-type.CIJgfS.so : ELF 64-bit LSB (GNU/Linux), version 1, unknown type 259, x86-64
 wrong-arch.UcNmgz.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, AArch64
 file-version-0.lZYuda.so : ELF 64-bit LSB (GNU/Linux), version 0, shared library or PIC executable, x86-64
 file-version-2.ucfdwL.so : ELF 64-bit LSB (GNU/Linux), version 2, shared library or PIC executable, x86-64
 no-sections.rSjsHh.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 no-sections.rSjsHh.so : contains 0 sections of 64 bytes at offset 0 ; section header string table (shstrtab) is entry 0
 no-sections.rSjsHh.so : no section table present, not able to find Qt metadata
 qtmetadata-executable.vrxcIf.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64
 qtmetadata-executable.vrxcIf.so : contains 42 sections of 64 bytes at offset 997256 ; section header string table (shstrtab) is entry 41
 qtmetadata-executable.vrxcIf.so : shstrtab section is located at offset 996831 size 423
 qtmetadata-executable.vrxcIf.so : section 0 name "" type NULL flags X offset 0x0 size 0x0
 qtmetadata-executable.vrxcIf.so : section 1 name ".note.gnu.property" type NOTE flags AX offset 0x2a8 size 0x30
 qtmetadata-executable.vrxcIf.so : section 2 name ".note.gnu.build-id" type NOTE flags AX offset 0x2d8 size 0x24
 qtmetadata-executable.vrxcIf.so : section 3 name ".hash" type HASH flags AX offset 0x300 size 0x44c
 qtmetadata-executable.vrxcIf.so : section 4 name ".gnu.hash" type 0x6ffffff6 flags AX offset 0x750 size 0x3b8
 qtmetadata-executable.vrxcIf.so : section 5 name ".dynsym" type DYNSYM flags AX offset 0xb08 size 0xd50
 qtmetadata-executable.vrxcIf.so : section 6 name ".dynstr" type STRTAB flags AX offset 0x1858 size 0x15d8
 qtmetadata-executable.vrxcIf.so : section 7 name ".gnu.version" type 0x6fffffff flags AX offset 0x2e30 size 0x11c
 qtmetadata-executable.vrxcIf.so : section 8 name ".gnu.version_r" type 0x6ffffffe flags AX offset 0x2f50 size 0xb0
 qtmetadata-executable.vrxcIf.so : section 9 name ".rela.dyn" type RELA flags AX offset 0x3000 size 0x480
 qtmetadata-executable.vrxcIf.so : section 10 name ".rela.plt" type RELA flags AX offset 0x3480 size 0x7e0
 qtmetadata-executable.vrxcIf.so : section 11 name ".init" type PROGBITS flags AX offset 0x4000 size 0x1b
 qtmetadata-executable.vrxcIf.so : section 12 name ".plt" type PROGBITS flags AX offset 0x4020 size 0x550
 qtmetadata-executable.vrxcIf.so : section 13 name ".plt.got" type PROGBITS flags AX offset 0x4570 size 0x8
 qtmetadata-executable.vrxcIf.so : section 14 name ".text" type PROGBITS flags AX offset 0x4580 size 0x110e
 qtmetadata-executable.vrxcIf.so : section 15 name ".fini" type PROGBITS flags AX offset 0x5690 size 0xd
 qtmetadata-executable.vrxcIf.so : section 16 name ".rodata" type PROGBITS flags AX offset 0x6000 size 0x473
 qtmetadata-executable.vrxcIf.so : section 17 name ".qtversion" type PROGBITS flags AX offset 0x6478 size 0x10
 qtmetadata-executable.vrxcIf.so : section 18 name ".qtmetadata" type PROGBITS flags AX offset 0x64a0 size 0x19b
 qtmetadata-executable.vrxcIf.so : found .qtmetadata section
 qtmetadata-writable.stzwrk.so : ELF 64-bit LSB (GNU/Linux), version 1, shared library or PIC executable, x86-64

Change-Id: I42eb903a916645db9900fffd16a4437af9728eea
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Thiago Macieira 2021-09-10 20:30:08 -07:00
parent 3abcff49eb
commit 46fc01d7ca
12 changed files with 827 additions and 233 deletions

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2017 The Qt Company Ltd.
** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -39,189 +40,542 @@
#include "qelfparser_p.h"
#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
#if defined (Q_OF_ELF) && __has_include(<elf.h>)
#include "qlibrary_p.h"
#include <qdebug.h>
#include <qloggingcategory.h>
#include <qnumeric.h>
#include <qsysinfo.h>
#include <elf.h>
QT_BEGIN_NAMESPACE
// #define QELFPARSER_DEBUG 1
// Whether we include some extra validity checks
// (checks to ensure we don't read out-of-bounds are always included)
static constexpr bool IncludeValidityChecks = true;
const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh)
#ifdef QT_BUILD_INTERNAL
# define QELFPARSER_DEBUG
#endif
#if defined(QELFPARSER_DEBUG)
static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser")
# define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
#else
# define qEDebug if (false) {} else QNoDebug()
#endif
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
namespace {
template <QSysInfo::Endian Order> struct ElfEndianTraits
{
sh->name = qFromUnaligned<qelfword_t>(data);
data += sizeof(qelfword_t); // sh_name
sh->type = qFromUnaligned<qelfword_t>(data);
data += sizeof(qelfword_t) // sh_type
+ sizeof(qelfaddr_t) // sh_flags
+ sizeof(qelfaddr_t); // sh_addr
sh->offset = qFromUnaligned<qelfoff_t>(data);
data += sizeof(qelfoff_t); // sh_offset
sh->size = qFromUnaligned<qelfoff_t>(data);
data += sizeof(qelfoff_t); // sh_size
return data;
static constexpr unsigned char DataOrder = ELFDATA2LSB;
template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
};
template <> struct ElfEndianTraits<QSysInfo::BigEndian>
{
static constexpr unsigned char DataOrder = ELFDATA2MSB;
template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
};
template <typename EquivalentPointerType> struct ElfTypeTraits
{
static constexpr unsigned char Class = ELFCLASS64;
// integer types
using Half = Elf64_Half;
using Word = Elf64_Word;
using Addr = Elf64_Addr;
using Off = Elf64_Off;
// structure types
using Ehdr = Elf64_Ehdr;
using Shdr = Elf64_Shdr;
using Phdr = Elf64_Phdr;
using Nhdr = Elf64_Nhdr;
};
template <> struct ElfTypeTraits<quint32>
{
static constexpr unsigned char Class = ELFCLASS32;
// integer types
using Half = Elf32_Half;
using Word = Elf32_Word;
using Addr = Elf32_Addr;
using Off = Elf32_Off;
// structure types
using Ehdr = Elf32_Ehdr;
using Shdr = Elf32_Shdr;
using Phdr = Elf32_Phdr;
using Nhdr = Elf32_Nhdr;
};
struct ElfMachineCheck
{
static const Elf32_Half ExpectedMachine =
#if 0
// nothing
#elif defined(Q_PROCESSOR_ARM_32)
EM_ARM
#elif defined(Q_PROCESSOR_ARM_64)
EM_AARCH64
#elif defined(Q_PROCESSOR_BLACKFIN)
EM_BLACKFIN
#elif defined(Q_PROCESSOR_IA64)
EM_IA_64
#elif defined(Q_PROCESSOR_MIPS)
EM_MIPS
#elif defined(Q_PROCESSOR_POWER_32)
EM_PPC
#elif defined(Q_PROCESSOR_POWER_64)
EM_PPC64
#elif defined(Q_PROCESSOR_RISCV)
EM_RISCV
#elif defined(Q_PROCESSOR_S390)
EM_S390
#elif defined(Q_PROCESSOR_SH)
EM_SH
#elif defined(Q_PROCESSOR_SPARC_V9)
# warning "Please confirm that this is correct for Linux and Solaris"
EM_SPARCV9
#elif defined(Q_PROCESSOR_SPARC_64)
# warning "Please confirm that this is correct for Linux and Solaris"
EM_SPARCV9
#elif defined(Q_PROCESSOR_SPARC)
EM_SPARC
#elif defined(Q_PROCESSOR_WASM)
#elif defined(Q_PROCESSOR_X86_32)
EM_386
#elif defined(Q_PROCESSOR_X86_64)
EM_X86_64
#else
# error "Unknown Q_PROCESSOR_xxx macro, please update."
EM_NONE
#endif
;
};
struct ElfHeaderCommonCheck
{
static_assert(std::is_same_v<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
"e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
// bytes 0-3
static bool checkElfMagic(const uchar *ident)
{
return memcmp(ident, ELFMAG, SELFMAG) == 0;
}
// byte 6
static bool checkElfVersion(const uchar *ident)
{
uchar elfversion = ident[EI_VERSION];
return elfversion == EV_CURRENT;
}
struct CommonHeader {
Elf32_Half type;
Elf32_Half machine;
Elf32_Word version;
};
};
template <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
struct ElfHeaderCheck : public ElfHeaderCommonCheck
{
using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
using EndianTraits = ElfEndianTraits<Order>;
using Ehdr = typename TypeTraits::Ehdr;
// byte 4
static bool checkClass(const uchar *ident)
{
uchar klass = ident[EI_CLASS];
return klass == TypeTraits::Class;
}
// byte 5
static bool checkDataOrder(const uchar *ident)
{
uchar data = ident[EI_DATA];
return data == EndianTraits::DataOrder;
}
// byte 7
static bool checkOsAbi(const uchar *ident)
{
uchar osabi = ident[EI_OSABI];
// we don't check
Q_UNUSED(osabi);
return true;
}
// byte 8
static bool checkAbiVersion(const uchar *ident)
{
uchar abiversion = ident[EI_ABIVERSION];
// we don't check (and I don't know anyone who uses this)
Q_UNUSED(abiversion);
return true;
}
// bytes 9-16
static bool checkPadding(const uchar *ident)
{
// why would we check this?
Q_UNUSED(ident);
return true;
}
static bool checkIdent(const Ehdr &header)
{
return checkElfMagic(header.e_ident)
&& checkClass(header.e_ident)
&& checkDataOrder(header.e_ident)
&& checkElfVersion(header.e_ident)
&& checkOsAbi(header.e_ident)
&& checkAbiVersion(header.e_ident)
&& checkPadding(header.e_ident);
}
static bool checkType(const Ehdr &header)
{
return header.e_type == ET_DYN;
}
static bool checkMachine(const Ehdr &header)
{
return header.e_machine == ElfMachineCheck::ExpectedMachine;
}
static bool checkFileVersion(const Ehdr &header)
{
return header.e_version == EV_CURRENT;
}
static bool checkHeader(const Ehdr &header)
{
if (!checkIdent(header))
return false;
if (!IncludeValidityChecks)
return true;
return checkType(header)
&& checkMachine(header)
&& checkFileVersion(header);
}
Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header)
{
if (!checkElfMagic(header.e_ident))
return QLibrary::tr("invalid signature");
if (!checkClass(header.e_ident))
return QLibrary::tr("file is for a different word size");
if (!checkDataOrder(header.e_ident))
return QLibrary::tr("file is for the wrong endianness");
if (!checkElfVersion(header.e_ident) || !checkFileVersion(header))
return QLibrary::tr("file has an unknown ELF version");
if (!checkOsAbi(header.e_ident) || !checkAbiVersion(header.e_ident))
return QLibrary::tr("file has an unexpected ABI");
if (!checkType(header))
return QLibrary::tr("file is not a shared object");
if (!checkMachine(header))
return QLibrary::tr("file is for a different processor");
return QString();
}
static CommonHeader extractCommonHeader(const uchar *data)
{
auto header = reinterpret_cast<const Ehdr *>(data);
CommonHeader r;
r.type = EndianTraits::fromEndian(header->e_type);
r.machine = EndianTraits::fromEndian(header->e_machine);
r.version = EndianTraits::fromEndian(header->e_version);
return r;
}
};
struct ElfHeaderDebug { const uchar *e_ident; };
Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeaderDebug h)
{
const uchar *e_ident = h.e_ident;
if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) {
d << "Not an ELF file (invalid signature)";
return d;
}
QDebugStateSaver saver(d);
d.nospace();
quint8 elfclass = e_ident[EI_CLASS];
switch (elfclass) {
case ELFCLASSNONE:
default:
d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), ";
break;
case ELFCLASS32:
d << "ELF 32-bit ";
break;
case ELFCLASS64:
d << "ELF 64-bit ";
break;
}
quint8 dataorder = e_ident[EI_DATA];
switch (dataorder) {
case ELFDATANONE:
default:
d << "invalid endianness (" << e_ident[EI_DATA] << ')';
break;
case ELFDATA2LSB:
d << "LSB";
break;
case ELFDATA2MSB:
d << "MSB";
break;
}
switch (e_ident[EI_OSABI]) {
case ELFOSABI_SYSV: d << " (SYSV"; break;
case ELFOSABI_HPUX: d << " (HP-UX"; break;
case ELFOSABI_NETBSD: d << " (NetBSD"; break;
case ELFOSABI_GNU: d << " (GNU/Linux"; break;
case ELFOSABI_SOLARIS: d << " (Solaris"; break;
case ELFOSABI_AIX: d << " (AIX"; break;
case ELFOSABI_IRIX: d << " (IRIX"; break;
case ELFOSABI_FREEBSD: d << " (FreeBSD"; break;
case ELFOSABI_OPENBSD: d << " (OpenBSD"; break;
default: d << " (OS ABI " << e_ident[EI_VERSION]; break;
}
if (e_ident[EI_ABIVERSION])
d << " v" << e_ident[EI_ABIVERSION];
d << ')';
if (e_ident[EI_VERSION] != 1) {
d << ", file version " << e_ident[EI_VERSION];
return d;
}
ElfHeaderCommonCheck::CommonHeader r;
if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
else
return d;
d << ", version " << r.version;
switch (r.type) {
case ET_NONE: d << ", no type"; break;
case ET_REL: d << ", relocatable"; break;
case ET_EXEC: d << ", executable"; break;
case ET_DYN: d << ", shared library or PIC executable"; break;
case ET_CORE: d << ", core dump"; break;
default: d << ", unknown type " << r.type; break;
}
switch (r.machine) {
// list definitely not exhaustive!
case EM_NONE: d << ", no machine"; break;
case EM_ARM: d << ", ARM"; break;
case EM_AARCH64: d << ", AArch64"; break;
case EM_BLACKFIN: d << ", Blackfin"; break;
case EM_IA_64: d << ", IA-64"; break;
case EM_MIPS: d << ", MIPS"; break;
case EM_PPC: d << ", PowerPC"; break;
case EM_PPC64: d << ", PowerPC 64-bit"; break;
case EM_RISCV: d << ", RISC-V"; break;
case EM_S390: d << ", S/390"; break;
case EM_SH: d << ", SuperH"; break;
case EM_SPARC: d << ", SPARC"; break;
case EM_SPARCV9: d << ", SPARCv9"; break;
case EM_386: d << ", i386"; break;
case EM_X86_64: d << ", x86-64"; break;
default: d << ", other machine type " << r.machine; break;
}
return d;
}
QLibraryScanResult QElfParser::parse(const char *dataStart, ulong fdlen, QString *errMsg)
struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
{
#if defined(QELFPARSER_DEBUG)
qDebug() << "QElfParser::parse " << library;
#endif
// not exhaustive, just a few common things
QDebugStateSaver saver(d);
d << Qt::hex << Qt::showbase;
d << "type";
switch (s.shdr->sh_type) {
case SHT_NULL: d << "NULL"; break;
case SHT_PROGBITS: d << "PROGBITS"; break;
case SHT_SYMTAB: d << "SYMTAB"; break;
case SHT_STRTAB: d << "STRTAB"; break;
case SHT_RELA: d << "RELA"; break;
case SHT_HASH: d << "HASH"; break;
case SHT_DYNAMIC: d << "DYNAMIC"; break;
case SHT_NOTE: d << "NOTE"; break;
case SHT_NOBITS: d << "NOBITS"; break;
case SHT_DYNSYM: d << "DYNSYM"; break;
case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break;
case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break;
default: d << s.shdr->sh_type;
}
if (fdlen < 64) {
*errMsg = QLibrary::tr("'%1' is not an ELF object (%2)").arg(*errMsg, QLibrary::tr("file too small"));
return {};
}
const char *data = dataStart;
if (qstrncmp(data, "\177ELF", 4) != 0) {
*errMsg = QLibrary::tr("'%1' is not an ELF object").arg(*errMsg);
return {};
}
// 32 or 64 bit
if (data[4] != 1 && data[4] != 2) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd cpu architecture"));
d << "flags";
d.nospace();
if (s.shdr->sh_flags & SHF_WRITE)
d << 'W';
if (s.shdr->sh_flags & SHF_ALLOC)
d << 'A';
if (s.shdr->sh_flags & SHF_EXECINSTR)
d << 'X';
if (s.shdr->sh_flags & SHF_STRINGS)
d << 'S';
if (s.shdr->sh_flags & SHF_TLS)
d << 'T';
d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size;
return d;
}
struct ErrorMaker
{
QString *errMsg;
constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const
{
*errMsg = QLibrary::tr("'%1' is not a valid ELF object (%2)")
.arg(*errMsg, std::move(text));
return {};
}
/* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs
to match the _plugin_ architecture.
*/
constexpr int ExpectedClass = (sizeof(void *) == 4) ? 1 : 2;
if (data[4] != ExpectedClass) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("wrong cpu architecture"));
QLibraryScanResult notfound() const
{
*errMsg = QLibrary::tr("'%1' is not a Qt plugin (.qtmetadata section not found)")
.arg(*errMsg);
return {};
}
};
} // unnamed namespace
// endian
constexpr int ExpectedEndianness = (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) ? 1 : 2;
if (data[5] != ExpectedEndianness) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("odd endianness"));
return {};
}
QT_WARNING_POP
data += 16 // e_ident
+ sizeof(qelfhalf_t) // e_type
+ sizeof(qelfhalf_t) // e_machine
+ sizeof(qelfword_t) // e_version
+ sizeof(qelfaddr_t) // e_entry
+ sizeof(qelfoff_t); // e_phoff
using T = ElfHeaderCheck<>::TypeTraits;
qelfoff_t e_shoff = qFromUnaligned<qelfoff_t> (data);
data += sizeof(qelfoff_t) // e_shoff
+ sizeof(qelfword_t); // e_flags
static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
{
auto header = reinterpret_cast<const T::Ehdr *>(data.data());
qelfhalf_t e_shsize = qFromUnaligned<qelfhalf_t> (data);
// in order to find the .qtmetadata section, we need to:
// a) find the section table
// it's located at offset header->e_shoff
// validate it
T::Word e_shnum = header->e_shnum;
T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half
if (qAddOverflow(offset, header->e_shoff, &offset) || offset > size_t(data.size()))
return error(QLibrary::tr("section table extends past the end of the file"));
if (e_shsize > fdlen) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shsize"));
return {};
}
// b) find the section entry for the section header string table (shstrab)
// it's a section whose entry is pointed by e_shstrndx
auto sections = reinterpret_cast<const T::Shdr *>(data.data() + header->e_shoff);
auto sections_end = sections + e_shnum;
auto shdr = sections + header->e_shstrndx;
data += sizeof(qelfhalf_t) // e_ehsize
+ sizeof(qelfhalf_t) // e_phentsize
+ sizeof(qelfhalf_t); // e_phnum
// validate the shstrtab
offset = shdr->sh_offset;
T::Off shstrtab_size = shdr->sh_size;
qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size;
if (T::Off end; qAddOverflow<T::Off>(offset, shstrtab_size, &end)
|| end > size_t(data.size()))
return error(QLibrary::tr("section header string table extends past the end of the file"));
qelfhalf_t e_shentsize = qFromUnaligned<qelfhalf_t> (data);
// c) iterate over the sections to find .qtmetadata
const char *shstrtab_start = data.data() + offset;
shdr = sections;
for (int section = 0; shdr != sections_end; ++section, ++shdr) {
QLatin1String name;
if (shdr->sh_name < shstrtab_size) {
const char *namestart = shstrtab_start + shdr->sh_name;
size_t len = qstrnlen(namestart, shstrtab_size - shdr->sh_name);
name = QLatin1String(namestart, len);
}
qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr};
if (e_shentsize % 4) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, QLibrary::tr("unexpected e_shentsize"));
return {};
}
data += sizeof(qelfhalf_t); // e_shentsize
qelfhalf_t e_shnum = qFromUnaligned<qelfhalf_t> (data);
data += sizeof(qelfhalf_t); // e_shnum
qelfhalf_t e_shtrndx = qFromUnaligned<qelfhalf_t> (data);
data += sizeof(qelfhalf_t); // e_shtrndx
// sanity check the section
if (name.isNull())
return error(QLibrary::tr("a section name extends past the end of the file"));
if ((quint32)(e_shnum * e_shentsize) > fdlen) {
const QString message =
QLibrary::tr("announced %n section(s), each %1 byte(s), exceed file size",
nullptr, int(e_shnum)).arg(e_shentsize);
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(*errMsg, message);
return {};
}
// sections aren't allowed to extend past the end of the file, unless
// they are NOBITS sections
if (shdr->sh_type == SHT_NOBITS)
continue;;
if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end)
|| end > size_t(data.size())) {
return error(QLibrary::tr("a section data extends past the end of the file"));
}
#if defined(QELFPARSER_DEBUG)
qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes";
#endif
ElfSectionHeader strtab;
qulonglong soff = e_shoff + qelfword_t(e_shentsize) * qelfword_t(e_shtrndx);
if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("shstrtab section header seems to be at %1")
.arg(QString::number(soff, 16)));
return {};
}
parseSectionHeader(dataStart + soff, &strtab);
m_stringTableFileOffset = strtab.offset;
if ((quint32)(strtab.offset + strtab.size) > fdlen || strtab.offset == 0) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("string table seems to be at %1")
.arg(QString::number(strtab.offset, 16)));
return {};
}
#if defined(QELFPARSER_DEBUG)
qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data());
#endif
const char *s = dataStart + e_shoff;
for (int i = 0; i < e_shnum; ++i) {
ElfSectionHeader sh;
parseSectionHeader(s, &sh);
if (sh.name == 0) {
s += e_shentsize;
if (name != QLatin1String(".qtmetadata"))
continue;
qEDebug << "found .qtmetadata section";
if (IncludeValidityChecks && shdr->sh_flags & (SHF_WRITE | SHF_EXECINSTR)) {
if (shdr->sh_flags & SHF_WRITE)
return error(QLibrary::tr(".qtmetadata section is writable"));
return error(QLibrary::tr(".qtmetadata section is executable"));
}
const char *shnam = dataStart + m_stringTableFileOffset + sh.name;
if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
return error(QLibrary::tr("section .qtmetadata is too small"));
if (m_stringTableFileOffset + sh.name > fdlen) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("section name %1 of %2 behind end of file")
.arg(i).arg(e_shnum));
return {};
}
#if defined(QELFPARSER_DEBUG)
qDebug() << "++++" << i << shnam;
#endif
if (qstrcmp(shnam, ".qtmetadata") == 0 ) {
if (!(sh.type & 0x1)) {
if (shnam[1] == 'r') {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("empty .rodata. not a library."));
return {};
}
#if defined(QELFPARSER_DEBUG)
qDebug()<<"section is not program data. skipped.";
#endif
s += e_shentsize;
continue;
}
if (sh.offset == 0 || (sh.offset + sh.size) > fdlen || sh.size < 1) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("missing section data. This is not a library."));
return {};
}
if (sh.size < sizeof(QPluginMetaData::MagicHeader)) {
*errMsg = QLibrary::tr("'%1' is an invalid ELF object (%2)")
.arg(*errMsg, QLibrary::tr("section .qtmetadata is too small"));
return {};
}
sh.offset += sizeof(QPluginMetaData::MagicString);
sh.size -= sizeof(QPluginMetaData::MagicString);
return { qsizetype(sh.offset), qsizetype(sh.size) };
}
s += e_shentsize;
return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
}
return {};
// section .qtmetadata not found
return error.notfound();
}
QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg)
{
ErrorMaker error(errMsg);
if (size_t(data.size()) < sizeof(T::Ehdr)) {
qEDebug << "file too small:" << size_t(data.size());
return error(QLibrary::tr("file too small"));
}
qEDebug << ElfHeaderDebug{ reinterpret_cast<const uchar *>(data.data()) };
auto header = reinterpret_cast<const T::Ehdr *>(data.data());
if (!ElfHeaderCheck<>::checkHeader(*header))
return error(ElfHeaderCheck<>::explainCheckFailure(*header));
qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize
<< "bytes at offset" << header->e_shoff
<< "; section header string table (shstrtab) is entry" << header->e_shstrndx;
// some sanity checks
if constexpr (IncludeValidityChecks) {
if (header->e_shentsize != sizeof(T::Shdr))
return error(QLibrary::tr("unexpected section entry size (%1)")
.arg(header->e_shentsize));
}
if (header->e_shoff == 0 || header->e_shnum == 0) {
// this is still a valid ELF file but we don't have a section table
qEDebug << "no section table present, not able to find Qt metadata";
return error.notfound();
}
if (header->e_shnum && header->e_shstrndx >= header->e_shnum)
return error(QLibrary::tr("e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
.arg(header->e_shstrndx).arg(header->e_shnum));
return scanSections(data, error);
}
QT_END_NAMESPACE

View File

@ -56,35 +56,13 @@
QT_REQUIRE_CONFIG(library);
#if defined(Q_OF_ELF) && defined(Q_CC_GNU)
#if defined(Q_OF_ELF) && __has_include(<elf.h>)
QT_BEGIN_NAMESPACE
class QString;
class QLibraryPrivate;
typedef quint16 qelfhalf_t;
typedef quint32 qelfword_t;
typedef quintptr qelfoff_t;
typedef quintptr qelfaddr_t;
class QElfParser
struct QElfParser
{
public:
enum { ElfLittleEndian = 0, ElfBigEndian = 1 };
struct ElfSectionHeader
{
qelfword_t name;
qelfword_t type;
qelfoff_t offset;
qelfoff_t size;
};
qelfoff_t m_stringTableFileOffset;
const char *parseSectionHeader(const char* s, ElfSectionHeader *sh);
QLibraryScanResult parse(const char *m_s, ulong fdlen, QString *errMsg);
static QLibraryScanResult parse(QByteArrayView data, QString *errMsg);
};
QT_END_NAMESPACE

View File

@ -193,8 +193,8 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin
More importantly, the pattern string may exist in the debug information due
to it being used in the plugin in the first place.
*/
#if defined (Q_OF_ELF)
return QElfParser().parse(s, s_len, errMsg);
#if defined (Q_OF_ELF) && __has_include(<elf.h>)
return QElfParser::parse({s, s_len}, errMsg);
#elif defined(Q_OF_MACH_O)
return QMachOParser::parse(s, s_len, errMsg);
#endif

View File

@ -1,4 +0,0 @@
p¶¤Ðã¨ø±ÕÛcdL+ôúæî&‘¿&÷ýe<C3BD>ü¥=კ²
•o°ÃÊŽI §Ù<C2A7>Ïmgƒ]!ÀZ
L'Ž)t±
ÙN»¸(e©× P)Y8öG ˆ6ß-yÈÏÀ ñŸ§÷—“"ôZÖÿ?Ë^<5E>náÁÇß5$žôY=£ð#y

View File

@ -1 +0,0 @@
£Çv.³Y<59>¨tKëW3

View File

@ -1 +0,0 @@
£ÝÈÈ‚åžT-õ«´ÊôÚ¥ Àä¸ï¨ì¾œÀi<C380>8¼_ñxÓõª¾I±Ð×®ÝxÎ=úØ4@þñ[¨<>—úBàKS$ú

View File

@ -1 +0,0 @@
<EFBFBD> !<21>\~هU­u<C2AD><75>:9<><39> <><1B>T<EFBFBD>+91<39>E<>ما<D985>x<EFBFBD>ng5سك<D8B3>zh<13><><EFBFBD><EFBFBD>ئ^t<><1D><><EFBFBD><EFBFBD>'ئخmm*ث<>dXH;vw+<2B>G<EFBFBD><47>أـظ <0A>9L<>0!

View File

@ -1,2 +0,0 @@
яЬє<EFBFBD>QВ
уЕ-Ђ9в

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
@ -31,7 +31,10 @@
#include <QSignalSpy>
#include <QJsonArray>
#include <qdir.h>
#include <qendian.h>
#include <qpluginloader.h>
#include <qprocess.h>
#include <qtemporaryfile.h>
#include "theplugin/plugininterface.h"
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
@ -91,6 +94,84 @@
# define PREFIX "lib"
#endif
#if defined(Q_OF_ELF)
# include <elf.h>
# include <memory>
# include <functional>
# ifdef _LP64
using ElfHeader = Elf64_Ehdr;
using ElfShdr = Elf64_Shdr;
# else
using ElfHeader = Elf32_Ehdr;
using ElfShdr = Elf32_Shdr;
# endif
struct ElfPatcher
{
using FullPatcher = void(ElfHeader *, QFile *);
FullPatcher *f;
ElfPatcher(FullPatcher *f = nullptr) : f(f) {}
template <typename T> using IsSingleArg = std::is_invocable<T, ElfHeader *>;
template <typename T> static std::enable_if_t<IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
{
using WithoutQFile = void(*)(ElfHeader *);
static const WithoutQFile f = t;
return { [](ElfHeader *h, QFile *) { f(h);} };
}
template <typename T> static std::enable_if_t<!IsSingleArg<T>::value, ElfPatcher> fromLambda(T &&t)
{
return { t };
}
};
Q_DECLARE_METATYPE(ElfPatcher)
static std::unique_ptr<QTemporaryFile> patchElf(const QString &source, ElfPatcher patcher)
{
std::unique_ptr<QTemporaryFile> tmplib;
bool ok = false;
[&]() {
QFile srclib(source);
QVERIFY2(srclib.open(QIODevice::ReadOnly), qPrintable(srclib.errorString()));
qint64 srcsize = srclib.size();
const uchar *srcdata = srclib.map(0, srcsize, QFile::MapPrivateOption);
QVERIFY2(srcdata, qPrintable(srclib.errorString()));
// copy our source plugin so we can modify it
tmplib.reset(new QTemporaryFile(QTest::currentDataTag() + QString(".XXXXXX" SUFFIX)));
QVERIFY2(tmplib->open(), qPrintable(tmplib->errorString()));
// sanity-check
QByteArray magic = QByteArray::fromRawData(reinterpret_cast<const char *>(srcdata), SELFMAG);
QCOMPARE(magic, QByteArray(ELFMAG));
// copy everything via mmap()
QVERIFY2(tmplib->resize(srcsize), qPrintable(tmplib->errorString()));
uchar *dstdata = tmplib->map(0, srcsize);
memcpy(dstdata, srcdata, srcsize);
// now patch the file
patcher.f(reinterpret_cast<ElfHeader *>(dstdata), tmplib.get());
ok = true;
}();
if (!ok)
tmplib.reset();
return tmplib;
}
// All ELF systems are expected to support GCC expression statements
#define patchElf(source, patcher) __extension__({ \
auto r = patchElf(source, patcher); \
if (QTest::currentTestFailed()) return; \
std::move(r); \
})
#endif
static QString sys_qualifiedLibraryName(const QString &fileName)
{
QString name = QLatin1String("bin/") + QLatin1String(PREFIX) + fileName + QLatin1String(SUFFIX);
@ -111,13 +192,13 @@ private slots:
void errorString();
void loadHints();
void deleteinstanceOnUnload();
#if defined (__ELF__)
void loadDebugObj();
void loadCorruptElf_data();
void loadCorruptElf();
#endif
void loadMachO_data();
void loadMachO();
#if defined (Q_OS_UNIX)
void loadGarbage();
#endif
void relativePath();
void absolutePath();
void reloadPlugin();
@ -289,48 +370,253 @@ void tst_QPluginLoader::deleteinstanceOnUnload()
}
}
#if defined (__ELF__)
void tst_QPluginLoader::loadDebugObj()
{
#if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif
#if defined (__ELF__)
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/debugobj.so")));
QPluginLoader lib1(QFINDTESTDATA("elftest/debugobj.so"));
QCOMPARE(lib1.load(), false);
#endif
}
void tst_QPluginLoader::loadCorruptElf()
void tst_QPluginLoader::loadCorruptElf_data()
{
#if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif
#if defined (__ELF__)
if (sizeof(void*) == 8) {
QVERIFY(QFile::exists(QFINDTESTDATA("elftest/corrupt1.elf64.so")));
QTest::addColumn<QString>("snippet");
QTest::addColumn<ElfPatcher>("patcher");
auto newRow = [](const char *rowname, QString &&snippet, auto patcher) {
QTest::newRow(rowname) << std::move(snippet) << ElfPatcher::fromLambda(patcher);
};
QPluginLoader lib1(QFINDTESTDATA("elftest/corrupt1.elf64.so"));
QCOMPARE(lib1.load(), false);
QVERIFY2(lib1.errorString().contains("not an ELF object"), qPrintable(lib1.errorString()));
using H = ElfHeader *; // because I'm lazy
newRow("not-elf", "invalid signature", [](H h) {
h->e_ident[EI_MAG0] = 'Q';
h->e_ident[EI_MAG1] = 't';
});
QPluginLoader lib2(QFINDTESTDATA("elftest/corrupt2.elf64.so"));
QCOMPARE(lib2.load(), false);
QVERIFY2(lib2.errorString().contains("invalid"), qPrintable(lib2.errorString()));
newRow("wrong-word-size", "file is for a different word size", [](H h) {
h->e_ident[EI_CLASS] = sizeof(void *) == 8 ? ELFCLASS32 : ELFCLASS64;
QPluginLoader lib3(QFINDTESTDATA("elftest/corrupt3.elf64.so"));
QCOMPARE(lib3.load(), false);
QVERIFY2(lib3.errorString().contains("invalid"), qPrintable(lib3.errorString()));
} else if (sizeof(void*) == 4) {
QPluginLoader libW(QFINDTESTDATA("elftest/corrupt3.elf64.so"));
QCOMPARE(libW.load(), false);
QVERIFY2(libW.errorString().contains("architecture"), qPrintable(libW.errorString()));
} else {
QFAIL("Please port QElfParser to this platform or blacklist this test.");
}
#endif
// unnecessary, but we're doing it anyway
# ifdef _LP64
Elf32_Ehdr o;
o.e_phentsize = sizeof(Elf32_Phdr);
o.e_shentsize = sizeof(Elf32_Shdr);
# else
Elf64_Ehdr o;
o.e_phentsize = sizeof(Elf64_Phdr);
o.e_shentsize = sizeof(Elf64_Shdr);
# endif
memcpy(o.e_ident, h->e_ident, EI_NIDENT);
o.e_type = h->e_type;
o.e_machine = h->e_machine;
o.e_version = h->e_version;
o.e_entry = h->e_entry;
o.e_phoff = h->e_phoff;
o.e_shoff = h->e_shoff;
o.e_flags = h->e_flags;
o.e_ehsize = sizeof(o);
o.e_phnum = h->e_phnum;
o.e_shnum = h->e_shnum;
o.e_shstrndx = h->e_shstrndx;
memcpy(h, &o, sizeof(o));
});
newRow("invalid-word-size", "file is for a different word size", [](H h) {
h->e_ident[EI_CLASS] = ELFCLASSNONE;;
});
newRow("unknown-word-size", "file is for a different word size", [](H h) {
h->e_ident[EI_CLASS] |= 0x40;
});
newRow("wrong-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] = QSysInfo::ByteOrder == QSysInfo::LittleEndian ? ELFDATA2MSB : ELFDATA2LSB;
// unnecessary, but we're doing it anyway
h->e_type = qbswap(h->e_type);
h->e_machine = qbswap(h->e_machine);
h->e_version = qbswap(h->e_version);
h->e_entry = qbswap(h->e_entry);
h->e_phoff = qbswap(h->e_phoff);
h->e_shoff = qbswap(h->e_shoff);
h->e_flags = qbswap(h->e_flags);
h->e_ehsize = qbswap(h->e_ehsize);
h->e_phnum = qbswap(h->e_phnum);
h->e_phentsize = qbswap(h->e_phentsize);
h->e_shnum = qbswap(h->e_shnum);
h->e_shentsize = qbswap(h->e_shentsize);
h->e_shstrndx = qbswap(h->e_shstrndx);
});
newRow("invalid-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] = ELFDATANONE;
});
newRow("unknown-endian", "file is for the wrong endianness", [](H h) {
h->e_ident[EI_DATA] |= 0x40;
});
newRow("elf-version-0", "file has an unknown ELF version", [](H h) {
--h->e_ident[EI_VERSION];
});
newRow("elf-version-2", "file has an unknown ELF version", [](H h) {
++h->e_ident[EI_VERSION];
});
newRow("executable", "file is not a shared object", [](H h) {
h->e_type = ET_EXEC;
});
newRow("relocatable", "file is not a shared object", [](H h) {
h->e_type = ET_REL;
});
newRow("core-file", "file is not a shared object", [](H h) {
h->e_type = ET_CORE;
});
newRow("invalid-type", "file is not a shared object", [](H h) {
h->e_type |= 0x100;
});
newRow("wrong-arch", "file is for a different processor", [](H h) {
// could just ++h->e_machine...
# if defined(Q_PROCESSOR_X86_64)
h->e_machine = EM_AARCH64;
# elif defined(Q_PROCESSOR_ARM_64)
h->e_machine = EM_X86_64;
# elif defined(Q_PROCESSOR_X86_32)
h->e_machine = EM_ARM;
# elif defined(Q_PROCESSOR_ARM)
h->e_machine = EM_386;
# elif defined(Q_PROCESSOR_MIPS_64)
h->e_machine = EM_PPC64;
# elif defined(Q_PROCESSOR_MIPS_32)
h->e_machine = EM_PPC;
# elif defined(Q_PROCESSOR_POWER_64)
h->e_machine = EM_S390;
# elif defined(Q_PROCESSOR_POWER_32)
h->e_machine = EM_MIPS;
# endif
});
newRow("file-version-0", "file has an unknown ELF version", [](H h) {
--h->e_version;
});
newRow("file-version-2", "file has an unknown ELF version", [](H h) {
++h->e_version;
});
newRow("section-entry-size-zero", "unexpected section entry size", [](H h) {
h->e_shentsize = 0;
});
newRow("section-entry-small", "unexpected section entry size", [](H h) {
h->e_shentsize = alignof(ElfShdr);
});
newRow("section-entry-misaligned", "unexpected section entry size", [](H h) {
++h->e_shentsize;
});
newRow("no-sections", "is not a Qt plugin (.qtmetadata section not found)", [](H h){
h->e_shnum = h->e_shoff = h->e_shstrndx = 0;
});
// section table tests
newRow("section-table-starts-past-eof", "section table extends past the end of the file",
[](H h, QFile *f) {
h->e_shoff = f->size();
});
newRow("section-table-ends-past-eof", "section table extends past the end of the file",
[](H h, QFile *f) {
h->e_shoff = f->size() + 1 - h->e_shentsize * h->e_shnum;
});
static auto getSection = +[](H h, int index) {
auto sections = reinterpret_cast<ElfShdr *>(h->e_shoff + reinterpret_cast<uchar *>(h));
return sections + index;
};
// arbitrary section bounds checks
// section index = 0 is usually a NULL section, so we try 1
newRow("section1-starts-past-eof", "a section data extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, 1);
s->sh_offset = f->size();
});
newRow("section1-ends-past-eof", "a section data extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, 1);
s->sh_size = f->size() + 1 - s->sh_offset;
});
newRow("section1-bounds-overflow", "a section data extends past the end of the file", [](H h) {
ElfShdr *s = getSection(h, 1);
s->sh_size = -sizeof(*s);
});
// section header string table tests
newRow("shstrndx-invalid", "e_shstrndx greater than the number of sections", [](H h) {
h->e_shstrndx = h->e_shnum;
});
newRow("shstrtab-starts-past-eof", "section header string table extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_offset = f->size();
});
newRow("shstrtab-ends-past-eof", "section header string table extends past the end of the file",
[](H h, QFile *f) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_size = f->size() + 1 - s->sh_offset;
});
newRow("shstrtab-bounds-overflow", "section header string table extends past the end of the file", [](H h) {
ElfShdr *s = getSection(h, h->e_shstrndx);
s->sh_size = -sizeof(*s);
});
newRow("section-name-past-eof", "section name extends past the end of the file", [](H h, QFile *f) {
ElfShdr *section1 = getSection(h, 1);
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
section1->sh_name = f->size() - shstrtab->sh_offset;
});
newRow("section-name-past-end-of-shstrtab", "section name extends past the end of the file", [](H h) {
ElfShdr *section1 = getSection(h, 1);
ElfShdr *shstrtab = getSection(h, h->e_shstrndx);
section1->sh_name = shstrtab->sh_size;
});
newRow("debug-symbols", ".qtmetadata section not found", [](H h) {
// attempt to make it look like extracted debug info
for (int i = 1; i < h->e_shnum; ++i) {
ElfShdr *s = getSection(h, i);
if (s->sh_type == SHT_NOBITS)
break;
if (s->sh_type != SHT_NOTE && s->sh_flags & SHF_ALLOC)
s->sh_type = SHT_NOBITS;
}
});
// we don't know which section is .qtmetadata, so we just apply to all of them
static auto applyToAllSectionFlags = +[](H h, int flag) {
for (int i = 0; i < h->e_shnum; ++i)
getSection(h, i)->sh_flags |= flag;
};
newRow("qtmetadata-executable", ".qtmetadata section is executable", [](H h) {
applyToAllSectionFlags(h, SHF_EXECINSTR);
});
newRow("qtmetadata-writable", ".qtmetadata section is writable", [](H h) {
applyToAllSectionFlags(h, SHF_WRITE);
});
}
void tst_QPluginLoader::loadCorruptElf()
{
QFETCH(QString, snippet);
QFETCH(ElfPatcher, patcher);
std::unique_ptr<QTemporaryFile> tmplib =
patchElf(sys_qualifiedLibraryName("theplugin"), patcher);
QPluginLoader lib(tmplib->fileName());
QVERIFY(!lib.load());
QVERIFY2(lib.errorString().contains(snippet), qPrintable(lib.errorString()));
}
#endif // __ELF__
void tst_QPluginLoader::loadMachO_data()
{
#if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)
@ -404,21 +690,6 @@ void tst_QPluginLoader::loadMachO()
#endif
}
#if defined (Q_OS_UNIX)
void tst_QPluginLoader::loadGarbage()
{
#if !defined(QT_SHARED)
QSKIP("This test requires a shared build of Qt, as QPluginLoader::setFileName is a no-op in static builds");
#endif
for (int i=0; i<5; i++) {
const QString name = QLatin1String("elftest/garbage") + QString::number(i + 1) + QLatin1String(".so");
QPluginLoader lib(QFINDTESTDATA(name));
QCOMPARE(lib.load(), false);
QVERIFY(lib.errorString() != QString("Unknown error"));
}
}
#endif
void tst_QPluginLoader::relativePath()
{
#if !defined(QT_SHARED)