cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

dbus-vmstate.c (14719B)


      1/*
      2 * QEMU dbus-vmstate
      3 *
      4 * Copyright (C) 2019 Red Hat Inc
      5 *
      6 * Authors:
      7 *  Marc-André Lureau <marcandre.lureau@redhat.com>
      8 *
      9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
     10 * See the COPYING file in the top-level directory.
     11 */
     12
     13#include "qemu/osdep.h"
     14#include "qemu/units.h"
     15#include "qemu/dbus.h"
     16#include "qemu/error-report.h"
     17#include "qapi/error.h"
     18#include "qom/object_interfaces.h"
     19#include "qapi/qmp/qerror.h"
     20#include "migration/vmstate.h"
     21#include "trace.h"
     22#include "qom/object.h"
     23
     24
     25#define TYPE_DBUS_VMSTATE "dbus-vmstate"
     26OBJECT_DECLARE_SIMPLE_TYPE(DBusVMState,
     27                           DBUS_VMSTATE)
     28
     29
     30struct DBusVMState {
     31    Object parent;
     32
     33    GDBusConnection *bus;
     34    char *dbus_addr;
     35    char *id_list;
     36
     37    uint32_t data_size;
     38    uint8_t *data;
     39};
     40
     41static const GDBusPropertyInfo vmstate_property_info[] = {
     42    { -1, (char *) "Id", (char *) "s",
     43      G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
     44};
     45
     46static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
     47    &vmstate_property_info[0],
     48    NULL
     49};
     50
     51static const GDBusInterfaceInfo vmstate1_interface_info = {
     52    -1,
     53    (char *) "org.qemu.VMState1",
     54    (GDBusMethodInfo **) NULL,
     55    (GDBusSignalInfo **) NULL,
     56    (GDBusPropertyInfo **) &vmstate_property_info_pointers,
     57    NULL,
     58};
     59
     60#define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
     61
     62static GHashTable *
     63get_id_list_set(DBusVMState *self)
     64{
     65    g_auto(GStrv) ids = NULL;
     66    g_autoptr(GHashTable) set = NULL;
     67    int i;
     68
     69    if (!self->id_list) {
     70        return NULL;
     71    }
     72
     73    ids = g_strsplit(self->id_list, ",", -1);
     74    set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
     75    for (i = 0; ids[i]; i++) {
     76        g_hash_table_add(set, ids[i]);
     77        ids[i] = NULL;
     78    }
     79
     80    return g_steal_pointer(&set);
     81}
     82
     83static GHashTable *
     84dbus_get_proxies(DBusVMState *self, GError **err)
     85{
     86    g_autoptr(GHashTable) proxies = NULL;
     87    g_autoptr(GHashTable) ids = NULL;
     88    g_auto(GStrv) names = NULL;
     89    Error *error = NULL;
     90    size_t i;
     91
     92    ids = get_id_list_set(self);
     93    proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
     94                                    g_free, g_object_unref);
     95
     96    names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
     97    if (!names) {
     98        g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
     99                    error_get_pretty(error));
    100        error_free(error);
    101        return NULL;
    102    }
    103
    104    for (i = 0; names[i]; i++) {
    105        g_autoptr(GDBusProxy) proxy = NULL;
    106        g_autoptr(GVariant) result = NULL;
    107        g_autofree char *id = NULL;
    108        size_t size;
    109
    110        proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
    111                    (GDBusInterfaceInfo *) &vmstate1_interface_info,
    112                    names[i],
    113                    "/org/qemu/VMState1",
    114                    "org.qemu.VMState1",
    115                    NULL, err);
    116        if (!proxy) {
    117            return NULL;
    118        }
    119
    120        result = g_dbus_proxy_get_cached_property(proxy, "Id");
    121        if (!result) {
    122            g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    123                                "VMState Id property is missing.");
    124            return NULL;
    125        }
    126
    127        id = g_variant_dup_string(result, &size);
    128        if (ids && !g_hash_table_remove(ids, id)) {
    129            g_clear_pointer(&id, g_free);
    130            g_clear_object(&proxy);
    131            continue;
    132        }
    133        if (size == 0 || size >= 256) {
    134            g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    135                        "VMState Id '%s' is invalid.", id);
    136            return NULL;
    137        }
    138
    139        if (!g_hash_table_insert(proxies, id, proxy)) {
    140            g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    141                        "Duplicated VMState Id '%s'", id);
    142            return NULL;
    143        }
    144        id = NULL;
    145        proxy = NULL;
    146
    147        g_clear_pointer(&result, g_variant_unref);
    148    }
    149
    150    if (ids) {
    151        g_autofree char **left = NULL;
    152
    153        left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
    154        if (*left) {
    155            g_autofree char *leftids = g_strjoinv(",", left);
    156            g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
    157                        "Required VMState Id are missing: %s", leftids);
    158            return NULL;
    159        }
    160    }
    161
    162    return g_steal_pointer(&proxies);
    163}
    164
    165static int
    166dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
    167{
    168    g_autoptr(GError) err = NULL;
    169    g_autoptr(GVariant) result = NULL;
    170    g_autoptr(GVariant) value = NULL;
    171
    172    value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
    173                                      data, size, sizeof(char));
    174    result = g_dbus_proxy_call_sync(proxy, "Load",
    175                                    g_variant_new("(@ay)",
    176                                                  g_steal_pointer(&value)),
    177                                    G_DBUS_CALL_FLAGS_NO_AUTO_START,
    178                                    -1, NULL, &err);
    179    if (!result) {
    180        error_report("%s: Failed to Load: %s", __func__, err->message);
    181        return -1;
    182    }
    183
    184    return 0;
    185}
    186
    187static int dbus_vmstate_post_load(void *opaque, int version_id)
    188{
    189    DBusVMState *self = DBUS_VMSTATE(opaque);
    190    g_autoptr(GInputStream) m = NULL;
    191    g_autoptr(GDataInputStream) s = NULL;
    192    g_autoptr(GError) err = NULL;
    193    g_autoptr(GHashTable) proxies = NULL;
    194    uint32_t nelem;
    195
    196    trace_dbus_vmstate_post_load(version_id);
    197
    198    proxies = dbus_get_proxies(self, &err);
    199    if (!proxies) {
    200        error_report("%s: Failed to get proxies: %s", __func__, err->message);
    201        return -1;
    202    }
    203
    204    m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
    205    s = g_data_input_stream_new(m);
    206    g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
    207    g_buffered_input_stream_set_buffer_size(G_BUFFERED_INPUT_STREAM(s),
    208                                            DBUS_VMSTATE_SIZE_LIMIT);
    209
    210    nelem = g_data_input_stream_read_uint32(s, NULL, &err);
    211    if (err) {
    212        goto error;
    213    }
    214
    215    while (nelem > 0) {
    216        GDBusProxy *proxy = NULL;
    217        uint32_t len;
    218        gsize bytes_read, avail;
    219        char id[256];
    220
    221        len = g_data_input_stream_read_uint32(s, NULL, &err);
    222        if (err) {
    223            goto error;
    224        }
    225        if (len >= 256) {
    226            error_report("%s: Invalid DBus vmstate proxy name %u",
    227                         __func__, len);
    228            return -1;
    229        }
    230        if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
    231                                     &bytes_read, NULL, &err)) {
    232            goto error;
    233        }
    234        if (bytes_read != len) {
    235            error_report("%s: Short read", __func__);
    236            return -1;
    237        }
    238        id[len] = 0;
    239
    240        trace_dbus_vmstate_loading(id);
    241
    242        proxy = g_hash_table_lookup(proxies, id);
    243        if (!proxy) {
    244            error_report("%s: Failed to find proxy Id '%s'", __func__, id);
    245            return -1;
    246        }
    247
    248        len = g_data_input_stream_read_uint32(s, NULL, &err);
    249        if (len > DBUS_VMSTATE_SIZE_LIMIT) {
    250            error_report("%s: Invalid vmstate size: %u", __func__, len);
    251            return -1;
    252        }
    253
    254        g_buffered_input_stream_fill(G_BUFFERED_INPUT_STREAM(s), len, NULL,
    255                                     &err);
    256        if (err) {
    257            goto error;
    258        }
    259
    260        avail = g_buffered_input_stream_get_available(
    261            G_BUFFERED_INPUT_STREAM(s));
    262        if (len > avail) {
    263            error_report("%s: Not enough data available to load for Id: '%s'. "
    264                "Available data size: %zu, Actual vmstate size: %u",
    265                __func__, id, avail, len);
    266            return -1;
    267        }
    268
    269        if (dbus_load_state_proxy(proxy,
    270                g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
    271                                                    NULL),
    272                len) < 0) {
    273            error_report("%s: Failed to restore Id '%s'", __func__, id);
    274            return -1;
    275        }
    276
    277        if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
    278            goto error;
    279        }
    280
    281        nelem -= 1;
    282    }
    283
    284    return 0;
    285
    286error:
    287    error_report("%s: Failed to read from stream: %s", __func__, err->message);
    288    return -1;
    289}
    290
    291static void
    292dbus_save_state_proxy(gpointer key,
    293                      gpointer value,
    294                      gpointer user_data)
    295{
    296    GDataOutputStream *s = user_data;
    297    const char *id = key;
    298    GDBusProxy *proxy = value;
    299    g_autoptr(GVariant) result = NULL;
    300    g_autoptr(GVariant) child = NULL;
    301    g_autoptr(GError) err = NULL;
    302    const uint8_t *data;
    303    gsize size;
    304
    305    trace_dbus_vmstate_saving(id);
    306
    307    result = g_dbus_proxy_call_sync(proxy, "Save",
    308                                    NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
    309                                    -1, NULL, &err);
    310    if (!result) {
    311        error_report("%s: Failed to Save: %s", __func__, err->message);
    312        return;
    313    }
    314
    315    child = g_variant_get_child_value(result, 0);
    316    data = g_variant_get_fixed_array(child, &size, sizeof(char));
    317    if (!data) {
    318        error_report("%s: Failed to Save: not a byte array", __func__);
    319        return;
    320    }
    321    if (size > DBUS_VMSTATE_SIZE_LIMIT) {
    322        error_report("%s: Too large vmstate data to save: %zu",
    323                     __func__, (size_t)size);
    324        return;
    325    }
    326
    327    if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
    328        !g_data_output_stream_put_string(s, id, NULL, &err) ||
    329        !g_data_output_stream_put_uint32(s, size, NULL, &err) ||
    330        !g_output_stream_write_all(G_OUTPUT_STREAM(s),
    331                                   data, size, NULL, NULL, &err)) {
    332        error_report("%s: Failed to write to stream: %s",
    333                     __func__, err->message);
    334    }
    335}
    336
    337static int dbus_vmstate_pre_save(void *opaque)
    338{
    339    DBusVMState *self = DBUS_VMSTATE(opaque);
    340    g_autoptr(GOutputStream) m = NULL;
    341    g_autoptr(GDataOutputStream) s = NULL;
    342    g_autoptr(GHashTable) proxies = NULL;
    343    g_autoptr(GError) err = NULL;
    344
    345    trace_dbus_vmstate_pre_save();
    346
    347    proxies = dbus_get_proxies(self, &err);
    348    if (!proxies) {
    349        error_report("%s: Failed to get proxies: %s", __func__, err->message);
    350        return -1;
    351    }
    352
    353    m = g_memory_output_stream_new_resizable();
    354    s = g_data_output_stream_new(m);
    355    g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
    356
    357    if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
    358                                         NULL, &err)) {
    359        error_report("%s: Failed to write to stream: %s",
    360                     __func__, err->message);
    361        return -1;
    362    }
    363
    364    g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
    365
    366    if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
    367        > UINT32_MAX) {
    368        error_report("%s: DBus vmstate buffer is too large", __func__);
    369        return -1;
    370    }
    371
    372    if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
    373        error_report("%s: Failed to close stream: %s", __func__, err->message);
    374        return -1;
    375    }
    376
    377    g_free(self->data);
    378    self->data_size =
    379        g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
    380    self->data =
    381        g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
    382
    383    return 0;
    384}
    385
    386static const VMStateDescription dbus_vmstate = {
    387    .name = TYPE_DBUS_VMSTATE,
    388    .version_id = 0,
    389    .pre_save = dbus_vmstate_pre_save,
    390    .post_load = dbus_vmstate_post_load,
    391    .fields = (VMStateField[]) {
    392        VMSTATE_UINT32(data_size, DBusVMState),
    393        VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
    394        VMSTATE_END_OF_LIST()
    395    }
    396};
    397
    398static void
    399dbus_vmstate_complete(UserCreatable *uc, Error **errp)
    400{
    401    DBusVMState *self = DBUS_VMSTATE(uc);
    402    g_autoptr(GError) err = NULL;
    403
    404    if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
    405        error_setg(errp, "There is already an instance of %s",
    406                   TYPE_DBUS_VMSTATE);
    407        return;
    408    }
    409
    410    if (!self->dbus_addr) {
    411        error_setg(errp, QERR_MISSING_PARAMETER, "addr");
    412        return;
    413    }
    414
    415    self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
    416                    G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
    417                    G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
    418                    NULL, NULL, &err);
    419    if (err) {
    420        error_setg(errp, "failed to connect to DBus: '%s'", err->message);
    421        return;
    422    }
    423
    424    if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
    425                         &dbus_vmstate, self) < 0) {
    426        error_setg(errp, "Failed to register vmstate");
    427    }
    428}
    429
    430static void
    431dbus_vmstate_finalize(Object *o)
    432{
    433    DBusVMState *self = DBUS_VMSTATE(o);
    434
    435    vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
    436
    437    g_clear_object(&self->bus);
    438    g_free(self->dbus_addr);
    439    g_free(self->id_list);
    440    g_free(self->data);
    441}
    442
    443static char *
    444get_dbus_addr(Object *o, Error **errp)
    445{
    446    DBusVMState *self = DBUS_VMSTATE(o);
    447
    448    return g_strdup(self->dbus_addr);
    449}
    450
    451static void
    452set_dbus_addr(Object *o, const char *str, Error **errp)
    453{
    454    DBusVMState *self = DBUS_VMSTATE(o);
    455
    456    g_free(self->dbus_addr);
    457    self->dbus_addr = g_strdup(str);
    458}
    459
    460static char *
    461get_id_list(Object *o, Error **errp)
    462{
    463    DBusVMState *self = DBUS_VMSTATE(o);
    464
    465    return g_strdup(self->id_list);
    466}
    467
    468static void
    469set_id_list(Object *o, const char *str, Error **errp)
    470{
    471    DBusVMState *self = DBUS_VMSTATE(o);
    472
    473    g_free(self->id_list);
    474    self->id_list = g_strdup(str);
    475}
    476
    477static char *
    478dbus_vmstate_get_id(VMStateIf *vmif)
    479{
    480    return g_strdup(TYPE_DBUS_VMSTATE);
    481}
    482
    483static void
    484dbus_vmstate_class_init(ObjectClass *oc, void *data)
    485{
    486    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
    487    VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
    488
    489    ucc->complete = dbus_vmstate_complete;
    490    vc->get_id = dbus_vmstate_get_id;
    491
    492    object_class_property_add_str(oc, "addr",
    493                                  get_dbus_addr, set_dbus_addr);
    494    object_class_property_add_str(oc, "id-list",
    495                                  get_id_list, set_id_list);
    496}
    497
    498static const TypeInfo dbus_vmstate_info = {
    499    .name = TYPE_DBUS_VMSTATE,
    500    .parent = TYPE_OBJECT,
    501    .instance_size = sizeof(DBusVMState),
    502    .instance_finalize = dbus_vmstate_finalize,
    503    .class_init = dbus_vmstate_class_init,
    504    .interfaces = (InterfaceInfo[]) {
    505        { TYPE_USER_CREATABLE },
    506        { TYPE_VMSTATE_IF },
    507        { }
    508    }
    509};
    510
    511static void
    512register_types(void)
    513{
    514    type_register_static(&dbus_vmstate_info);
    515}
    516
    517type_init(register_types);