view libgui/qterminal/libqterminal/unix/BlockArray.cpp @ 30264:de3275323dff

libqterminal: Avoid integer overflow in multiplication. * libgui/qterminal/libqterminal/unix/BlockArray.cpp (moveBlock, BlockArray::increaseBuffer): Cast integer offset argument of fseek to long int before multiplication to avoid integer overflow. The overflow is very unlikely to happen and this is adopted code (not actually maintained). But it's an easy fix that doesn't clutter the code too much.
author Markus Mützel <markus.muetzel@gmx.de>
date Sun, 31 Oct 2021 18:06:22 +0100
parents 194eb4bd202b
children
line wrap: on
line source

/*
    This file is part of Konsole, an X terminal.
    Copyright (C) 2000, 2013 by Stephan Kulow <coolo@kde.org>

    Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301  USA.

*/

// Own
#include "unix/BlockArray.h"

#include <QtCore>

// System
#include <assert.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <unistd.h>
#include <stdio.h>


static int blocksize = 0;

BlockArray::BlockArray()
    : size(0),
      current(size_t(-1)),
      index(size_t(-1)),
      lastmap(nullptr),
      lastmap_index(size_t(-1)),
      lastblock(nullptr), ion(-1),
      length(0)
{
    // lastmap_index = index = current = size_t(-1);
    if (blocksize == 0)
        blocksize = ((sizeof(Block) / getpagesize()) + 1) * getpagesize();

}

BlockArray::~BlockArray()
{
    setHistorySize(0);
    assert(!lastblock);
}

size_t BlockArray::append(Block *block)
{
    if (!size)
        return size_t(-1);

    ++current;
    if (current >= size) current = 0;

    int rc;
    rc = lseek(ion, current * blocksize, SEEK_SET); if (rc < 0) { perror("HistoryBuffer::add.seek"); setHistorySize(0); return size_t(-1); }
    rc = write(ion, block, blocksize); if (rc < 0) { perror("HistoryBuffer::add.write"); setHistorySize(0); return size_t(-1); }

    length++;
    if (length > size) length = size;

    ++index;

    delete block;
    return current;
}

size_t BlockArray::newBlock()
{
    if (!size)
        return size_t(-1);
    append(lastblock);

    lastblock = new Block();
    return index + 1;
}

Block *BlockArray::lastBlock() const
{
    return lastblock;
}

bool BlockArray::has(size_t i) const
{
    if (i == index + 1)
        return true;

    if (i > index)
        return false;
    if (index - i >= length)
        return false;
    return true;
}

const Block* BlockArray::at(size_t i)
{
    if (i == index + 1)
        return lastblock;

    if (i == lastmap_index)
        return lastmap;

    if (i > index) {
        qDebug() << "BlockArray::at() i > index\n";
        return nullptr;
    }

//     if (index - i >= length) {
//         kDebug(1211) << "BlockArray::at() index - i >= length\n";
//         return 0;
//     }

    size_t j = i; // (current - (index - i) + (index/size+1)*size) % size ;

    assert(j < size);
    unmap();

    Block *block = (Block*)mmap(nullptr, blocksize, PROT_READ, MAP_PRIVATE, ion, j * blocksize);

    if (block == (Block*)-1) { perror("mmap"); return nullptr; }

    lastmap = block;
    lastmap_index = i;

    return block;
}

void BlockArray::unmap()
{
    if (lastmap) {
        int res = munmap((char*)lastmap, blocksize);
        if (res < 0) perror("munmap");
    }
    lastmap = nullptr;
    lastmap_index = size_t(-1);
}

bool BlockArray::setSize(size_t newsize)
{
    return setHistorySize(newsize * 1024 / blocksize);
}

