]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Fix] Port security fixes from libucl upstream
authorVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 13 Apr 2026 08:22:01 +0000 (09:22 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Mon, 13 Apr 2026 08:22:01 +0000 (09:22 +0100)
- 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

contrib/libucl/ucl_internal.h
contrib/libucl/ucl_msgpack.c
contrib/libucl/ucl_parser.c
contrib/libucl/ucl_schema.c
contrib/libucl/ucl_sexp.c

index a472fed32825260ff2be5b538654a5cdfffa36d1..7bf7231a084e3d15334b86e9108fad492e4d976c 100644 (file)
@@ -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
 
index 9cb0b6f78b02634f711a3e965f688775526ae967..ee2581698580eba1dc8c8e8b89ba2a5b1ca2ae08 100644 (file)
@@ -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:
index 9bb786831d0fcddd4266e59d9b8465682315209d..5f30877073e96c1d79c44586cbb09ad631957059 100644 (file)
@@ -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;
        }
 
index 68f01187e375743f965f36eac8472d2c94f56e41..3f834c095bb6e972ef69df9eacd1115e2ec61300 100644 (file)
@@ -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;
+                       }
                }
        }
 
index b1e38e5176558e457ce624b95d0feabf1c70bc3c..41e6b8a33813b5a5a8d17239fb7427028810803f 100644 (file)
@@ -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;