From: Vsevolod Stakhov Date: Mon, 13 Apr 2026 08:22:01 +0000 (+0100) Subject: [Fix] Port security fixes from libucl upstream X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=79bf94ea37a1189a2c46a77db8896e12f4f7bf27;p=thirdparty%2Frspamd.git [Fix] Port security fixes from libucl upstream - msgpack: fix negative fixint encoding/decoding (wrong bitmask) - msgpack: replace unaligned pointer casts with memcpy - msgpack: validate key length before truncating to ssize_t - msgpack/csexp: add UCL_MAX_NESTING (1024) depth limit - parser: add bounds checks in parse_key, parse_value, parse_macro - schema: add NULL guard for err pointer in anyOf/not validators --- diff --git a/contrib/libucl/ucl_internal.h b/contrib/libucl/ucl_internal.h index a472fed328..7bf7231a08 100644 --- a/contrib/libucl/ucl_internal.h +++ b/contrib/libucl/ucl_internal.h @@ -140,6 +140,7 @@ typedef SSIZE_T ssize_t; */ #define UCL_MAX_RECURSION 16 +#define UCL_MAX_NESTING 1024 #define UCL_TRASH_KEY 0 #define UCL_TRASH_VALUE 1 diff --git a/contrib/libucl/ucl_msgpack.c b/contrib/libucl/ucl_msgpack.c index 9cb0b6f78b..ee25816985 100644 --- a/contrib/libucl/ucl_msgpack.c +++ b/contrib/libucl/ucl_msgpack.c @@ -145,9 +145,9 @@ ucl_emitter_print_int_msgpack (struct ucl_emitter_context *ctx, int64_t val) /* Bithack abs */ uval = ((val ^ (val >> 63)) - (val >> 63)); - if (val > -(1 << 5)) { + if (val >= -32) { len = 1; - buf[0] = (mask_negative | uval) & 0xff; + buf[0] = (uint8_t) (int8_t) val; } else if (uval <= INT8_MAX) { uint8_t v = (uint8_t)val; @@ -770,6 +770,18 @@ ucl_msgpack_get_container (struct ucl_parser *parser, /* * Insert new container to the stack */ + unsigned int depth = 0; + struct ucl_stack *sp; + for (sp = parser->stack; sp != NULL; sp = sp->next) { + depth++; + } + if (depth >= UCL_MAX_NESTING) { + ucl_create_err(&parser->err, + "msgpack containers are nested too deep (over %d)", + UCL_MAX_NESTING); + return NULL; + } + if (parser->stack == NULL) { parser->stack = calloc (1, sizeof (struct ucl_stack)); @@ -1035,15 +1047,24 @@ ucl_msgpack_consume (struct ucl_parser *parser) case 1: len = *p; break; - case 2: - len = FROM_BE16 (*(uint16_t *)p); + case 2: { + uint16_t v; + memcpy(&v, p, sizeof(v)); + len = FROM_BE16(v); break; - case 4: - len = FROM_BE32 (*(uint32_t *)p); + } + case 4: { + uint32_t v; + memcpy(&v, p, sizeof(v)); + len = FROM_BE32(v); break; - case 8: - len = FROM_BE64 (*(uint64_t *)p); + } + case 8: { + uint64_t v; + memcpy(&v, p, sizeof(v)); + len = FROM_BE64(v); break; + } default: ucl_create_err (&parser->err, "invalid length of the length field: %u", (unsigned)obj_parser->len); @@ -1179,13 +1200,13 @@ ucl_msgpack_consume (struct ucl_parser *parser) } key = p; - keylen = len; - if (keylen > remain || keylen == 0) { + if (len == 0 || (int64_t) len > remain) { ucl_create_err (&parser->err, "too long or empty key"); return false; } + keylen = len; p += len; remain -= len; @@ -1461,7 +1482,7 @@ ucl_msgpack_parse_int (struct ucl_parser *parser, len = 1; break; case msgpack_negative_fixint: - obj->value.iv = - (*pos & 0x1f); + obj->value.iv = (int8_t) *pos; len = 1; break; case msgpack_uint8: diff --git a/contrib/libucl/ucl_parser.c b/contrib/libucl/ucl_parser.c index 9bb786831d..5f30877073 100644 --- a/contrib/libucl/ucl_parser.c +++ b/contrib/libucl/ucl_parser.c @@ -1386,6 +1386,14 @@ ucl_parse_key(struct ucl_parser *parser, struct ucl_chunk *chunk, p = chunk->pos; + if (p >= chunk->end) { + ucl_set_err(parser, UCL_ESYNTAX, + "unexpected end of input while parsing key", + &parser->err); + *end_of_object = true; + return true; + } + if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) { ucl_chunk_skipc(chunk, p); parser->prev_state = parser->state; @@ -1760,8 +1768,9 @@ ucl_parse_value(struct ucl_parser *parser, struct ucl_chunk *chunk) p = chunk->pos; /* Skip any spaces and comments */ - if (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || - (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1]))) { + if (p < chunk->end && + (ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE) || + (chunk->remain >= 2 && ucl_lex_is_comment(p[0], p[1])))) { while (p < chunk->end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) { ucl_chunk_skipc(chunk, p); } @@ -2260,6 +2269,13 @@ ucl_parse_macro_value(struct ucl_parser *parser, p = chunk->pos; + if (p >= chunk->end) { + ucl_set_err(parser, UCL_ESYNTAX, + "unexpected end of input while parsing macro value", + &parser->err); + return false; + } + switch (*p) { case '"': /* We have macro value encoded in quotes */ @@ -2292,6 +2308,12 @@ ucl_parse_macro_value(struct ucl_parser *parser, } ucl_chunk_skipc(chunk, p); } + if (p >= chunk->end) { + ucl_set_err(parser, UCL_ESYNTAX, + "unterminated macro value, missing '}'", + &parser->err); + return false; + } *macro_start = c; *macro_len = p - c; ucl_chunk_skipc(chunk, p); @@ -2344,7 +2366,7 @@ ucl_parse_macro_arguments(struct ucl_parser *parser, saved.remain = chunk->remain; p = chunk->pos; - if (*p != '(' || chunk->remain < 2) { + if (chunk->remain < 2 || *p != '(') { return NULL; } diff --git a/contrib/libucl/ucl_schema.c b/contrib/libucl/ucl_schema.c index 68f01187e3..3f834c095b 100644 --- a/contrib/libucl/ucl_schema.c +++ b/contrib/libucl/ucl_schema.c @@ -990,7 +990,9 @@ ucl_schema_validate (const ucl_object_t *schema, } else { /* Reset error */ - err->code = UCL_SCHEMA_OK; + if (err != NULL) { + err->code = UCL_SCHEMA_OK; + } } } @@ -1019,7 +1021,9 @@ ucl_schema_validate (const ucl_object_t *schema, } else { /* Reset error */ - err->code = UCL_SCHEMA_OK; + if (err != NULL) { + err->code = UCL_SCHEMA_OK; + } } } diff --git a/contrib/libucl/ucl_sexp.c b/contrib/libucl/ucl_sexp.c index b1e38e5176..41e6b8a338 100644 --- a/contrib/libucl/ucl_sexp.c +++ b/contrib/libucl/ucl_sexp.c @@ -62,6 +62,7 @@ bool ucl_parse_csexp(struct ucl_parser *parser) ucl_object_t *obj; struct ucl_stack *st; uint64_t len = 0; + unsigned int depth = 0; enum { start_parse, read_obrace, @@ -95,6 +96,14 @@ bool ucl_parse_csexp(struct ucl_parser *parser) break; case read_obrace: + if (depth >= UCL_MAX_NESTING) { + ucl_create_err(&parser->err, + "csexp nesting too deep (over %d)", + UCL_MAX_NESTING); + state = parse_err; + continue; + } + st = calloc(1, sizeof(*st)); if (st == NULL) { @@ -125,6 +134,7 @@ bool ucl_parse_csexp(struct ucl_parser *parser) LL_PREPEND(parser->stack, st); } + depth++; p++; NEXT_STATE; @@ -217,6 +227,9 @@ bool ucl_parse_csexp(struct ucl_parser *parser) free(st); st = NULL; + if (depth > 0) { + depth--; + } p++; NEXT_STATE; break;