Commit message: xkbcomp: fix pointer value for FreeStmt

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
CVE: CVE-2018-15857
CWE: CWE-416
Vulnerability description: An invalid free in ExprAppendMultiKeysymList in xkbcomp/ast-build.c in xkbcommon before 0.8.1 could be used by local attackers to crash xkbcommon keymap parsers or possibly have unspecified other impact by supplying a crafted keymap file.

Project: libxkbcommon
File: ast-build.c

AppendStmt(ParseCommon *to, ParseCommon *append)
{
    ParseCommon *iter;

    if (!to)
        return append;

    for (iter = to; iter->next; iter = iter->next);

    iter->next = append;
    return to;
}

Project: libxkbcommon
File: ast-build.c

BoolVarCreate(xkb_atom_t ident, bool set)
{
    ExprDef *name, *value;
    VarDef *def;
    if (!(name = ExprCreateIdent(ident))) {
        return NULL;
    }
    if (!(value = ExprCreateBoolean(set))) {
        FreeStmt((ParseCommon *) name);
        return NULL;
    }
    if (!(def = VarCreate(name, value))) {
        FreeStmt((ParseCommon *) name);
        FreeStmt((ParseCommon *) value);
        return NULL;
    }
    return def;
}

Project: libxkbcommon
File: ast-build.c

