Commit message: None
CVE: CVE-2018-16541
CWE: CWE-416
Vulnerability description: In Artifex Ghostscript before 9.24, attackers able to supply crafted PostScript files could use incorrect free logic in pagedevice replacement to crash the interpreter.

Project: ghostscript
File: imain.c

extend_path_list_container (gs_main_instance * minst, gs_file_path * pfp)
{
    uint len = r_size(&minst->lib_path.container);
    ref *paths, *opaths = minst->lib_path.container.value.refs;

    /* Add 5 entries at a time to reduce VM thrashing */
    paths = (ref *) gs_alloc_byte_array(minst->heap, len + LIB_PATH_EXTEND, sizeof(ref),
                                        "extend_path_list_container array");

    if (paths == 0) {
        return_error(gs_error_VMerror);
    }
    make_array(&minst->lib_path.container, avm_foreign, len + LIB_PATH_EXTEND, paths);
    make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
               minst->lib_path.container.value.refs);

    memcpy(paths, opaths, len * sizeof(ref));
    r_set_size(&minst->lib_path.list, len);

    gs_free_object (minst->heap, opaths, "extend_path_list_container");
    return(0);
}

Project: ghostscript
File: imain.c

file_path_add(gs_main_instance * minst, gs_file_path * pfp, const char *dirs)
{
    uint len = r_size(&pfp->list);
    const char *dpath = dirs;
    int code;

    if (dirs == 0)
        return 0;
    for (;;) {                  /* Find the end of the next directory name. */
        const char *npath = dpath;

        while (*npath != 0 && *npath != gp_file_name_list_separator)
            npath++;
        if (npath > dpath) {
            if (len == r_size(&pfp->container)) {
                code = extend_path_list_container(minst, pfp);
                if (code < 0) {
                    emprintf(minst->heap, "\nAdding path to search paths failed.\n");
                    return(code);
                }
            }
            make_const_string(&pfp->container.value.refs[len],
                              avm_foreign | a_readonly,
                              npath - dpath, (const byte *)dpath);
            ++len;
        }
        if (!*npath)
            break;
        dpath = npath + 1;
    }
    r_set_size(&pfp->list, len);
    return 0;
}

Project: ghostscript
File: imain.c

get_global_op_array(const gs_memory_t *mem)
{
    gs_main_instance *minst = get_minst_from_memory(mem);
    return &minst->i_ctx_p->op_array_table_global;
}

Project: ghostscript
File: imain.c

get_local_op_array(const gs_memory_t *mem)
{
    gs_main_instance *minst = get_minst_from_memory(mem);
    return &minst->i_ctx_p->op_array_table_local;
}

Project: ghostscript
File: imain.c

get_minst_from_memory(const gs_memory_t *mem)
{
    return (gs_main_instance*)mem->gs_lib_ctx->top_of_system;
}

Project: ghostscript
File: imain.c

gs_abort(const gs_memory_t *mem)
{
    /* In previous versions, we tried to do a cleanup (using gs_to_exit),
     * but more often than not, that will trip another abort and create
     * an infinite recursion. So just abort without trying to cleanup.
     */
    gp_do_exit(1);
}

Project: ghostscript
File: imain.c

gs_finit_push_systemdict(i_ctx_t *i_ctx_p)
{
    if (i_ctx_p == NULL)
        return;
    if (dsp == dstop ) {
        if (ref_stack_extend(&d_stack, 1) < 0) {
            /* zend() cannot fail */
            (void)zend(i_ctx_p);
        }
    }
    dsp++;
    ref_assign(dsp, systemdict);
}

Project: ghostscript
File: imain.c

gs_main_alloc_instance(gs_memory_t *mem)
{
    gs_main_instance *minst;
    if (mem == NULL)
        return NULL;

    minst = (gs_main_instance *)gs_alloc_bytes_immovable(mem,
                                                         sizeof(gs_main_instance),
                                                         "init_main_instance");
    if (minst == NULL)
        return NULL;
    memcpy(minst, &gs_main_instance_init_values, sizeof(gs_main_instance_init_values));
    minst->heap = mem;
    mem->gs_lib_ctx->top_of_system = minst;
    return minst;
}

