Range-check all values passed to C code.

This commit is contained in:
Florian Bruhin 2014-05-14 23:29:18 +02:00
parent daf42fdc0d
commit 603fbdf239
8 changed files with 154 additions and 25 deletions

View File

@ -28,12 +28,13 @@ from PyQt5.QtCore import pyqtSlot, Qt, QObject, QProcess
from PyQt5.QtGui import QClipboard
from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog
import qutebrowser.utils.url as urlutils
import qutebrowser.utils.message as message
import qutebrowser.commands.utils as cmdutils
import qutebrowser.utils.webelem as webelem
import qutebrowser.config.config as config
import qutebrowser.browser.hints as hints
import qutebrowser.utils.url as urlutils
import qutebrowser.utils.message as message
import qutebrowser.utils.webelem as webelem
import qutebrowser.utils.misc as utils
from qutebrowser.utils.misc import shell_escape
from qutebrowser.commands.exceptions import CommandError
@ -75,6 +76,7 @@ class CurCommandDispatcher(QObject):
perc = int(count)
else:
perc = float(perc)
perc = utils.check_overflow(perc, 'int', fatal=False)
frame = self._tabs.currentWidget().page_.currentFrame()
m = frame.scrollBarMaximum(orientation)
if m == 0:
@ -294,6 +296,8 @@ class CurCommandDispatcher(QObject):
"""
dx = int(int(count) * float(dx))
dy = int(int(count) * float(dy))
cmdutils.check_overflow(dx, 'int')
cmdutils.check_overflow(dy, 'int')
self._tabs.currentWidget().page_.currentFrame().scroll(dx, dy)
@cmdutils.register(instance='mainwindow.tabs.cur', name='scroll_perc_x',
@ -333,8 +337,11 @@ class CurCommandDispatcher(QObject):
"""
frame = self._tabs.currentWidget().page_.currentFrame()
size = frame.geometry()
frame.scroll(int(count) * float(mx) * size.width(),
int(count) * float(my) * size.height())
dx = int(count) * float(mx) * size.width()
dy = int(count) * float(my) * size.height()
cmdutils.check_overflow(dx, 'int')
cmdutils.check_overflow(dy, 'int')
frame.scroll(dx, dy)
@cmdutils.register(instance='mainwindow.tabs.cur')
def yank(self, sel=False):

View File

