Commit message: xkbcomp: Don't crash on no-op modmask expressions

If we have an expression of the form 'l1' in an interp section, we
unconditionally try to dereference its args, even if it has none.

Signed-off-by: Daniel Stone <daniels@collabora.com>
CVE: CVE-2018-15863
CWE: CWE-476
Vulnerability description: Unchecked NULL pointer usage in ResolveStateAndPredicate in xkbcomp/compat.c in xkbcommon before 0.8.2 could be used by local attackers to crash (NULL pointer dereference) the xkbcommon parser by supplying a crafted keymap file with a no-op modmask expression.

Project: libxkbcommon
File: compat.c

HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge)
{
    bool ok;

    merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge);

    free(info->name);
    info->name = strdup_safe(file->name);

    for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) {
        switch (stmt->type) {
        case STMT_INCLUDE:
            ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt);
            break;
        case STMT_INTERP:
            ok = HandleInterpDef(info, (InterpDef *) stmt, merge);
            break;
        case STMT_GROUP_COMPAT:
            log_dbg(info->ctx,
                    "The \"group\" statement in compat is unsupported; "
                    "Ignored\n");
            ok = true;
            break;
        case STMT_LED_MAP:
            ok = HandleLedMapDef(info, (LedMapDef *) stmt, merge);
            break;
        case STMT_VAR:
            ok = HandleGlobalVar(info, (VarDef *) stmt);
            break;
        case STMT_VMOD:
            ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge);
            break;
        default:
            log_err(info->ctx,
                    "Compat files may not include other types; "
                    "Ignoring %s\n", stmt_type_to_string(stmt->type));
            ok = false;
            break;
        }

        if (!ok)
            info->errorCount++;

        if (info->errorCount > 10) {
            log_err(info->ctx,
                    "Abandoning compatibility map \"%s\"\n", file->name);
            break;
        }
    }
}

Project: libxkbcommon
File: compat.c

MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from,
                        enum merge_mode merge)
{
    if (from->errorCount > 0) {
        into->errorCount += from->errorCount;
        return;
    }

    into->mods = from->mods;

    if (into->name == NULL) {
        into->name = from->name;
        from->name = NULL;
    }

    if (darray_empty(into->interps)) {
        into->interps = from->interps;
        darray_init(from->interps);
    }
    else {
        SymInterpInfo *si;
        darray_foreach(si, from->interps) {
            si->merge = (merge == MERGE_DEFAULT ? si->merge : merge);
            if (!AddInterp(into, si, false))
                into->errorCount++;
        }
    }

    if (into->num_leds == 0) {
        memcpy(into->leds, from->leds, sizeof(*from->leds) * from->num_leds);
        into->num_leds = from->num_leds;
        from->num_leds = 0;
    }
    else {
        for (xkb_led_index_t i = 0; i < from->num_leds; i++) {
            LedInfo *ledi = &from->leds[i];
            ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge);
            if (!AddLedMap(into, ledi, false))
                into->errorCount++;
        }
    }
}