Project: ghostscript
File: imain.c

gs_main_init0(gs_main_instance * minst, FILE * in, FILE * out, FILE * err,
              int max_lib_paths)
{
    ref *array;

    /* Do platform-dependent initialization. */
    /* We have to do this as the very first thing, */
    /* because it detects attempts to run 80N86 executables (N>0) */
    /* on incompatible processors. */
    gp_init();

    /* Initialize the imager. */

    /* Reset debugging flags */
#ifdef PACIFY_VALGRIND
    VALGRIND_HG_DISABLE_CHECKING(gs_debug, 128);
#endif
    memset(gs_debug, 0, 128);
    gs_log_errors = 0;  /* gs_debug['#'] = 0 */

    gp_get_realtime(minst->base_time);

    /* Initialize the file search paths. */
    array = (ref *) gs_alloc_byte_array(minst->heap, max_lib_paths, sizeof(ref),
                                        "lib_path array");
    if (array == 0) {
        gs_lib_finit(1, gs_error_VMerror, minst->heap);
        return_error(gs_error_VMerror);
    }
    make_array(&minst->lib_path.container, avm_foreign, max_lib_paths,
               array);
    make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
               minst->lib_path.container.value.refs);
    minst->lib_path.env = 0;
    minst->lib_path.final = 0;
    minst->lib_path.count = 0;
    minst->user_errors = 1;
    minst->init_done = 0;
    return 0;
}

Project: ghostscript
File: imain.c

gs_main_init1(gs_main_instance * minst)
{
    gs_dual_memory_t idmem;
    name_table *nt = NULL;
    int code = 0;

    if (minst->init_done < 1) {
        code = ialloc_init(&idmem, minst->heap,
                        minst->memory_clump_size, gs_have_level2());

        if (code < 0)
            return code;
        code = gs_lib_init1((gs_memory_t *)idmem.space_system);
        if (code < 0)
            goto fail;
        alloc_save_init(&idmem);
        {
            gs_memory_t *mem = (gs_memory_t *)idmem.space_system;
            nt = names_init(minst->name_table_size, idmem.space_system);

            if (nt == 0) {
                code = gs_note_error(gs_error_VMerror);
                goto fail;
            }
            mem->gs_lib_ctx->gs_name_table = nt;
            code = gs_register_struct_root(mem, mem->gs_lib_ctx->name_table_root,
                                           (void **)&mem->gs_lib_ctx->gs_name_table,
                                           "the_gs_name_table");
            if (code < 0)
                goto fail;
            mem->gs_lib_ctx->client_check_file_permission = z_check_file_permissions;
        }
        code = obj_init(&minst->i_ctx_p, &idmem);  /* requires name_init */
        if (code < 0)
            goto fail;
        minst->init_done = 1;
        code = i_plugin_init(minst->i_ctx_p);
        if (code < 0)
            goto fail;
        code = i_iodev_init(&idmem);
        if (code < 0)
            goto fail;
    }
    return 0;

fail:
    names_free(nt);
    if (minst->i_ctx_p == NULL)
        ialloc_finit(&idmem);
    return code;
}

Project: ghostscript
File: imain.c

