From 9804935437c11f795c0ec8af65623e3d2f3fd0a2 Mon Sep 17 00:00:00 2001 From: Stephan Bosch <stephan.bosch@open-xchange.com> Date: Fri, 30 Aug 2024 01:25:25 +0200 Subject: [PATCH] lib-sieve: plugins: include: Fix crash occurring when more than one optional include is present --- src/lib-sieve/plugins/include/cmd-include.c | 6 +- .../plugins/include/ext-include-binary.c | 92 +++++++++++++------ .../plugins/include/ext-include-binary.h | 11 ++- .../plugins/include/ext-include-common.c | 40 +++++--- .../plugins/include/ext-include-common.h | 4 +- src/lib-sieve/plugins/include/ext-include.c | 2 +- .../include/execute/optional-missing.sieve | 4 + tests/extensions/include/optional.svtest | 17 ++++ 8 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 tests/extensions/include/execute/optional-missing.sieve diff --git a/src/lib-sieve/plugins/include/cmd-include.c b/src/lib-sieve/plugins/include/cmd-include.c index 4d6f7a7bf..38135a773 100644 --- a/src/lib-sieve/plugins/include/cmd-include.c +++ b/src/lib-sieve/plugins/include/cmd-include.c @@ -81,7 +81,7 @@ const struct sieve_operation_def include_operation = { struct cmd_include_context_data { enum ext_include_script_location location; - + const char *script_name; struct sieve_script *script; enum ext_include_flags flags; @@ -328,6 +328,7 @@ cmd_include_validate(struct sieve_validator *valdtr, } ext_include_ast_link_included_script(cmd->ext, cmd->ast_node->ast, script); + ctx_data->script_name = p_strdup(sieve_command_pool(cmd), script_name); ctx_data->script = script; (void)sieve_ast_arguments_detach(arg, 1); @@ -351,6 +352,7 @@ cmd_include_generate(const struct sieve_codegen_env *cgenv, This yields the id of the binary block containing the compiled byte code. */ ret = ext_include_generate_include(cgenv, cmd, ctx_data->location, + ctx_data->script_name, ctx_data->flags, ctx_data->script, &included); if (ret < 0) @@ -385,7 +387,7 @@ static bool opc_include_dump(const struct sieve_dumptime_env *denv, binctx = ext_include_binary_get_context(denv->oprtn->ext, denv->sbin); included = ext_include_binary_script_get_included(binctx, include_id); - if (included == NULL) + if (included == NULL || included->block == NULL) return FALSE; sieve_code_descend(denv); diff --git a/src/lib-sieve/plugins/include/ext-include-binary.c b/src/lib-sieve/plugins/include/ext-include-binary.c index ce72b3425..0d5d76128 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.c +++ b/src/lib-sieve/plugins/include/ext-include-binary.c @@ -59,7 +59,7 @@ struct ext_include_binary_context { struct sieve_binary *binary; struct sieve_binary_block *dependency_block; - HASH_TABLE(struct sieve_script *, + HASH_TABLE(const struct ext_include_script_info *, struct ext_include_script_info *) included_scripts; ARRAY(struct ext_include_script_info *) include_index; @@ -68,6 +68,32 @@ struct ext_include_binary_context { bool outdated:1; }; +static int +ext_include_script_info_cmp(const struct ext_include_script_info *sinfo1, + const struct ext_include_script_info *sinfo2) +{ + if (sinfo1 == sinfo2) + return 0; + if (sinfo1 == NULL || sinfo2 == NULL) + return (sinfo1 == NULL ? -1 : 1); + if (sinfo1->script == NULL || sinfo2->script == NULL) { + if (sinfo1->location != sinfo2->location) + return (sinfo1->location > sinfo2->location ? 1 : -1); + return strcmp(sinfo1->script_name, sinfo2->script_name); + } + + return sieve_script_cmp(sinfo1->script, sinfo2->script); +} + +static unsigned int +ext_include_script_info_hash(const struct ext_include_script_info *sinfo) +{ + if (sinfo == NULL) + return 0; + return ((unsigned int)sinfo->location + + str_hash(sinfo->script_name)); +} + static struct ext_include_binary_context * ext_include_binary_create_context(const struct sieve_extension *this_ext, struct sieve_binary *sbin) @@ -79,7 +105,8 @@ ext_include_binary_create_context(const struct sieve_extension *this_ext, ctx->binary = sbin; hash_table_create(&ctx->included_scripts, pool, 0, - sieve_script_hash, sieve_script_cmp); + ext_include_script_info_hash, + ext_include_script_info_cmp); p_array_init(&ctx->include_index, pool, 128); sieve_binary_extension_set(sbin, this_ext, &include_binary_ext, ctx); @@ -132,24 +159,29 @@ ext_include_binary_init(const struct sieve_extension *this_ext, struct ext_include_script_info * ext_include_binary_script_include(struct ext_include_binary_context *binctx, enum ext_include_script_location location, + const char *script_name, enum ext_include_flags flags, struct sieve_script *script, struct sieve_binary_block *inc_block) { pool_t pool = sieve_binary_pool(binctx->binary); struct ext_include_script_info *incscript; + const struct ext_include_script_info *key; incscript = p_new(pool, struct ext_include_script_info, 1); incscript->id = array_count(&binctx->include_index)+1; incscript->location = location; + incscript->script_name = p_strdup(pool, script_name); incscript->flags = flags; incscript->script = script; incscript->block = inc_block; + key = incscript; /* Unreferenced on binary_free */ - sieve_script_ref(script); + if (script != NULL) + sieve_script_ref(script); - hash_table_insert(binctx->included_scripts, script, incscript); + hash_table_insert(binctx->included_scripts, key, incscript); array_append(&binctx->include_index, &incscript, 1); return incscript; @@ -157,12 +189,18 @@ ext_include_binary_script_include(struct ext_include_binary_context *binctx, struct ext_include_script_info * ext_include_binary_script_get_include_info( - struct ext_include_binary_context *binctx, struct sieve_script *script) + struct ext_include_binary_context *binctx, + enum ext_include_script_location location, const char *script_name) { - struct ext_include_script_info *incscript = - hash_table_lookup(binctx->included_scripts, script); + struct ext_include_script_info key_def; + const struct ext_include_script_info *key; - return incscript; + i_zero(&key_def); + key_def.location = location; + key_def.script_name = script_name; + key = &key_def; + + return hash_table_lookup(binctx->included_scripts, key); } const struct ext_include_script_info * @@ -179,13 +217,6 @@ ext_include_binary_script_get_included( return NULL; } -const struct ext_include_script_info * -ext_include_binary_script_get(struct ext_include_binary_context *binctx, - struct sieve_script *script) -{ - return hash_table_lookup(binctx->included_scripts, script); -} - unsigned int ext_include_binary_script_get_count(struct ext_include_binary_context *binctx) { @@ -239,10 +270,12 @@ ext_include_binary_pre_save(const struct sieve_extension *ext ATTR_UNUSED, sieve_binary_emit_unsigned(sblock, 0); } sieve_binary_emit_byte(sblock, incscript->location); - sieve_binary_emit_cstring( - sblock, sieve_script_name(incscript->script)); + sieve_binary_emit_cstring(sblock, incscript->script_name); sieve_binary_emit_byte(sblock, incscript->flags); - sieve_script_binary_write_metadata(incscript->script, sblock); + if (incscript->block != NULL) { + sieve_script_binary_write_metadata(incscript->script, + sblock); + } } result = ext_include_variables_save(sblock, binctx->global_vars, @@ -386,8 +419,9 @@ ext_include_binary_open(const struct sieve_extension *ext, } /* Can we read script metadata ? */ - ret = sieve_script_binary_read_metadata(script, sblock, - &offset); + ret = (inc_block == NULL ? 1 : + sieve_script_binary_read_metadata(script, sblock, + &offset)); if (ret < 0) { /* Binary is corrupt, recompile */ e_error(svinst->event, "include: " @@ -402,8 +436,10 @@ ext_include_binary_open(const struct sieve_extension *ext, if (ret == 0) binctx->outdated = TRUE; - (void)ext_include_binary_script_include(binctx, location, flags, - script, inc_block); + (void)ext_include_binary_script_include(binctx, location, + str_c(script_name), + flags, script, + inc_block); sieve_script_unref(&script); } @@ -432,13 +468,13 @@ ext_include_binary_free(const struct sieve_extension *ext ATTR_UNUSED, struct ext_include_binary_context *binctx = (struct ext_include_binary_context *)context; struct hash_iterate_context *hctx; - struct sieve_script *script; + const struct ext_include_script_info *key; struct ext_include_script_info *incscript; /* Release references to all included script objects */ hctx = hash_table_iterate_init(binctx->included_scripts); while (hash_table_iterate(hctx, binctx->included_scripts, - &script, &incscript)) + &key, &incscript)) sieve_script_unref(&incscript->script); hash_table_iterate_deinit(&hctx); @@ -459,7 +495,7 @@ bool ext_include_binary_dump(const struct sieve_extension *ext, struct ext_include_binary_context *binctx = ext_include_binary_get_context(ext, sbin); struct hash_iterate_context *hctx; - struct sieve_script *script; + const struct ext_include_script_info *key; struct ext_include_script_info *incscript; if (!ext_include_variables_dump(denv, binctx->global_vars)) @@ -467,13 +503,13 @@ bool ext_include_binary_dump(const struct sieve_extension *ext, hctx = hash_table_iterate_init(binctx->included_scripts); while (hash_table_iterate(hctx, binctx->included_scripts, - &script, &incscript)) { + &key, &incscript)) { if (incscript->block == NULL) { sieve_binary_dump_sectionf( denv, "Included %s script '%s' (MISSING)", ext_include_script_location_name( incscript->location), - sieve_script_name(incscript->script)); + incscript->script_name); } else { unsigned int block_id = sieve_binary_block_get_id(incscript->block); @@ -482,7 +518,7 @@ bool ext_include_binary_dump(const struct sieve_extension *ext, denv, "Included %s script '%s' (block: %d)", ext_include_script_location_name( incscript->location), - sieve_script_name(incscript->script), block_id); + incscript->script_name, block_id); denv->sblock = incscript->block; denv->cdumper = sieve_code_dumper_create(denv); diff --git a/src/lib-sieve/plugins/include/ext-include-binary.h b/src/lib-sieve/plugins/include/ext-include-binary.h index d3c6e62c6..cfc09c88d 100644 --- a/src/lib-sieve/plugins/include/ext-include-binary.h +++ b/src/lib-sieve/plugins/include/ext-include-binary.h @@ -31,9 +31,11 @@ ext_include_binary_get_global_scope(const struct sieve_extension *this_ext, struct ext_include_script_info { unsigned int id; + enum ext_include_script_location location; + const char *script_name; + struct sieve_script *script; enum ext_include_flags flags; - enum ext_include_script_location location; struct sieve_binary_block *block; }; @@ -41,19 +43,18 @@ struct ext_include_script_info { struct ext_include_script_info * ext_include_binary_script_include(struct ext_include_binary_context *binctx, enum ext_include_script_location location, + const char *script_name, enum ext_include_flags flags, struct sieve_script *script, struct sieve_binary_block *inc_block); struct ext_include_script_info * ext_include_binary_script_get_include_info( - struct ext_include_binary_context *binctx, struct sieve_script *script); + struct ext_include_binary_context *binctx, + enum ext_include_script_location location, const char *script_name); const struct ext_include_script_info * ext_include_binary_script_get_included( struct ext_include_binary_context *binctx, unsigned int include_id); -const struct ext_include_script_info * -ext_include_binary_script_get(struct ext_include_binary_context *binctx, - struct sieve_script *script); unsigned int ext_include_binary_script_get_count(struct ext_include_binary_context *binctx); diff --git a/src/lib-sieve/plugins/include/ext-include-common.c b/src/lib-sieve/plugins/include/ext-include-common.c index a8972dfd3..04bc4b7f3 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.c +++ b/src/lib-sieve/plugins/include/ext-include-common.c @@ -32,6 +32,8 @@ struct ext_include_generator_context { unsigned int nesting_depth; + enum ext_include_script_location location; + const char *script_name; struct sieve_script *script; struct ext_include_generator_context *parent; }; @@ -271,6 +273,7 @@ static struct ext_include_generator_context * ext_include_create_generator_context( struct sieve_generator *gentr, struct ext_include_generator_context *parent, + enum ext_include_script_location location, const char *script_name, struct sieve_script *script) { struct ext_include_generator_context *ctx; @@ -278,6 +281,8 @@ ext_include_create_generator_context( pool_t pool = sieve_generator_pool(gentr); ctx = p_new(pool, struct ext_include_generator_context, 1); ctx->parent = parent; + ctx->location = location; + ctx->script_name = p_strdup(pool, script_name); ctx->script = script; if (parent == NULL) ctx->nesting_depth = 0; @@ -299,11 +304,14 @@ static inline void ext_include_initialize_generator_context( const struct sieve_extension *this_ext, struct sieve_generator *gentr, struct ext_include_generator_context *parent, + enum ext_include_script_location location, const char *script_name, struct sieve_script *script) { sieve_generator_extension_set_context( gentr, this_ext, - ext_include_create_generator_context(gentr, parent, script)); + ext_include_create_generator_context(gentr, parent, + location, script_name, + script)); } void ext_include_register_generator_context( @@ -315,8 +323,10 @@ void ext_include_register_generator_context( /* Initialize generator context if necessary */ if (ctx == NULL) { + i_assert(cgenv->script != NULL); ctx = ext_include_create_generator_context( - cgenv->gentr, NULL, cgenv->script); + cgenv->gentr, NULL, EXT_INCLUDE_LOCATION_PERSONAL, + sieve_script_name(cgenv->script), cgenv->script); sieve_generator_extension_set_context( cgenv->gentr, this_ext, (void *)ctx); @@ -458,8 +468,8 @@ ext_include_interpreter_get_global_variables( int ext_include_generate_include( const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, enum ext_include_flags flags, - struct sieve_script *script, + enum ext_include_script_location location, const char *script_name, + enum ext_include_flags flags, struct sieve_script *script, const struct ext_include_script_info **included_r) { const struct sieve_extension *this_ext = cmd->ext; @@ -499,7 +509,10 @@ int ext_include_generate_include( if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) { pctx = ctx; while (pctx != NULL) { - if (sieve_script_equals(pctx->script, script)) { + if (pctx->location == location && + strcmp(pctx->script_name, script_name) == 0 && + (pctx->script == NULL || script == NULL || + sieve_script_equals(pctx->script, script))) { /* Just drop circular include when uploading inactive script; not an error */ @@ -524,7 +537,8 @@ int ext_include_generate_include( binctx = ext_include_binary_init(this_ext, sbin, cgenv->ast); /* Is the script already compiled into the current binary? */ - included = ext_include_binary_script_get_include_info(binctx, script); + included = ext_include_binary_script_get_include_info( + binctx, location, script_name); if (included != NULL) { /* Yes, only update flags */ if ((flags & EXT_INCLUDE_FLAG_OPTIONAL) == 0) @@ -532,7 +546,6 @@ int ext_include_generate_include( if ((flags & EXT_INCLUDE_FLAG_ONCE) == 0) included->flags &= ENUM_NEGATE(EXT_INCLUDE_FLAG_ONCE); } else { - const char *script_name = sieve_script_name(script); enum sieve_compile_flags cpflags = cgenv->flags; /* No, include new script */ @@ -550,13 +563,14 @@ int ext_include_generate_include( /* Allocate a new block in the binary and mark the script as included. */ - if (!sieve_script_is_open(script)) { + if (script == NULL || !sieve_script_is_open(script)) { /* Just making an empty entry to mark a missing script */ i_assert((flags & EXT_INCLUDE_FLAG_MISSING_AT_UPLOAD) != 0 || (flags & EXT_INCLUDE_FLAG_OPTIONAL) != 0); included = ext_include_binary_script_include( - binctx, location, flags, script, NULL); + binctx, location, script_name, flags, NULL, + NULL); result = 0; } else { struct sieve_binary_block *inc_block = @@ -564,7 +578,8 @@ int ext_include_generate_include( /* Real include */ included = ext_include_binary_script_include( - binctx, location, flags, script, inc_block); + binctx, location, script_name, flags, script, + inc_block); /* Parse */ if ((ast = sieve_parse(script, ehandler, @@ -604,7 +619,8 @@ int ext_include_generate_include( */ subgentr = sieve_generator_create(ast, ehandler, cpflags); ext_include_initialize_generator_context( - cmd->ext, subgentr, ctx, script); + cmd->ext, subgentr, ctx, location, script_name, + script); if (sieve_generator_run(subgentr, &inc_block) == NULL) { sieve_command_generate_error( @@ -683,7 +699,7 @@ int ext_include_execute_include(const struct sieve_runtime_env *renv, /* Check for invalid include id (== corrupt binary) */ included = ext_include_binary_script_get_included(binctx, include_id); - if (included == NULL) { + if (included == NULL || included->block == NULL) { sieve_runtime_trace_error( renv, "include: include id %d is invalid", include_id); return SIEVE_EXEC_BIN_CORRUPT; diff --git a/src/lib-sieve/plugins/include/ext-include-common.h b/src/lib-sieve/plugins/include/ext-include-common.h index ca586b7e6..b1a793f15 100644 --- a/src/lib-sieve/plugins/include/ext-include-common.h +++ b/src/lib-sieve/plugins/include/ext-include-common.h @@ -143,8 +143,8 @@ void ext_include_register_generator_context( int ext_include_generate_include( const struct sieve_codegen_env *cgenv, struct sieve_command *cmd, - enum ext_include_script_location location, enum ext_include_flags flags, - struct sieve_script *script, + enum ext_include_script_location location, const char *script_name, + enum ext_include_flags flags, struct sieve_script *script, const struct ext_include_script_info **included_r); /* Interpreter context */ diff --git a/src/lib-sieve/plugins/include/ext-include.c b/src/lib-sieve/plugins/include/ext-include.c index f85bd2489..a8423beb5 100644 --- a/src/lib-sieve/plugins/include/ext-include.c +++ b/src/lib-sieve/plugins/include/ext-include.c @@ -67,7 +67,7 @@ ext_include_binary_load(const struct sieve_extension *ext, const struct sieve_extension_def include_extension = { .name = "include", - .version = 1, + .version = 2, .load = ext_include_load, .unload = ext_include_unload, diff --git a/tests/extensions/include/execute/optional-missing.sieve b/tests/extensions/include/execute/optional-missing.sieve new file mode 100644 index 000000000..e80569a8d --- /dev/null +++ b/tests/extensions/include/execute/optional-missing.sieve @@ -0,0 +1,4 @@ +require "include"; + +include :personal :optional "missing_a"; +include :personal :optional "missing_aa"; diff --git a/tests/extensions/include/optional.svtest b/tests/extensions/include/optional.svtest index 345f8309b..0b19c9d9d 100644 --- a/tests/extensions/include/optional.svtest +++ b/tests/extensions/include/optional.svtest @@ -38,3 +38,20 @@ test "Included Optional - Binary" { test_fail "unexpected result value: ${result}"; } } + +test "Included Optional - Binary Missing" { + if not test_script_compile "execute/optional-missing.sieve" { + test_fail "failed to compile sieve script"; + } + + test_binary_save "optional-missing"; + test_binary_load "optional-missing"; + + if not test_script_run { + test_fail "failed to execute sieve script"; + } + + if not string "${result}" " ONE TWO" { + test_fail "unexpected result value: ${result}"; + } +} -- GitLab