@ -24,11 +24,32 @@ Module attributes:
import inspect
from collections import Iterable
import qutebrowser.utils.misc as utils
from qutebrowser.commands._command import Command
from qutebrowser.commands.exceptions import CommandError
cmd_dict = {}
def check_overflow(arg, ctype):
"""Check if the given argument is in bounds for the given type.
Args:
arg: The argument to check
ctype: The C/Qt type to check as a string.
Raise:
CommandError if the argument is out of bounds.
ValueError if the given ctype is unknown.
"""
# FIXME we somehow should have nicer exceptions...
try:
utils.check_overflow(arg, ctype)
except OverflowError:
raise CommandError("Numeric argument is too large for internal {} "
"representation.".format(ctype))
def arg_or_count(arg, count, default=None, countzero=None):
"""Get a value based on an argument and count given to a command.

View File

@ -568,6 +568,8 @@ class WebKitBytes(BaseType):
if self.maxsize is not None and val > self.maxsize:
raise ValidationError(value, "must be {} "
"maximum!".format(self.maxsize))
if val < 0:
raise ValidationError(value, "must be 0 minimum!")
def transform(self, value):
if value == '':

View File

@ -25,16 +25,12 @@ DATA: The config defaults, an OrderedDict of sections.
"""
import re
import struct
from collections import OrderedDict
from qutebrowser.config._value import SettingValue
import qutebrowser.config._conftypes as types
import qutebrowser.config._sections as sect
INT_MAX = 2 ** (8 * struct.calcsize('@i')) // 2 - 1
INT64_MAX = 2 ** 64 // 2 - 1
from qutebrowser.utils.misc import MAXVALS
FIRST_COMMENT = r"""
@ -253,7 +249,7 @@ DATA = OrderedDict([
('input', sect.KeyValue(
('timeout',
SettingValue(types.Int(minval=0), '500'),
SettingValue(types.Int(minval=0, maxval=MAXVALS['int']), '500'),
"Timeout for ambiguous keybindings."),
('insert-mode-on-plugins',
@ -301,11 +297,11 @@ DATA = OrderedDict([
"Whether to wrap when changing tabs."),
('min-tab-width',
SettingValue(types.Int(), '100'),
SettingValue(types.Int(minval=1), '100'),
"The minimum width of a tab."),
('max-tab-width',
SettingValue(types.Int(), '200'),
SettingValue(types.Int(minval=1), '200'),
"The maximum width of a tab."),
('show-favicons',
@ -477,37 +473,43 @@ DATA = OrderedDict([
"Font family for fantasy fonts."),
('font-size-minimum',
SettingValue(types.Int(none=True), ''),
SettingValue(types.Int(none=True, minval=1, maxval=MAXVALS['int']),
''),
"The hard minimum font size."),
('font-size-minimum-logical',
SettingValue(types.Int(none=True), ''),
SettingValue(types.Int(none=True, minval=1, maxval=MAXVALS['int']),
''),
"The minimum logical font size that is applied when zooming out."),
('font-size-default',
SettingValue(types.Int(none=True), ''),
SettingValue(types.Int(none=True, minval=1, maxval=MAXVALS['int']),
''),
"The default font size for regular text."),
('font-size-default-fixed',
SettingValue(types.Int(none=True), ''),
SettingValue(types.Int(none=True, minval=1, maxval=MAXVALS['int']),
''),
"The default font size for fixed-pitch text."),
('maximum-pages-in-cache',
SettingValue(types.Int(none=True), ''),
SettingValue(types.Int(none=True, minval=0, maxval=MAXVALS['int']),
''),
"Sets the maximum number of pages to hold in the memory page cache."),
('object-cache-capacities',
SettingValue(types.WebKitBytesList(length=3, maxsize=INT_MAX), ''),
SettingValue(types.WebKitBytesList(length=3, maxsize=MAXVALS['int']),
''),
"Specifies the capacities for the memory cache for dead objects "
"such as stylesheets or scripts. Three values are expected: "
"cacheMinDeadCapacity, cacheMaxDead, totalCapacity"),
('offline-storage-default-quota',
SettingValue(types.WebKitBytes(maxsize=INT64_MAX), ''),
SettingValue(types.WebKitBytes(maxsize=MAXVALS['int64']), ''),
"Default quota for new offline storage databases."),
('offline-web-application-cache-quota',
SettingValue(types.WebKitBytes(maxsize=INT64_MAX), ''),
SettingValue(types.WebKitBytes(maxsize=MAXVALS['int64']), ''),
"Quota for the offline web application cache."),
)),

View File

@ -34,6 +34,48 @@ import qutebrowser.utils.misc as utils
from qutebrowser.test.helpers import environ_set_temp
class CheckOverflowTests(TestCase):
"""Test check_overflow."""
INT32_MIN = -(2 ** 31)
INT32_MAX = 2 ** 31 - 1
INT64_MIN = -(2 ** 63)
INT64_MAX = 2 ** 63 - 1
GOOD_VALUES = {
'int': [-1, 0, 1, 23.42, INT32_MIN, INT32_MAX],
'int64': [-1, 0, 1, 23.42, INT64_MIN, INT64_MAX],
}
BAD_VALUES = {
'int': [(INT32_MIN - 1, INT32_MIN),
(INT32_MAX + 1, INT32_MAX),
(float(INT32_MAX + 1), INT32_MAX)],
'int64': [(INT64_MIN - 1, INT64_MIN),
(INT64_MAX + 1, INT64_MAX),
(float(INT64_MAX + 1), INT64_MAX)],
}
def test_good_values(self):
for ctype, vals in self.GOOD_VALUES.items():
for val in vals:
utils.check_overflow(val, ctype)
def test_bad_values_fatal(self):
for ctype, vals in self.BAD_VALUES.items():
for (val, _) in vals:
with self.assertRaises(OverflowError, msg=ctype):
utils.check_overflow(val, ctype)
def test_bad_values_nonfatal(self):
for ctype, vals in self.BAD_VALUES.items():
for (val, replacement) in vals:
newval = utils.check_overflow(val, ctype, fatal=False)
self.assertEqual(newval, replacement,
"{}: {}".format(ctype, val))
class ReadFileTests(TestCase):
"""Test read_file."""

View File

@ -15,7 +15,12 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Other utilities which don't fit anywhere else."""
"""Other utilities which don't fit anywhere else.
Module attributes:
MAXVAL: A dictionary of C/Qt types (as string) mapped to their maximum
value.
"""
import os
import re
@ -31,6 +36,49 @@ from PyQt5.QtCore import QCoreApplication, QStandardPaths
import qutebrowser
MAXVALS = {
'int': 2 ** 31 - 1,
'int64': 2 ** 63 - 1,
}
MINVALS = {
'int': -(2 ** 31),
'int64': -(2 ** 63),
}
def check_overflow(arg, ctype, fatal=True):
"""Check if the given argument is in bounds for the given type.
Args:
arg: The argument to check
ctype: The C/Qt type to check as a string.
fatal: Wether to raise exceptions (True) or truncate values (False)
Return
The truncated argument if fatal=False
The original argument if it's in bounds.
Raise:
OverflowError if the argument is out of bounds and fatal=True.
"""
# FIXME we somehow should have nicer exceptions...
maxval = MAXVALS[ctype]
minval = MINVALS[ctype]
if arg > maxval:
if fatal:
raise OverflowError(arg)
else:
return maxval
elif arg < minval:
if fatal:
raise OverflowError(arg)
else:
return minval
else:
return arg
def read_file(filename):
"""Get the contents of a file contained with qutebrowser.

View File

@ -246,6 +246,7 @@ class TabbedBrowser(TabWidget):
if count is None:
return self.currentWidget()
elif 1 <= count <= self.count():
cmdutils.check_overflow(count + 1, 'int')
return self.widget(count - 1)
else:
return None
@ -418,6 +419,7 @@ class TabbedBrowser(TabWidget):
countzero=self.count())
except ValueError as e:
raise CommandError(e)
cmdutils.check_overflow(idx + 1, 'int')
if 1 <= idx <= self.count():
self.setCurrentIndex(idx - 1)
else:
@ -448,6 +450,8 @@ class TabbedBrowser(TabWidget):
cur_idx = self.currentIndex()
icon = self.tabIcon(cur_idx)
label = self.tabText(cur_idx)
cmdutils.check_overflow(cur_idx, 'int')
cmdutils.check_overflow(next_idx, 'int')
self.removeTab(cur_idx)
self.insertTab(new_idx, tab, icon, label)
self.setCurrentIndex(new_idx)

View File

@ -24,11 +24,12 @@ from PyQt5.QtCore import pyqtSlot, QRect, QPoint, QCoreApplication
from PyQt5.QtWidgets import QWidget, QVBoxLayout
from PyQt5.QtWebKitWidgets import QWebInspector
import qutebrowser.commands.utils as cmdutils
import qutebrowser.config.config as config
import qutebrowser.utils.misc as utils
from qutebrowser.widgets.statusbar.bar import StatusBar
from qutebrowser.widgets._tabbedbrowser import TabbedBrowser
from qutebrowser.widgets._completion import CompletionView
import qutebrowser.commands.utils as cmdutils
import qutebrowser.config.config as config
class MainWindow(QWidget):
@ -102,7 +103,9 @@ class MainWindow(QWidget):
height = int(confheight)
# hpoint now would be the bottom-left edge of the widget if it was on
# the top of the main window.
topleft = QPoint(0, self.height() - self.status.height() - height)
topleft_y = self.height() - self.status.height() - height
topleft_y = utils.check_overflow(topleft_y, 'int', fatal=False)
topleft = QPoint(0, topleft_y)
bottomright = self.status.geometry().topRight()
if self.inspector.isVisible():
topleft -= QPoint(0, self.inspector.height())