mirror of
https://bitbucket.org/smil3y/katie.git
synced 2025-02-23 18:32:55 +00:00
reimplement plugin verification via system provided ELF structures
this is likely unsafe to use on multi-library hosts but the option to disable the verification is there Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
6996092330
commit
3deb8da473
3 changed files with 41 additions and 169 deletions
|
@ -24,8 +24,8 @@ All standard requirements besides C++11 compatible runtime and compiler
|
||||||
should be checked for during build if they are newer than POSIX.1c
|
should be checked for during build if they are newer than POSIX.1c
|
||||||
(https://en.wikipedia.org/wiki/POSIX). Read any documentation that may be
|
(https://en.wikipedia.org/wiki/POSIX). Read any documentation that may be
|
||||||
relevant to the changes you make such as manual pages for Linux
|
relevant to the changes you make such as manual pages for Linux
|
||||||
(https://linux.die.net/man/), FreeBSD (https://www.freebsd.org/cgi/man.cgi)
|
(https://linux.die.net/man/), FreeBSD (https://www.freebsd.org/cgi/man.cgi),
|
||||||
and OpenBSD (https://man.openbsd.org/).
|
OpenBSD (https://man.openbsd.org/) and Solaris (https://docs.oracle.com/en/).
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
You can import tests and adjust them as needed from stock Qt4 copy
|
You can import tests and adjust them as needed from stock Qt4 copy
|
||||||
|
|
|
@ -54,6 +54,17 @@
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifndef QT_NO_PLUGIN_CHECK
|
||||||
|
# include <elf.h>
|
||||||
|
# if QT_POINTER_SIZE == 8
|
||||||
|
# define QT_ELF_EHDR_TYPE Elf64_Ehdr
|
||||||
|
# define QT_ELF_SHDR_TYPE Elf64_Shdr
|
||||||
|
# else
|
||||||
|
# define QT_ELF_EHDR_TYPE Elf32_Ehdr
|
||||||
|
# define QT_ELF_SHDR_TYPE Elf32_Shdr
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(QMutex, qt_library_mutex)
|
Q_GLOBAL_STATIC(QMutex, qt_library_mutex)
|
||||||
|
@ -75,11 +86,10 @@ Q_GLOBAL_STATIC(QMutex, qt_library_mutex)
|
||||||
system-specific library locations (e.g. \c LD_LIBRARY_PATH on
|
system-specific library locations (e.g. \c LD_LIBRARY_PATH on
|
||||||
Unix), unless the file name has an absolute path. If the file
|
Unix), unless the file name has an absolute path. If the file
|
||||||
cannot be found, QLibrary tries the name with different
|
cannot be found, QLibrary tries the name with different
|
||||||
platform-specific file suffixes, like ".so" on Unix, ".dylib" on
|
platform-specific file suffixes, like ".so" on Unix. This makes
|
||||||
the Mac, or ".dll" on Windows. This makes it possible
|
it possible to specify shared libraries that are only identified
|
||||||
to specify shared libraries that are only identified by their
|
by their basename (i.e. without their suffix), so the same code
|
||||||
basename (i.e. without their suffix), so the same code will work
|
will work on different operating systems.
|
||||||
on different operating systems.
|
|
||||||
|
|
||||||
The most important functions are load() to dynamically load the
|
The most important functions are load() to dynamically load the
|
||||||
library file, isLoaded() to check whether loading was successful,
|
library file, isLoaded() to check whether loading was successful,
|
||||||
|
@ -138,157 +148,10 @@ Q_GLOBAL_STATIC(QMutex, qt_library_mutex)
|
||||||
\sa loadHints
|
\sa loadHints
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#ifndef QT_NO_PLUGIN_CHECK
|
#ifndef QT_NO_PLUGIN_CHECK
|
||||||
struct qt_token_info
|
|
||||||
{
|
|
||||||
qt_token_info(const char *f, const ulong fc)
|
|
||||||
: fields(f), field_count(fc), results(fc), lengths(fc)
|
|
||||||
{
|
|
||||||
results.fill(0);
|
|
||||||
lengths.fill(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *fields;
|
|
||||||
const ulong field_count;
|
|
||||||
|
|
||||||
QVector<const char *> results;
|
|
||||||
QVector<ulong> lengths;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
return values:
|
|
||||||
1 parse ok
|
|
||||||
0 eos
|
|
||||||
-1 parse error
|
|
||||||
*/
|
|
||||||
static int qt_tokenize(const char *s, ulong s_len, ulong *advance,
|
|
||||||
qt_token_info &token_info)
|
|
||||||
{
|
|
||||||
if (!s)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ulong pos = 0, field = 0, fieldlen = 0;
|
|
||||||
char current;
|
|
||||||
int ret = -1;
|
|
||||||
*advance = 0;
|
|
||||||
for (;;) {
|
|
||||||
current = s[pos];
|
|
||||||
|
|
||||||
// next char
|
|
||||||
++pos;
|
|
||||||
++fieldlen;
|
|
||||||
++*advance;
|
|
||||||
|
|
||||||
if (! current || pos == s_len + 1) {
|
|
||||||
// save result
|
|
||||||
token_info.results[(int)field] = s;
|
|
||||||
token_info.lengths[(int)field] = fieldlen - 1;
|
|
||||||
|
|
||||||
// end of string
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current == token_info.fields[field]) {
|
|
||||||
// save result
|
|
||||||
token_info.results[(int)field] = s;
|
|
||||||
token_info.lengths[(int)field] = fieldlen - 1;
|
|
||||||
|
|
||||||
// end of field
|
|
||||||
fieldlen = 0;
|
|
||||||
++field;
|
|
||||||
if (field == token_info.field_count - 1) {
|
|
||||||
// parse ok
|
|
||||||
ret = 1;
|
|
||||||
}
|
|
||||||
if (field == token_info.field_count) {
|
|
||||||
// done parsing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset string and its length
|
|
||||||
s = s + pos;
|
|
||||||
s_len -= pos;
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
returns true if the string s was correctly parsed, false otherwise.
|
|
||||||
*/
|
|
||||||
static bool qt_parse_pattern(const char *s, uint *version)
|
|
||||||
{
|
|
||||||
bool ret = true;
|
|
||||||
|
|
||||||
qt_token_info pinfo("=\n", 2);
|
|
||||||
int parse;
|
|
||||||
ulong at = 0, advance, parselen = qstrlen(s);
|
|
||||||
do {
|
|
||||||
parse = qt_tokenize(s + at, parselen, &advance, pinfo);
|
|
||||||
if (parse == -1) {
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
at += advance;
|
|
||||||
parselen -= advance;
|
|
||||||
|
|
||||||
if (qstrncmp("version", pinfo.results[0], pinfo.lengths[0]) == 0) {
|
|
||||||
QByteArray qv(pinfo.results[1], pinfo.lengths[1]);
|
|
||||||
bool ok;
|
|
||||||
*version = qv.toUInt(&ok, 0);
|
|
||||||
}
|
|
||||||
} while (parse == 1 && parselen > 0);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long qt_find_pattern(const char *s, ulong s_len)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
we search from the end of the file because on the supported
|
|
||||||
systems, the read-only data/text segments are placed at the end
|
|
||||||
of the file. HOWEVER, when building with debugging enabled, all
|
|
||||||
the debug symbols are placed AFTER the data/text segments.
|
|
||||||
|
|
||||||
what does this mean? when building in release mode, the search
|
|
||||||
is fast because the data we are looking for is at the end of the
|
|
||||||
file... when building in debug mode, the search is slower
|
|
||||||
because we have to skip over all the debugging symbols first
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const char pattern[] = "pattern=KT_PLUGIN_VERIFICATION_DATA";
|
|
||||||
static const ulong p_len = qstrlen(pattern);
|
|
||||||
|
|
||||||
if (!s || p_len > s_len)
|
|
||||||
return -1;
|
|
||||||
ulong i, hs = 0, hp = 0, delta = s_len - p_len;
|
|
||||||
|
|
||||||
for (i = 0; i < p_len; ++i) {
|
|
||||||
hs += s[delta + i];
|
|
||||||
hp += pattern[i];
|
|
||||||
}
|
|
||||||
i = delta;
|
|
||||||
for (;;) {
|
|
||||||
if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0)
|
|
||||||
return i;
|
|
||||||
if (i == 0)
|
|
||||||
break;
|
|
||||||
--i;
|
|
||||||
hs -= s[i + p_len];
|
|
||||||
hs += s[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This opens the specified library, mmaps it into memory, and searches
|
This opens the specified library, mmaps it into memory, and searches
|
||||||
for the KT_PLUGIN_VERIFICATION_DATA. The advantage of this approach is that
|
for the plugin seciton. The advantage of this approach is that
|
||||||
we can get the verification data without have to actually load the library.
|
we can get the verification data without have to actually load the library.
|
||||||
This lets us detect mismatches more safely.
|
This lets us detect mismatches more safely.
|
||||||
|
|
||||||
|
@ -308,29 +171,41 @@ static bool qt_unix_query(const QString &library, uint *version, QLibraryPrivate
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong fdlen = file.size();
|
const char *filedata = reinterpret_cast<char*>(file.map(0, file.size()));
|
||||||
const char *filedata = reinterpret_cast<char*>(file.map(0, fdlen));
|
|
||||||
if (filedata == 0) {
|
if (filedata == 0) {
|
||||||
// try reading the data into memory instead
|
// try reading the data into memory instead
|
||||||
const QByteArray data = file.readAll();
|
const QByteArray data = file.readAll();
|
||||||
filedata = data.constData();
|
filedata = data.constData();
|
||||||
fdlen = data.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ELF binaries build with GNU or Clang have .ktplugin sections.
|
ELF binaries build with GNU or Clang have .ktplugin sections.
|
||||||
*/
|
*/
|
||||||
const long pos = qt_find_pattern(filedata, fdlen);
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (pos >= 0)
|
QT_ELF_EHDR_TYPE *ehdr = (QT_ELF_EHDR_TYPE*)(filedata);
|
||||||
ret = qt_parse_pattern(filedata + pos, version);
|
QT_ELF_SHDR_TYPE *shdr = (QT_ELF_SHDR_TYPE*)(filedata + ehdr->e_shoff);
|
||||||
|
|
||||||
|
QT_ELF_SHDR_TYPE *sh_strtab = &shdr[ehdr->e_shstrndx];
|
||||||
|
const char *const sh_strtab_p = filedata + sh_strtab->sh_offset;
|
||||||
|
|
||||||
|
for (int i = 0; i < ehdr->e_shnum; ++i) {
|
||||||
|
const char* sectioname = sh_strtab_p + shdr[i].sh_name;
|
||||||
|
if (qstrcmp(sectioname, ".ktplugin") == 0) {
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
compatiblity between releases is not guratneed thus no version matching is done
|
||||||
|
*/
|
||||||
|
*version = QT_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library);
|
lib->errorString = QLibrary::tr("Plugin verification data mismatch in '%1'").arg(library);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // QT_NO_PLUGIN_CHECK
|
#endif // QT_NO_PLUGIN_CHECK
|
||||||
|
|
||||||
typedef QMap<QString, QLibraryPrivate*> LibraryMap;
|
typedef QMap<QString, QLibraryPrivate*> LibraryMap;
|
||||||
|
|
|
@ -47,9 +47,10 @@ typedef QObject *(*QtPluginInstanceFunction)();
|
||||||
#define Q_EXPORT_PLUGIN(PLUGIN) \
|
#define Q_EXPORT_PLUGIN(PLUGIN) \
|
||||||
Q_EXPORT_PLUGIN2(PLUGIN, PLUGIN)
|
Q_EXPORT_PLUGIN2(PLUGIN, PLUGIN)
|
||||||
|
|
||||||
#if defined(__ELF__)
|
#if !defined(QT_NO_PLUGIN_CHECK)
|
||||||
# define Q_PLUGIN_VERIFICATION_SECTION \
|
# define Q_PLUGIN_VERIFICATION_SECTION \
|
||||||
__attribute__ ((section (".ktplugin"))) __attribute__((used))
|
__attribute__ ((section (".ktplugin"))) __attribute__((used)) \
|
||||||
|
static const char kt_plugin_verification_data[] = QT_VERSION_HEX_STR;
|
||||||
#else
|
#else
|
||||||
# define Q_PLUGIN_VERIFICATION_SECTION
|
# define Q_PLUGIN_VERIFICATION_SECTION
|
||||||
#endif
|
#endif
|
||||||
|
@ -58,11 +59,7 @@ typedef QObject *(*QtPluginInstanceFunction)();
|
||||||
// qlibrary.cpp as well. changing the pattern will break all
|
// qlibrary.cpp as well. changing the pattern will break all
|
||||||
// backwards compatibility as well (no old plugins will be loaded).
|
// backwards compatibility as well (no old plugins will be loaded).
|
||||||
#define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \
|
#define Q_EXPORT_PLUGIN2(PLUGIN, PLUGINCLASS) \
|
||||||
Q_PLUGIN_VERIFICATION_SECTION static const char kt_plugin_verification_data[] = \
|
Q_PLUGIN_VERIFICATION_SECTION \
|
||||||
"pattern=KT_PLUGIN_VERIFICATION_DATA\n" \
|
|
||||||
"version=" QT_VERSION_HEX_STR "\n"; \
|
|
||||||
Q_EXTERN_C Q_DECL_EXPORT const char * kt_plugin_query_verification_data() \
|
|
||||||
{ return kt_plugin_verification_data; } \
|
|
||||||
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) * kt_plugin_instance() \
|
Q_EXTERN_C Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) * kt_plugin_instance() \
|
||||||
{ \
|
{ \
|
||||||
static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
|
static QT_PREPEND_NAMESPACE(QPointer)<QT_PREPEND_NAMESPACE(QObject)> _instance; \
|
||||||
|
|
Loading…
Add table
Reference in a new issue