bool BlockArray::setHistorySize(size_t newsize)
{
//    kDebug(1211) << "setHistorySize " << size << " " << newsize;

    if (size == newsize)
        return false;

    unmap();

    if (!newsize) {
        delete lastblock;
        lastblock = nullptr;
        if (ion >= 0) close(ion);
        ion = -1;
        current = size_t(-1);
        return true;
    }

    if (!size) {
        FILE* tmp = tmpfile();
        if (!tmp) {
            perror("konsole: cannot open temp file.\n");
        } else {
            ion = dup(fileno(tmp));
            if (ion<0) {
                perror("konsole: cannot dup temp file.\n");
                fclose(tmp);
            }
        }
        if (ion < 0)
            return false;

        assert(!lastblock);

        lastblock = new Block();
        size = newsize;
        return false;
    }

    if (newsize > size) {
        increaseBuffer();
        size = newsize;
        return false;
    } else {
        decreaseBuffer(newsize);
        if (ftruncate(ion, length*blocksize) == -1)
		  perror("ftruncate");
        size = newsize;

        return true;
    }
}

void moveBlock(FILE *fion, int cursor, int newpos, char *buffer2)
{
    int res = fseek(fion, static_cast<long int> (cursor) * blocksize, SEEK_SET);
    if (res)
        perror("fseek");
    res = fread(buffer2, blocksize, 1, fion);
    if (res != 1)
        perror("fread");

    res = fseek(fion, static_cast<long int> (newpos) * blocksize, SEEK_SET);
    if (res)
        perror("fseek");
    res = fwrite(buffer2, blocksize, 1, fion);
    if (res != 1)
        perror("fwrite");
    //    printf("moving block %d to %d\n", cursor, newpos);
}

void BlockArray::decreaseBuffer(size_t newsize)
{
    if (index < newsize) // still fits in whole
        return;

    int offset = (current - (newsize - 1) + size) % size;

    if (!offset)
        return;

    // The Block constructor could do somthing in future...
    char *buffer1 = new char[blocksize];

    FILE *fion = fdopen(dup(ion), "w+b");
    if (!fion) {
        delete [] buffer1;
        perror("fdopen/dup");
        return;
    }

    int firstblock;
    if (current <= newsize) {
        firstblock = current + 1;
    } else {
        firstblock = 0;
    }

    size_t oldpos;
    for (size_t i = 0, cursor=firstblock; i < newsize; i++) {
        oldpos = (size + cursor + offset) % size;
        moveBlock(fion, oldpos, cursor, buffer1);
        if (oldpos < newsize) {
            cursor = oldpos;
        } else
            cursor++;
    }

    current = newsize - 1;
    length = newsize;

    delete [] buffer1;

    fclose(fion);

}

void BlockArray::increaseBuffer()
{
    if (index < size) // not even wrapped once
        return;

    int offset = (current + size + 1) % size;
    if (!offset) // no moving needed
        return;

    // The Block constructor could do somthing in future...
    char *buffer1 = new char[blocksize];
    char *buffer2 = new char[blocksize];

    int runs = 1;
    int bpr = size; // blocks per run

    if (size % offset == 0) {
        bpr = size / offset;
        runs = offset;
    }

    FILE *fion = fdopen(dup(ion), "w+b");
    if (!fion) {
        perror("fdopen/dup");
	delete [] buffer1;
	delete [] buffer2;
        return;
    }

    int res;
    for (int i = 0; i < runs; i++)
    {
        // free one block in chain
        int firstblock = (offset + i) % size;
        res = fseek(fion, static_cast<long int> (firstblock) * blocksize, SEEK_SET);
        if (res)
            perror("fseek");
        res = fread(buffer1, blocksize, 1, fion);
        if (res != 1)
            perror("fread");
        int newpos = 0;
        for (int j = 1, cursor=firstblock; j < bpr; j++)
        {
            cursor = (cursor + offset) % size;
            newpos = (cursor - offset + size) % size;
            moveBlock(fion, cursor, newpos, buffer2);
        }
        res = fseek(fion, static_cast<long int> (i) * blocksize, SEEK_SET);
        if (res)
            perror("fseek");
        res = fwrite(buffer1, blocksize, 1, fion);
        if (res != 1)
            perror("fwrite");
    }
    current = size - 1;
    length = size;

    delete [] buffer1;
    delete [] buffer2;

    fclose(fion);

}