Skip to content

Commit 2c56ddb

Browse files
authored
Merge commit from fork
* fix Unpacker crash after unpack failure. * fixup
1 parent 0f4f350 commit 2c56ddb

4 files changed

Lines changed: 49 additions & 10 deletions

File tree

msgpack/_unpacker.pyx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ cdef extern from "unpack.h":
5151
execute_fn unpack_skip
5252
execute_fn read_array_header
5353
execute_fn read_map_header
54+
5455
void unpack_init(unpack_context* ctx)
5556
object unpack_data(unpack_context* ctx)
5657
void unpack_clear(unpack_context* ctx)
@@ -197,6 +198,7 @@ def unpackb(object packed, *, object object_hook=None, object list_hook=None,
197198
if off < buf_len:
198199
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
199200
return obj
201+
200202
unpack_clear(&ctx)
201203
if ret == 0:
202204
raise ValueError("Unpack failed: incomplete input")
@@ -475,15 +477,17 @@ cdef class Unpacker:
475477
obj = unpack_data(&self.ctx)
476478
unpack_init(&self.ctx)
477479
return obj
478-
elif ret == 0:
480+
if ret == 0:
479481
if self.file_like is not None:
480482
self.read_from_file()
481483
continue
482484
if iter:
483485
raise StopIteration("No more data to unpack.")
484486
else:
485487
raise OutOfData("No more data to unpack.")
486-
elif ret == -2:
488+
489+
unpack_clear(&self.ctx)
490+
if ret == -2:
487491
raise FormatError
488492
elif ret == -3:
489493
raise StackError

msgpack/unpack_template.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,14 @@ static inline PyObject* unpack_data(unpack_context* ctx)
7272

7373
static inline void unpack_clear(unpack_context *ctx)
7474
{
75-
unsigned int i;
76-
for (i = 1; i < ctx->top; i++) {
77-
Py_CLEAR(ctx->stack[i].obj);
75+
for (unsigned int i = 0; i < ctx->top; i++) {
7876
/* map_key holds a live reference only while waiting for the value */
7977
if (ctx->stack[i].ct == CT_MAP_VALUE) {
8078
Py_CLEAR(ctx->stack[i].map_key);
8179
}
80+
Py_CLEAR(ctx->stack[i].obj);
8281
}
83-
Py_CLEAR(ctx->stack[0].obj);
82+
unpack_init(ctx);
8483
}
8584

8685
static inline int unpack_execute(bool construct, unpack_context* ctx, const char* data, Py_ssize_t len, Py_ssize_t* off)
@@ -200,7 +199,7 @@ static inline int unpack_execute(bool construct, unpack_context* ctx, const char
200199
case 0xd5: // fixext 2
201200
case 0xd6: // fixext 4
202201
case 0xd7: // fixext 8
203-
again_fixed_trail_if_zero(ACS_EXT_VALUE,
202+
again_fixed_trail_if_zero(ACS_EXT_VALUE,
204203
(1 << (((unsigned int)*p) & 0x03))+1,
205204
_ext_zero);
206205
case 0xd8: // fixext 16
@@ -344,6 +343,7 @@ static inline int unpack_execute(bool construct, unpack_context* ctx, const char
344343
goto _header_again;
345344
case CT_MAP_VALUE:
346345
if(construct_cb(_map_item)(user, c->count, &c->obj, c->map_key, obj) < 0) { goto _failed; }
346+
c->map_key = NULL;
347347
if(++c->count == c->size) {
348348
obj = c->obj;
349349
if (construct_cb(_map_end)(user, &obj) < 0) { goto _failed; }
@@ -406,10 +406,18 @@ static inline int unpack_execute(bool construct, unpack_context* ctx, const char
406406
#undef start_container
407407

408408
static int unpack_construct(unpack_context *ctx, const char *data, Py_ssize_t len, Py_ssize_t *off) {
409-
return unpack_execute(1, ctx, data, len, off);
409+
int ret = unpack_execute(1, ctx, data, len, off);
410+
if (ret == -1) {
411+
unpack_clear(ctx);
412+
}
413+
return ret;
410414
}
411415
static int unpack_skip(unpack_context *ctx, const char *data, Py_ssize_t len, Py_ssize_t *off) {
412-
return unpack_execute(0, ctx, data, len, off);
416+
int ret = unpack_execute(0, ctx, data, len, off);
417+
if (ret == -1) {
418+
unpack_clear(ctx);
419+
}
420+
return ret;
413421
}
414422

415423
#define unpack_container_header read_array_header

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
Cython==3.2.1
1+
cython==3.2.5
22
setuptools==78.1.1
3+
pytest
34
build

test/test_except.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@ def hook(obj):
3333
object_pairs_hook=hook,
3434
)
3535

36+
up = Unpacker(object_hook=hook)
37+
38+
def up_unpack(x):
39+
up.feed(x)
40+
return up.unpack()
41+
42+
raises(DummyException, up_unpack, packb({}))
43+
raises(DummyException, up_unpack, packb({"fizz": "buzz"}))
44+
raises(DummyException, up_unpack, packb({"fizz": "buzz"}))
45+
raises(DummyException, up_unpack, packb({"fizz": {"buzz": "spam"}}))
46+
raises(
47+
DummyException,
48+
up_unpack,
49+
packb({"fizz": {"buzz": "spam"}}),
50+
)
51+
3652

3753
def test_raise_from_list_hook():
3854
def hook(lst: list) -> list:
@@ -138,3 +154,13 @@ def test_strict_map_key_with_object_pairs_hook():
138154
packed = packb(valid, use_bin_type=True)
139155
result = unpackb(packed, raw=False, strict_map_key=True, object_pairs_hook=list)
140156
assert result == [("key", "value")]
157+
158+
159+
def test_unpacker_should_not_crash_after_exception():
160+
up = Unpacker() # default: strict_map_key=True
161+
up.feed(b"\x83\x73\xc4\x00") # fixmap(3): int key (rejected) + empty bin8
162+
try:
163+
up.unpack() # ValueError: int is not allowed for map key ...
164+
except Exception:
165+
pass
166+
up.skip() # SIGSEGV (resumes from a corrupt parser context)

0 commit comments

Comments
 (0)