gs_main_init2(gs_main_instance * minst)
{
    i_ctx_t *i_ctx_p;
    int code = gs_main_init1(minst);
    int initial_init_level = minst->init_done;

    if (code < 0)
        return code;

    code = gs_main_init2aux(minst);
    if (code < 0)
       return code;
    i_ctx_p = minst->i_ctx_p; /* display_set_callback or run_string may change it */

    /* Now process the initial saved-pages=... argument, if any as well as saved-pages-test */
    if (initial_init_level < 2) {
       gx_device *pdev = gs_currentdevice(minst->i_ctx_p->pgs);	/* get the current device */
       gx_device_printer *ppdev = (gx_device_printer *)pdev;

        if (minst->saved_pages_test_mode) {
            if ((dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) <= 0)) {
                /* no warning or error if saved-pages-test mode is used, just disable it */
                minst->saved_pages_test_mode = false;  /* device doesn't support it */
            } else {
                if ((code = gx_saved_pages_param_process(ppdev, (byte *)"begin", 5)) < 0)
                    return code;
                if (code > 0)
                    if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
                        return code;
            }
        } else if (minst->saved_pages_initial_arg != NULL) {
            if (dev_proc(pdev, dev_spec_op)(pdev, gxdso_supports_saved_pages, NULL, 0) <= 0) {
                outprintf(minst->heap,
                          "   --saved-pages not supported by the '%s' device.\n",
                          pdev->dname);
                return gs_error_Fatal;
            }
            code = gx_saved_pages_param_process(ppdev, (byte *)minst->saved_pages_initial_arg,
                                                strlen(minst->saved_pages_initial_arg));
            if (code > 0)
                if ((code = gs_erasepage(minst->i_ctx_p->pgs)) < 0)
                    return code;
        }
    }

    if (gs_debug_c(':'))
        print_resource_usage(minst, &gs_imemory, "Start");
    gp_readline_init(&minst->readline_data, imemory_system);
    return 0;
}

Project: ghostscript
File: imain.c

gs_main_interpret(gs_main_instance *minst, ref * pref, int user_errors,
        int *pexit_code, ref * perror_object)
{
    int code;

    /* set interpreter pointer to lib_path */
    minst->i_ctx_p->lib_path = &minst->lib_path;

    code = gs_interpret(&minst->i_ctx_p, pref,
                user_errors, pexit_code, perror_object);
    return code;
}

Project: ghostscript
File: imain.c

gs_main_run_file(gs_main_instance * minst, const char *file_name, int user_errors, int *pexit_code, ref * perror_object)
{
    ref initial_file;
    int code = gs_main_run_file_open(minst, file_name, &initial_file);

    if (code < 0)
        return code;
    return gs_main_interpret(minst, &initial_file, user_errors,
                        pexit_code, perror_object);
}

Project: ghostscript
File: imain.c

gs_main_run_file_open(gs_main_instance * minst, const char *file_name, ref * pfref)
{
    gs_main_set_lib_paths(minst);
    if (gs_main_lib_open(minst, file_name, pfref) < 0) {
        emprintf1(minst->heap,
                  "Can't find initialization file %s.\n",
                  file_name);
        return_error(gs_error_Fatal);
    }
    r_set_attrs(pfref, a_execute + a_executable);
    return 0;
}

Project: ghostscript
File: imain.c

gs_main_run_string(gs_main_instance * minst, const char *str, int user_errors,
                   int *pexit_code, ref * perror_object)
{
    return gs_main_run_string_with_length(minst, str, (uint) strlen(str),
                                          user_errors,
                                          pexit_code, perror_object);
}

Project: ghostscript
File: imain.c

gs_main_run_string_continue(gs_main_instance * minst, const char *str,
         uint length, int user_errors, int *pexit_code, ref * perror_object)
{
    ref rstr;

    if (length == 0)
        return 0;               /* empty string signals EOF */
    make_const_string(&rstr, avm_foreign | a_readonly, length,
                      (const byte *)str);
    return gs_main_interpret(minst, &rstr, user_errors, pexit_code,
                        perror_object);
}

Project: ghostscript
File: imain.c

gs_main_run_string_end(gs_main_instance * minst, int user_errors,
                       int *pexit_code, ref * perror_object)
{
    ref rstr;

    make_empty_const_string(&rstr, avm_foreign | a_readonly);
    return gs_main_interpret(minst, &rstr, user_errors, pexit_code,
                        perror_object);
}

Project: ghostscript
File: imain.c

gs_main_run_string_with_length(gs_main_instance * minst, const char *str,
         uint length, int user_errors, int *pexit_code, ref * perror_object)
{
    int code;

    code = gs_main_run_string_begin(minst, user_errors,
                                    pexit_code, perror_object);
    if (code < 0)
        return code;
    code = gs_main_run_string_continue(minst, str, length, user_errors,
                                       pexit_code, perror_object);
    if (code != gs_error_NeedInput)
        return code;
    return gs_main_run_string_end(minst, user_errors,
                                  pexit_code, perror_object);
}

