The msgpack negative fixint decoder in src/ucl_msgpack.c:1389 uses a wrong bit mask, so every negative fixint value (-1 through -32) comes out wrong. For example, byte 0xff should decode to -1 but gives -31 instead. This affects all versions up to and including 0.9.4. (CWE-681).
Specifically, the bit mask is just incorrect:
// ucl_msgpack.c:1388-1390
case msgpack_negative_fixint:
obj->value.iv = -(*pos & 0x1f);
len = 1;
Per the MessagePack specification, negative fixint stores values in the range [-32, -1] directly as a signed 8-bit integer in bytes 0xe0 through 0xff. The correct decoding is simply (int8_t)*pos.
The current mask 0x1f extracts only the low 5 bits and negates them, producing a completely inverted mapping:
| Byte |
Expected |
Actual |
0xe0 |
-32 |
0 |
0xe1 |
-31 |
-1 |
0xfe |
-2 |
-30 |
0xff |
-1 |
-31 |
Every negative fixint value in every msgpack input parsed by libucl is silently corrupted.
Reproduction
#include <stdio.h>
#include <ucl.h>
int main(void) {
/* fixmap(1){ fixstr(1)"a" : negative_fixint(0xff = should be -1) } */
unsigned char data[] = {0x81, 0xa1, 0x61, 0xff};
struct ucl_parser *p = ucl_parser_new(0);
ucl_parser_add_chunk_full(p, data, 4, 0, UCL_DUPLICATE_APPEND, UCL_PARSE_MSGPACK);
ucl_object_t *obj = ucl_parser_get_object(p);
const ucl_object_t *val = ucl_object_lookup(obj, "a");
printf("0xff: got %lld, expected -1\n", (long long)ucl_object_toint(val));
/* Output: 0xff: got -31, expected -1 */
ucl_object_unref(obj);
ucl_parser_free(p);
return 0;
}
Suggested Fix
Replace the incorrect mask with a direct signed cast:
case msgpack_negative_fixint:
obj->value.iv = (int8_t)*pos;
len = 1;
break;
Happy to send a PR if that helps. :)
The msgpack negative fixint decoder in
src/ucl_msgpack.c:1389uses a wrong bit mask, so every negative fixint value (-1 through -32) comes out wrong. For example, byte0xffshould decode to -1 but gives -31 instead. This affects all versions up to and including 0.9.4. (CWE-681).Specifically, the bit mask is just incorrect:
Per the MessagePack specification, negative fixint stores values in the range [-32, -1] directly as a signed 8-bit integer in bytes
0xe0through0xff. The correct decoding is simply(int8_t)*pos.The current mask
0x1fextracts only the low 5 bits and negates them, producing a completely inverted mapping:0xe00xe10xfe0xffEvery negative fixint value in every msgpack input parsed by libucl is silently corrupted.
Reproduction
Suggested Fix
Replace the incorrect mask with a direct signed cast:
Happy to send a PR if that helps. :)