Skip to content
This repository has been archived by the owner on Apr 23, 2022. It is now read-only.

Fix exception when decoded value is too large by doubling buffer #4

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,7 @@ def test_long_random():
def test_decode_error():
with pytest.raises(xdelta3.XDeltaError) as exc_info:
xdelta3.decode(expected_delta, value_one)
assert exc_info.value.args[0] == 'Error occur executing xdelta3: XD3_INVALID_INPUT'


def test_no_delta():
with pytest.raises(xdelta3.NoDeltaFound) as exc_info:
xdelta3.encode(b'hello', b'goodbye')
assert exc_info.value.args[0] == 'No delta found shorter than the input value'
assert exc_info.value.args[0] == 'Error occurred executing xdelta3: XD3_INVALID_INPUT'


def test_different_compression():
Expand All @@ -57,6 +51,14 @@ def test_version():
xdelta3.print_version()


def test_huge_output():
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes much more sense 😄

b1 = b''
b2 = b't' * 10000 # 10K same characters
d = xdelta3.encode(b1, b2)
assert len(d) < 100 # 0 bytes + very small delta = big output
assert xdelta3.decode(b1, d) == b2


def test_readme():
value_one = b'wonderful string to demonstrate xdelta3, much of these two strings is the same.'
value_two = b'different string to demonstrate xdelta3, much of these two strings is the same.'
Expand Down
29 changes: 17 additions & 12 deletions xdelta3/_xdelta3.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
static PyObject *NoDeltaFound;
static PyObject *XDeltaError;

static PyObject * xdelta3_execute(PyObject *self, PyObject *args)
static PyObject * _xdelta3_execute(PyObject *self, PyObject *args, int buffer_double_cnt)
{
uint8_t *input_bytes = NULL, *source_bytes = NULL, *output_buf = NULL;
int input_len, source_len, flags, action, result;
Expand All @@ -21,13 +21,13 @@ static PyObject * xdelta3_execute(PyObject *self, PyObject *args)

if (action == 0) {
// if the output would be longer than the input itself, there's no point using delta encoding
output_alloc = input_size;
output_alloc = input_size << buffer_double_cnt;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid I don't agree with this. Sorry.

If I'm getting a delta that is bigger than the input I want to get an error which I have to deal with as soon as possible. Silently "failing" to produce a useful delta is not good.

Please remove this.

output_buf = main_malloc(output_alloc);
result = xd3_encode_memory(input_bytes, input_size, source_bytes, source_size,
output_buf, &output_size, output_alloc, flags);
} else {
// output shouldn't be bigger than the original plus the delta, but give a little leeway
output_alloc = input_size + source_size * 11 / 10;
output_alloc = (input_size + source_size * 11 / 10) << buffer_double_cnt;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove comments that no longer make sense.

output_buf = main_malloc(output_alloc);
result = xd3_decode_memory(input_bytes, input_size, source_bytes, source_size,
output_buf, &output_size, output_alloc, flags);
Expand All @@ -39,23 +39,31 @@ static PyObject * xdelta3_execute(PyObject *self, PyObject *args)
return ret;
}

if(result == ENOSPC) {
if (action == 0) {
// all is well, just not efficient delta could be found
PyErr_SetString(NoDeltaFound, "No delta found shorter than the input value");
} else {
if (result == ENOSPC) {
// Leave some leeway before we overflow int. This is a bad way, the better way would be to
// check above when we left shift.
if (buffer_double_cnt < sizeof(int) - 4) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you at least add a comment explaining what this is doing please.

PyErr_SetString(XDeltaError, "Output of decoding delta longer than expected");
} else {
main_free(output_buf);
return _xdelta3_execute(self, args, buffer_double_cnt + 1);
}
} else {
char exc_str[80];
sprintf(exc_str, "Error occur executing xdelta3: %s", xd3_strerror(result));
snprintf(exc_str, sizeof(exc_str) / sizeof(exc_str[0]),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense.

"Error occurred executing xdelta3: %s", xd3_strerror(result));
PyErr_SetString(XDeltaError, exc_str);

}
main_free(output_buf);
return NULL;
}

static PyObject * xdelta3_execute(PyObject *self, PyObject *args)
{
return _xdelta3_execute(self, args, 1);
}

static PyObject * xdelta3_version(PyObject *self, PyObject *args)
{
int result = main_version();
Expand Down Expand Up @@ -88,8 +96,5 @@ PyMODINIT_FUNC PyInit__xdelta3(void) {
Py_INCREF(XDeltaError);
PyModule_AddObject(m, "XDeltaError", XDeltaError);

NoDeltaFound = PyErr_NewException("xdelta3.NoDeltaFound", NULL, NULL);
Py_INCREF(NoDeltaFound);
PyModule_AddObject(m, "NoDeltaFound", NoDeltaFound);
return m;
}
3 changes: 1 addition & 2 deletions xdelta3/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from enum import IntEnum

import _xdelta3
from _xdelta3 import NoDeltaFound, XDeltaError # noqa
from _xdelta3 import XDeltaError # noqa

from .version import VERSION

__all__ = [
'NoDeltaFound',
'XDeltaError',
'Flags',
'encode',
Expand Down