Project: ghostscript
File: imain.c

gs_main_set_lib_paths(gs_main_instance * minst)
{
    ref *paths = minst->lib_path.container.value.refs;
    int first_is_here =
        (r_size(&minst->lib_path.list) != 0 &&
         paths[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0);
    int code = 0;
    int count = minst->lib_path.count;
    int i, have_rom_device = 0;

    if (minst->search_here_first) {
        if (!(first_is_here ||
              (r_size(&minst->lib_path.list) != 0 &&
               !bytes_compare((const byte *)gp_current_directory_name,
                              strlen(gp_current_directory_name),
                              paths[0].value.bytes,
                              r_size(&paths[0]))))
            ) {
            memmove(paths + 1, paths, count * sizeof(*paths));
            make_const_string(paths, avm_foreign | a_readonly,
                              strlen(gp_current_directory_name),
                              (const byte *)gp_current_directory_name);
        }
    } else {
        if (first_is_here)
            memmove(paths, paths + 1, count * sizeof(*paths));
    }
    r_set_size(&minst->lib_path.list,
               count + (minst->search_here_first ? 1 : 0));
    if (minst->lib_path.env != 0)
        code = file_path_add(minst, &minst->lib_path, minst->lib_path.env);
    /* now put the %rom%lib/ device path before the gs_lib_default_path on the list */
    for (i = 0; i < gx_io_device_table_count; i++) {
        const gx_io_device *iodev = gx_io_device_table[i];
        const char *dname = iodev->dname;

        if (dname && strlen(dname) == 5 && !memcmp("%rom%", dname, 5)) {
            struct stat pstat;
            /* gs_error_unregistered means no usable romfs is available */
            int code = iodev->procs.file_status((gx_io_device *)iodev, dname, &pstat);
            if (code != gs_error_unregistered){
                have_rom_device = 1;
            }
            break;
        }
    }
    if (have_rom_device && code >= 0) {
        code = file_path_add(minst, &minst->lib_path, "%rom%Resource/Init/");
        if (code < 0)
            return code;
        code = file_path_add(minst, &minst->lib_path, "%rom%lib/");
    }
    if (minst->lib_path.final != 0 && code >= 0)
        code = file_path_add(minst, &minst->lib_path, minst->lib_path.final);
    return code;
}

Project: ghostscript
File: imain.c

static char *gs_main_tempnames(gs_main_instance *minst)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    ref *SAFETY;
    ref *tempfiles;
    ref keyval[2];      /* for key and value */
    char *tempnames = NULL;
    int i;
    int idict;
    int len = 0;
    const byte *data = NULL;
    uint size;
    if (minst->init_done >= 2) {
        if (dict_find_string(systemdict, "SAFETY", &SAFETY) <= 0 ||
            dict_find_string(SAFETY, "tempfiles", &tempfiles) <= 0)
            return NULL;
        /* get lengths of temporary filenames */
        idict = dict_first(tempfiles);
        while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) {
            if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0)
                len += size + 1;
        }
        if (len != 0)
            tempnames = (char *)malloc(len+1);
        if (tempnames) {
            memset(tempnames, 0, len+1);
            /* copy temporary filenames */
            idict = dict_first(tempfiles);
            i = 0;
            while ((idict = dict_next(tempfiles, idict, &keyval[0])) >= 0) {
                if (obj_string_data(minst->heap, &keyval[0], &data, &size) >= 0) {
                    memcpy(tempnames+i, (const char *)data, size);
                    i+= size;
                    tempnames[i++] = '\0';
                }
            }
        }
    }
    return tempnames;
}

Project: ghostscript
File: imain.c

gs_pop_boolean(gs_main_instance * minst, bool * result)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    ref vref;
    int code = pop_value(i_ctx_p, &vref);

    if (code < 0)
        return code;
    check_type_only(vref, t_boolean);
    *result = vref.value.boolval;
    ref_stack_pop(&o_stack, 1);
    return 0;
}