ExprAppendKeysymList(ExprDef *expr, xkb_keysym_t sym)
{
    unsigned nSyms = darray_size(expr->keysym_list.syms);

    darray_append(expr->keysym_list.symsMapIndex, nSyms);
    darray_append(expr->keysym_list.symsNumEntries, 1);
    darray_append(expr->keysym_list.syms, sym);

    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateAction(xkb_atom_t name, ExprDef *args)
{
    EXPR_CREATE(ExprAction, expr, EXPR_ACTION_DECL, EXPR_TYPE_UNKNOWN);
    expr->action.name = name;
    expr->action.args = args;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateArrayRef(xkb_atom_t element, xkb_atom_t field, ExprDef *entry)
{
    EXPR_CREATE(ExprArrayRef, expr, EXPR_ARRAY_REF, EXPR_TYPE_UNKNOWN);
    expr->array_ref.element = element;
    expr->array_ref.field = field;
    expr->array_ref.entry = entry;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateBinary(enum expr_op_type op, ExprDef *left, ExprDef *right)
{
    EXPR_CREATE(ExprBinary, expr, op, EXPR_TYPE_UNKNOWN);

    if (op == EXPR_ASSIGN || left->expr.value_type == EXPR_TYPE_UNKNOWN)
        expr->expr.value_type = right->expr.value_type;
    else if (left->expr.value_type == right->expr.value_type ||
             right->expr.value_type == EXPR_TYPE_UNKNOWN)
        expr->expr.value_type = left->expr.value_type;
    expr->binary.left = left;
    expr->binary.right = right;

    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateBoolean(bool set)
{
    EXPR_CREATE(ExprBoolean, expr, EXPR_VALUE, EXPR_TYPE_BOOLEAN);
    expr->boolean.set = set;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateFieldRef(xkb_atom_t element, xkb_atom_t field)
{
    EXPR_CREATE(ExprFieldRef, expr, EXPR_FIELD_REF, EXPR_TYPE_UNKNOWN);
    expr->field_ref.element = element;
    expr->field_ref.field = field;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateFloat(void)
{
    EXPR_CREATE(ExprFloat, expr, EXPR_VALUE, EXPR_TYPE_FLOAT);
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateIdent(xkb_atom_t ident)
{
    EXPR_CREATE(ExprIdent, expr, EXPR_IDENT, EXPR_TYPE_UNKNOWN);
    expr->ident.ident = ident;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateInteger(int ival)
{
    EXPR_CREATE(ExprInteger, expr, EXPR_VALUE, EXPR_TYPE_INT);
    expr->integer.ival = ival;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateKeyName(xkb_atom_t key_name)
{
    EXPR_CREATE(ExprKeyName, expr, EXPR_VALUE, EXPR_TYPE_KEYNAME);
    expr->key_name.key_name = key_name;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateKeysymList(xkb_keysym_t sym)
{
    EXPR_CREATE(ExprKeysymList, expr, EXPR_KEYSYM_LIST, EXPR_TYPE_SYMBOLS);

    darray_init(expr->keysym_list.syms);
    darray_init(expr->keysym_list.symsMapIndex);
    darray_init(expr->keysym_list.symsNumEntries);

    darray_append(expr->keysym_list.syms, sym);
    darray_append(expr->keysym_list.symsMapIndex, 0);
    darray_append(expr->keysym_list.symsNumEntries, 1);

    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateMultiKeysymList(ExprDef *expr)
{
    unsigned nLevels = darray_size(expr->keysym_list.symsMapIndex);

    darray_resize(expr->keysym_list.symsMapIndex, 1);
    darray_resize(expr->keysym_list.symsNumEntries, 1);
    darray_item(expr->keysym_list.symsMapIndex, 0) = 0;
    darray_item(expr->keysym_list.symsNumEntries, 0) = nLevels;

    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateString(xkb_atom_t str)
{
    EXPR_CREATE(ExprString, expr, EXPR_VALUE, EXPR_TYPE_STRING);
    expr->string.str = str;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

ExprCreateUnary(enum expr_op_type op, enum expr_value_type type,
                ExprDef *child)
{
    EXPR_CREATE(ExprUnary, expr, op, type);
    expr->unary.child = child;
    return expr;
}

Project: libxkbcommon
File: ast-build.c

FreeExpr(ExprDef *expr)
{
    if (!expr)
        return;

    switch (expr->expr.op) {
    case EXPR_ACTION_LIST:
    case EXPR_NEGATE:
    case EXPR_UNARY_PLUS:
    case EXPR_NOT:
    case EXPR_INVERT:
        FreeStmt((ParseCommon *) expr->unary.child);
        break;

    case EXPR_DIVIDE:
    case EXPR_ADD:
    case EXPR_SUBTRACT:
    case EXPR_MULTIPLY:
    case EXPR_ASSIGN:
        FreeStmt((ParseCommon *) expr->binary.left);
        FreeStmt((ParseCommon *) expr->binary.right);
        break;

    case EXPR_ACTION_DECL:
        FreeStmt((ParseCommon *) expr->action.args);
        break;

    case EXPR_ARRAY_REF:
        FreeStmt((ParseCommon *) expr->array_ref.entry);
        break;

    case EXPR_KEYSYM_LIST:
        darray_free(expr->keysym_list.syms);
        darray_free(expr->keysym_list.symsMapIndex);
        darray_free(expr->keysym_list.symsNumEntries);
        break;

    default:
        break;
    }
}

Project: libxkbcommon
File: ast-build.c

FreeInclude(IncludeStmt *incl)
{
    IncludeStmt *next;

    while (incl)
    {
        next = incl->next_incl;

        free(incl->file);
        free(incl->map);
        free(incl->modifier);
        free(incl->stmt);

        free(incl);
        incl = next;
    }
}

Project: libxkbcommon
File: ast-build.c

FreeXkbFile(XkbFile *file)
{
    XkbFile *next;

    while (file)
    {
        next = (XkbFile *) file->common.next;

        switch (file->file_type) {
        case FILE_TYPE_KEYMAP:
            FreeXkbFile((XkbFile *) file->defs);
            break;

        case FILE_TYPE_TYPES:
        case FILE_TYPE_COMPAT:
        case FILE_TYPE_SYMBOLS:
        case FILE_TYPE_KEYCODES:
        case FILE_TYPE_GEOMETRY:
            FreeStmt(file->defs);
            break;

        default:
            break;
        }

        free(file->name);
        free(file);
        file = next;
    }
}

Project: libxkbcommon
File: ast-build.c

GroupCompatCreate(unsigned group, ExprDef *val)
{
    GroupCompatDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_GROUP_COMPAT;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->group = group;
    def->def = val;

    return def;
}

Project: libxkbcommon
File: ast-build.c

IncludeCreate(struct xkb_context *ctx, char *str, enum merge_mode merge)
{
    IncludeStmt *incl, *first;
    char *file, *map, *stmt, *tmp, *extra_data;
    char nextop;

    incl = first = NULL;
    file = map = NULL;
    tmp = str;
    stmt = strdup_safe(str);
    while (tmp && *tmp)
    {
        if (!ParseIncludeMap(&tmp, &file, &map, &nextop, &extra_data))
            goto err;

        /*
         * Given an RMLVO (here layout) like 'us,,fr', the rules parser
         * will give out something like 'pc+us+:2+fr:3+inet(evdev)'.
         * We should just skip the ':2' in this case and leave it to the
         * appropriate section to deal with the empty group.
         */
        if (isempty(file)) {
            free(file);
            free(map);
            free(extra_data);
            continue;
        }

        if (first == NULL) {
            first = incl = malloc(sizeof(*first));
        } else {
            incl->next_incl = malloc(sizeof(*first));
            incl = incl->next_incl;
        }

        if (!incl)
            break;

        incl->common.type = STMT_INCLUDE;
        incl->common.next = NULL;
        incl->merge = merge;
        incl->stmt = NULL;
        incl->file = file;
        incl->map = map;
        incl->modifier = extra_data;
        incl->next_incl = NULL;

        if (nextop == '|')
            merge = MERGE_AUGMENT;
        else
            merge = MERGE_OVERRIDE;
    }

    if (first)
        first->stmt = stmt;
    else
        free(stmt);

    return first;

err:
    log_err(ctx, "Illegal include statement \"%s\"; Ignored\n", stmt);
    FreeInclude(first);
    free(stmt);
    return NULL;
}

Project: libxkbcommon
File: ast-build.c

InterpCreate(xkb_keysym_t sym, ExprDef *match)
{
    InterpDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_INTERP;
    def->common.next = NULL;
    def->sym = sym;
    def->match = match;
    def->def = NULL;

    return def;
}

Project: libxkbcommon
File: ast-build.c

KeyAliasCreate(xkb_atom_t alias, xkb_atom_t real)
{
    KeyAliasDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_ALIAS;
    def->common.next = NULL;
    def->alias = alias;
    def->real = real;

    return def;
}

Project: libxkbcommon
File: ast-build.c

KeyTypeCreate(xkb_atom_t name, VarDef *body)
{
    KeyTypeDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_TYPE;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->name = name;
    def->body = body;

    return def;
}

Project: libxkbcommon
File: ast-build.c

KeycodeCreate(xkb_atom_t name, int64_t value)
{
    KeycodeDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_KEYCODE;
    def->common.next = NULL;
    def->name = name;
    def->value = value;

    return def;
}

Project: libxkbcommon
File: ast-build.c

LedMapCreate(xkb_atom_t name, VarDef *body)
{
    LedMapDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_LED_MAP;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->name = name;
    def->body = body;

    return def;
}

Project: libxkbcommon
File: ast-build.c

LedNameCreate(unsigned ndx, ExprDef *name, bool virtual)
{
    LedNameDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_LED_NAME;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->ndx = ndx;
    def->name = name;
    def->virtual = virtual;

    return def;
}

Project: libxkbcommon
File: ast-build.c

ModMapCreate(xkb_atom_t modifier, ExprDef *keys)
{
    ModMapDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_MODMAP;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->modifier = modifier;
    def->keys = keys;

    return def;
}

Project: libxkbcommon
File: ast-build.c

SymbolsCreate(xkb_atom_t keyName, VarDef *symbols)
{
    SymbolsDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_SYMBOLS;
    def->common.next = NULL;
    def->merge = MERGE_DEFAULT;
    def->keyName = keyName;
    def->symbols = symbols;

    return def;
}

Project: libxkbcommon
File: ast-build.c

VModCreate(xkb_atom_t name, ExprDef *value)
{
    VModDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_VMOD;
    def->common.next = NULL;
    def->name = name;
    def->value = value;

    return def;
}

Project: libxkbcommon
File: ast-build.c

VarCreate(ExprDef *name, ExprDef *value)
{
    VarDef *def = malloc(sizeof(*def));
    if (!def)
        return NULL;

    def->common.type = STMT_VAR;
    def->common.next = NULL;
    def->name = name;
    def->value = value;

    return def;
}

Project: libxkbcommon
File: ast-build.c

XkbFileCreate(enum xkb_file_type type, char *name, ParseCommon *defs,
              enum xkb_map_flags flags)
{
    XkbFile *file;

    file = calloc(1, sizeof(*file));
    if (!file)
        return NULL;

    XkbEscapeMapName(name);
    file->file_type = type;
    file->name = name ? name : strdup("(unnamed)");
    file->defs = defs;
    file->flags = flags;

    return file;
}

Project: libxkbcommon
File: ast-build.c

XkbFileFromComponents(struct xkb_context *ctx,
                      const struct xkb_component_names *kkctgs)
{
    char *const components[] = {
        kkctgs->keycodes, kkctgs->types,
        kkctgs->compat, kkctgs->symbols,
    };
    enum xkb_file_type type;
    IncludeStmt *include = NULL;
    XkbFile *file = NULL;
    ParseCommon *defs = NULL;

    for (type = FIRST_KEYMAP_FILE_TYPE; type <= LAST_KEYMAP_FILE_TYPE; type++) {
        include = IncludeCreate(ctx, components[type], MERGE_DEFAULT);
        if (!include)
            goto err;

        file = XkbFileCreate(type, NULL, (ParseCommon *) include, 0);
        if (!file) {
            FreeInclude(include);
            goto err;
        }

        defs = AppendStmt(defs, &file->common);
    }

    file = XkbFileCreate(FILE_TYPE_KEYMAP, NULL, defs, 0);
    if (!file)
        goto err;

    return file;

err:
    FreeXkbFile((XkbFile *) defs);
    return NULL;
}

Project: libxkbcommon
File: ast-build.c

expr_op_type_to_string(enum expr_op_type type)
{
    if (type >= _EXPR_NUM_VALUES)
        return NULL;
    return expr_op_type_strings[type];
}

Project: libxkbcommon
File: ast-build.c

expr_value_type_to_string(enum expr_value_type type)
{
    if (type >= _EXPR_TYPE_NUM_VALUES)
        return NULL;
    return expr_value_type_strings[type];
}

Project: libxkbcommon
File: ast-build.c

stmt_type_to_string(enum stmt_type type)
{
    if (type >= _STMT_NUM_VALUES)
        return NULL;
    return stmt_type_strings[type];
}

Project: libxkbcommon
File: ast-build.c

xkb_file_type_to_string(enum xkb_file_type type)
{
    if (type > _FILE_TYPE_NUM_ENTRIES)
        return "unknown";
    return xkb_file_type_strings[type];
}