Revision control
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsharedimageloader_p.h"
++ #include <private/qobject_p.h>
++ #include <private/qimage_p.h>
++
++ #include <QtCore/qpointer.h>
++ #include <QSharedMemory>
++
++ #include <memory>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_LOGGING_CATEGORY(lcSharedImage, "qt.quick.sharedimage");
++
++ struct SharedImageHeader {
++ quint8 magic;
++ quint8 version;
++ quint16 offset;
++ qint32 width;
++ qint32 height;
++ qint32 bpl;
++ QImage::Format format;
++ };
++ Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4 == 0);
++
++ #if QT_CONFIG(sharedmemory)
++ struct SharedImageInfo {
++ QString path;
++ QPointer<QSharedMemory> shmp;
++ };
++
++ void cleanupSharedImage(void *cleanupInfo)
++ {
++ if (!cleanupInfo)
++ return;
++ SharedImageInfo *sii = static_cast<SharedImageInfo *>(cleanupInfo);
++ qCDebug(lcSharedImage) << "Cleanup called for" << sii->path;
++ if (sii->shmp.isNull()) {
++ qCDebug(lcSharedImage) << "shm is 0 for" << sii->path;
++ return;
++ }
++ QSharedMemory *shm = sii->shmp.data();
++ sii->shmp.clear();
++ delete shm; // destructor detaches
++ delete sii;
++ }
++ #else
++ void cleanupSharedImage(void *) {}
++ #endif
++
++ class QSharedImageLoaderPrivate : public QObjectPrivate
++ {
++ Q_DECLARE_PUBLIC(QSharedImageLoader)
++
++ public:
++ QSharedImageLoaderPrivate() {}
++
++ QImage load(const QString &path, QSharedImageLoader::ImageParameters *params);
++
++ void storeImageToMem(void *data, const QImage &img);
++
++ bool verifyMem(const void *data, int size);
++
++ QImage createImageFromMem(const void *data, void *cleanupInfo);
++
++ };
++
++
++ void QSharedImageLoaderPrivate::storeImageToMem(void *data, const QImage &img)
++ {
++ Q_ASSERT(data && !img.isNull());
++
++ SharedImageHeader *h = static_cast<SharedImageHeader *>(data);
++ h->magic = 'Q';
++ h->version = 1;
++ h->offset = sizeof(SharedImageHeader);
++ h->width = img.width();
++ h->height = img.height();
++ h->bpl = img.bytesPerLine();
++ h->format = img.format();
++
++ uchar *p = static_cast<uchar *>(data) + sizeof(SharedImageHeader);
++ memcpy(p, img.constBits(), img.sizeInBytes());
++ }
++
++
++ bool QSharedImageLoaderPrivate::verifyMem(const void *data, int size)
++ {
++ if (!data || size < int(sizeof(SharedImageHeader)))
++ return false;
++
++ const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
++ if ((h->magic != 'Q')
++ || (h->version < 1)
++ || (h->offset < sizeof(SharedImageHeader))
++ || (h->width <= 0)
++ || (h->height <= 0)
++ || (h->bpl <= 0)
++ || (h->format <= QImage::Format_Invalid)
++ || (h->format >= QImage::NImageFormats)) {
++ return false;
++ }
++
++ const int depth = qt_depthForFormat(h->format);
++ if (h->bpl < (qint64(h->width) * depth + 7) / 8)
++ return false;
++
++ qint64 availSize = size - h->offset;
++ if (qint64(h->height) * h->bpl > availSize)
++ return false;
++
++ return true;
++ }
++
++
++ QImage QSharedImageLoaderPrivate::createImageFromMem(const void *data, void *cleanupInfo)
++ {
++ const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data);
++ const uchar *p = static_cast<const uchar *>(data) + h->offset;
++
++ QImage img(p, h->width, h->height, h->bpl, h->format, cleanupSharedImage, cleanupInfo);
++ return img;
++ }
++
++
++ QImage QSharedImageLoaderPrivate::load(const QString &path, QSharedImageLoader::ImageParameters *params)
++ {
++ #if QT_CONFIG(sharedmemory)
++ Q_Q(QSharedImageLoader);
++
++ QImage nil;
++ if (path.isEmpty())
++ return nil;
++
++ auto shm = std::make_unique<QSharedMemory>(QSharedMemory::legacyNativeKey(q->key(path, params)));
++ bool locked = false;
++
++ if (!shm->attach(QSharedMemory::ReadOnly)) {
++ QImage img = q->loadFile(path, params);
++ if (img.isNull())
++ return nil;
++ size_t size = sizeof(SharedImageHeader) + img.sizeInBytes();
++ if (size > size_t(std::numeric_limits<int>::max())) {
++ qCDebug(lcSharedImage) << "Image" << path << "to large to load";
++ return nil;
++ } else if (shm->create(int(size))) {
++ qCDebug(lcSharedImage) << "Created new shm segment of size" << size << "for image" << path;
++ if (!shm->lock()) {
++ qCDebug(lcSharedImage) << "Lock1 failed!?" << shm->errorString();
++ return nil;
++ }
++ locked = true;
++ storeImageToMem(shm->data(), img);
++ } else if (shm->error() == QSharedMemory::AlreadyExists) {
++ // race handling: other process may have created the share while
++ // we loaded the image, so try again to just attach
++ if (!shm->attach(QSharedMemory::ReadOnly)) {
++ qCDebug(lcSharedImage) << "Attach to existing failed?" << shm->errorString();
++ return nil;
++ }
++ } else {
++ qCDebug(lcSharedImage) << "Create failed?" << shm->errorString();
++ return nil;
++ }
++ }
++
++ Q_ASSERT(shm->isAttached());
++
++ if (!locked) {
++ if (!shm->lock()) {
++ qCDebug(lcSharedImage) << "Lock2 failed!?" << shm->errorString();
++ return nil;
++ }
++ locked = true;
++ }
++
++ if (!verifyMem(shm->constData(), shm->size())) {
++ qCDebug(lcSharedImage) << "Verifymem failed!?";
++ shm->unlock();
++ return nil;
++ }
++
++ QSharedMemory *shmp = shm.release();
++ SharedImageInfo *sii = new SharedImageInfo;
++ sii->path = path;
++ sii->shmp = shmp;
++ QImage shImg = createImageFromMem(shmp->constData(), sii);
++
++ if (!shmp->unlock()) {
++ qCDebug(lcSharedImage) << "UnLock failed!?";
++ }
++
++ return shImg;
++ #else
++ Q_UNUSED(path);
++ Q_UNUSED(params);
++ return QImage();
++ #endif
++ }
++
++
++ QSharedImageLoader::QSharedImageLoader(QObject *parent)
++ : QObject(*new QSharedImageLoaderPrivate, parent)
++ {
++ }
++
++ QSharedImageLoader::~QSharedImageLoader()
++ {
++ }
++
++ QImage QSharedImageLoader::load(const QString &path, ImageParameters *params)
++ {
++ Q_D(QSharedImageLoader);
++
++ return d->load(path, params);
++ }
++
++ QImage QSharedImageLoader::loadFile(const QString &path, ImageParameters *params)
++ {
++ Q_UNUSED(params);
++
++ return QImage(path);
++ }
++
++ QString QSharedImageLoader::key(const QString &path, ImageParameters *params)
++ {
++ Q_UNUSED(params);
++
++ return path;
++ }
++
++
++ QT_END_NAMESPACE
++
++ #include "moc_qsharedimageloader_p.cpp"