Project: ghostscript
File: imain.c

gs_pop_integer(gs_main_instance * minst, long *result)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    ref vref;
    int code = pop_value(i_ctx_p, &vref);

    if (code < 0)
        return code;
    check_type_only(vref, t_integer);
    *result = vref.value.intval;
    ref_stack_pop(&o_stack, 1);
    return 0;
}

Project: ghostscript
File: imain.c

gs_pop_real(gs_main_instance * minst, float *result)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    ref vref;
    int code = pop_value(i_ctx_p, &vref);

    if (code < 0)
        return code;
    switch (r_type(&vref)) {
        case t_real:
            *result = vref.value.realval;
            break;
        case t_integer:
            *result = (float)(vref.value.intval);
            break;
        default:
            return_error(gs_error_typecheck);
    }
    ref_stack_pop(&o_stack, 1);
    return 0;
}

Project: ghostscript
File: imain.c

gs_pop_string(gs_main_instance * minst, gs_string * result)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    ref vref;
    int code = pop_value(i_ctx_p, &vref);

    if (code < 0)
        return code;
    switch (r_type(&vref)) {
        case t_name:
            name_string_ref(minst->heap, &vref, &vref);
            code = 1;
            goto rstr;
        case t_string:
            code = (r_has_attr(&vref, a_write) ? 0 : 1);
          rstr:result->data = vref.value.bytes;
            result->size = r_size(&vref);
            break;
        default:
            return_error(gs_error_typecheck);
    }
    ref_stack_pop(&o_stack, 1);
    return code;
}

Project: ghostscript
File: imain.c

gs_push_boolean(gs_main_instance * minst, bool value)
{
    ref vref;

    make_bool(&vref, value);
    return push_value(minst, &vref);
}

Project: ghostscript
File: imain.c

gs_push_integer(gs_main_instance * minst, long value)
{
    ref vref;

    make_int(&vref, value);
    return push_value(minst, &vref);
}

Project: ghostscript
File: imain.c

gs_push_string(gs_main_instance * minst, byte * chars, uint length,
               bool read_only)
{
    ref vref;

    make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all),
                length, (byte *) chars);
    return push_value(minst, &vref);
}

Project: ghostscript
File: imain.c

gs_to_exit(const gs_memory_t *mem, int exit_status)
{
    return gs_to_exit_with_code(mem, exit_status, 0);
}

Project: ghostscript
File: imain.c

print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem,
                     const char *msg)
{
    ulong used = 0;		/* this we accumulate for the PS memories */
    long utime[2];
    int i;
    gs_memory_status_t status;

    gp_get_realtime(utime);

    for (i = 0; i < countof(dmem->spaces_indexed); ++i) {
        gs_ref_memory_t *mem = dmem->spaces_indexed[i];

        if (mem != 0 && (i == 0 || mem != dmem->spaces_indexed[i - 1])) {
            gs_ref_memory_t *mem_stable =
                (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem);

            gs_memory_status((gs_memory_t *)mem, &status);
            used += status.used;
            if (mem_stable != mem) {
                gs_memory_status((gs_memory_t *)mem_stable, &status);
                used += status.used;
            }
        }
    }
    /* Now get the overall values from the heap memory */
    gs_memory_status(minst->heap, &status);
    dmprintf5(minst->heap, "%% %s time = %g, memory allocated = %lu, used = %lu, max_used = %lu\n",
              msg, utime[0] - minst->base_time[0] +
              (utime[1] - minst->base_time[1]) / 1000000000.0,
              status.allocated, used, status.max_used);
}

Project: ghostscript
File: imain.c

push_value(gs_main_instance *minst, ref * pvalue)
{
    i_ctx_t *i_ctx_p = minst->i_ctx_p;
    int code = ref_stack_push(&o_stack, 1);

    if (code < 0)
        return code;
    *ref_stack_index(&o_stack, 0L) = *pvalue;
    return 0;
}