From b111d50347076336b3e655178d967f8e5c8c9913 Mon Sep 17 00:00:00 2001 From: Prashanth Pai Date: Mon, 30 May 2016 15:08:48 +0530 Subject: Add validation decorators As glfs and glfd are pointers to memory locations, passing invalid values of glfs and glfd to the libgfapi C library can result in segfault. This patch introduces decorators that validate glfs and glfd before calling correspoding C APIs. Change-Id: I4e86bd8e436e23cd41f75f428d246939c820bb9c Signed-off-by: Prashanth Pai --- test/functional/libgfapi-python-tests.py | 23 +++++++++++ test/unit/gluster/test_gfapi.py | 31 ++++++++++++++ test/unit/gluster/test_utils.py | 70 ++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 test/unit/gluster/test_utils.py (limited to 'test') diff --git a/test/functional/libgfapi-python-tests.py b/test/functional/libgfapi-python-tests.py index c29f400..4e339e0 100644 --- a/test/functional/libgfapi-python-tests.py +++ b/test/functional/libgfapi-python-tests.py @@ -127,6 +127,29 @@ class FileOpsTest(unittest.TestCase): self.assertRaises(OSError, self.vol.open, "file", 12345) + def test_double_close(self): + name = uuid4().hex + f = self.vol.fopen(name, 'w') + f.close() + for i in range(2): + try: + f.close() + except OSError as err: + self.assertEqual(err.errno, errno.EBADF) + else: + self.fail("Expecting OSError") + + def test_glfd_decorators_IO_on_invalid_glfd(self): + name = uuid4().hex + with self.vol.fopen(name, 'w') as f: + f.write("Valar Morghulis") + try: + s = f.read() + except OSError as err: + self.assertEqual(err.errno, errno.EBADF) + else: + self.fail("Expecting OSError") + def test_fopen_err(self): # mode not string self.assertRaises(TypeError, self.vol.fopen, "file", os.O_WRONLY) diff --git a/test/unit/gluster/test_gfapi.py b/test/unit/gluster/test_gfapi.py index 3934a6f..86fa621 100644 --- a/test/unit/gluster/test_gfapi.py +++ b/test/unit/gluster/test_gfapi.py @@ -11,6 +11,7 @@ import unittest import gluster +import inspect import os import stat import time @@ -70,6 +71,36 @@ class TestFile(unittest.TestCase): def tearDown(self): gluster.gfapi.api.glfs_close = self._saved_glfs_close + def test_validate_init(self): + self.assertRaises(ValueError, File, None) + self.assertRaises(ValueError, File, "not_int") + + try: + with File(None) as f: + pass + except ValueError: + pass + else: + self.fail("Expecting ValueError") + + try: + with File("not_int") as f: + pass + except ValueError: + pass + else: + self.fail("Expecting ValueError") + + def test_validate_glfd_decorator_applied(self): + for method_name, method_instance in \ + inspect.getmembers(File, predicate=inspect.ismethod): + if not method_name.startswith('_'): + try: + wrapper_attribute = method_instance.__wrapped__.__name__ + self.assertEqual(wrapper_attribute, method_name) + except AttributeError: + self.fail("Method File.%s isn't decorated" % (method_name)) + def test_fchmod_success(self): mock_glfs_fchmod = Mock() mock_glfs_fchmod.return_value = 0 diff --git a/test/unit/gluster/test_utils.py b/test/unit/gluster/test_utils.py new file mode 100644 index 0000000..eb7a15f --- /dev/null +++ b/test/unit/gluster/test_utils.py @@ -0,0 +1,70 @@ +# Copyright (c) 2016 Red Hat, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import unittest + +from gluster import utils +from gluster.exceptions import VolumeNotMounted + + +class TestUtils(unittest.TestCase): + + def test_validate_mount(self): + + class _FakeVol(object): + + def __init__(self): + self.fs = None + self._mounted = None + self.volname = "vol1" + + @utils.validate_mount + def test_method(self): + return + + v = _FakeVol() + try: + v.test_method() + except VolumeNotMounted as err: + self.assertEqual(str(err), 'Volume "vol1" not mounted.') + else: + self.fail("Expected VolumeNotMounted exception.") + + v.fs = 12345 + v._mounted = True + # Shouldn't raise exception. + v.test_method() + + def test_validate_glfd(self): + + class _FakeFile(object): + + def __init__(self, fd, path=None): + self.fd = fd + + @utils.validate_glfd + def test_method(self): + return + + def close(self): + self.fd = None + + f = _FakeFile(1234) + f.close() + self.assertTrue(f.fd is None) + self.assertRaises(OSError, f.test_method) + + f.fd = 1234 + # Shouldn't raise exception. + f.test_method() -- cgit