From 64f835633fe117d7b621f3286169803e57d437ab Mon Sep 17 00:00:00 2001 From: Adrian Zawadzki Date: Fri, 5 Dec 2025 10:20:54 +0100 Subject: [PATCH 1/2] fixed sigsegv for path as input file --- python_tests/test_copy_prefix.py | 15 +++++++++++++++ src/dbzero/bindings/python/PyInternalAPI.cpp | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/python_tests/test_copy_prefix.py b/python_tests/test_copy_prefix.py index 703877e7..0a68742a 100644 --- a/python_tests/test_copy_prefix.py +++ b/python_tests/test_copy_prefix.py @@ -271,3 +271,18 @@ def validate_copy(copy_id, expected_len = None, expected_min_len = None): for i in range(copy_id): last_len = validate_copy(i, expected_min_len = last_len) print(f"--- Copy {i} valid with {last_len} objects") + +def test_copy_prefix_throws_on_path_passed(db0_fixture): + path = "./invalid-dir/nonexistent/-copy/" + # remove path if it exists + if os.path.exists(path): + os.rmdir(path) + + root = MemoTestSingleton([]) + for _ in range(50): + root.value.append(MemoTestClass("a" * 1024)) # 1 KB string + db0.commit() + + with pytest.raises(Exception) as excinfo: + db0.copy_prefix(path) + assert "Output file points to a directory:" in str(excinfo.value) diff --git a/src/dbzero/bindings/python/PyInternalAPI.cpp b/src/dbzero/bindings/python/PyInternalAPI.cpp index 82276b66..67e10097 100644 --- a/src/dbzero/bindings/python/PyInternalAPI.cpp +++ b/src/dbzero/bindings/python/PyInternalAPI.cpp @@ -936,6 +936,10 @@ namespace db0::python PyObject *tryCopyPrefixImpl(BDevStorage &src_storage, const std::string &output_file_name, std::optional page_io_step_size, std::optional meta_io_step_size) { + // make sure output is file doesn't point to a directory + if (output_file_name.back() == '\\' || output_file_name.back() == '/') { + THROWF(db0::IOException) << "Output file points to a directory: " << output_file_name; + } // make sure output file does not exist if (db0::CFile::exists(output_file_name)) { THROWF(db0::IOException) << "Output file already exists: " << output_file_name; From da8275691a1902f4baf3c24490fa252238816b72 Mon Sep 17 00:00:00 2001 From: Adrian Zawadzki Date: Fri, 5 Dec 2025 10:53:38 +0100 Subject: [PATCH 2/2] refactor(copy_prefix): changed code acording to pull request comment changed exception type to OSError changed directory comparation to use std::filesystem::path::preferred_separator --- python_tests/test_copy_prefix.py | 3 +-- src/dbzero/bindings/python/PyInternalAPI.cpp | 11 ++++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python_tests/test_copy_prefix.py b/python_tests/test_copy_prefix.py index 0a68742a..2b37169d 100644 --- a/python_tests/test_copy_prefix.py +++ b/python_tests/test_copy_prefix.py @@ -283,6 +283,5 @@ def test_copy_prefix_throws_on_path_passed(db0_fixture): root.value.append(MemoTestClass("a" * 1024)) # 1 KB string db0.commit() - with pytest.raises(Exception) as excinfo: + with pytest.raises(OSError) as excinfo: db0.copy_prefix(path) - assert "Output file points to a directory:" in str(excinfo.value) diff --git a/src/dbzero/bindings/python/PyInternalAPI.cpp b/src/dbzero/bindings/python/PyInternalAPI.cpp index 67e10097..02c3fe4a 100644 --- a/src/dbzero/bindings/python/PyInternalAPI.cpp +++ b/src/dbzero/bindings/python/PyInternalAPI.cpp @@ -937,12 +937,14 @@ namespace db0::python std::optional page_io_step_size, std::optional meta_io_step_size) { // make sure output is file doesn't point to a directory - if (output_file_name.back() == '\\' || output_file_name.back() == '/') { - THROWF(db0::IOException) << "Output file points to a directory: " << output_file_name; + if (output_file_name.back() == std::filesystem::path::preferred_separator) { + PyErr_Format(PyExc_OSError, "Output file points to a directory: '%s'", output_file_name); + return nullptr; } // make sure output file does not exist if (db0::CFile::exists(output_file_name)) { - THROWF(db0::IOException) << "Output file already exists: " << output_file_name; + PyErr_Format(PyExc_OSError, "Output file already exists: '%s'", output_file_name); + return nullptr; } // use either explicit step size, input step size (if > 1) or default = 4MB @@ -1037,6 +1039,9 @@ namespace db0::python auto result = Py_OWN(tryCopyPrefixImpl(*storage, output_file_name, page_io_step_size, meta_io_step_size)); storage->close(); return result.steal(); + if (!result) { + return nullptr; + } } catch (...) { if (storage) { storage->close();