Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cow cache snapshot rewriting #88

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions block.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
#include "qemu/rcu.h"
#include "block/coroutines.h"

#include "libafl/syx-snapshot/syx-snapshot.h"

#ifdef CONFIG_BSD
#include <sys/ioctl.h>
#include <sys/queue.h>
Expand Down Expand Up @@ -1259,6 +1261,34 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
*child_flags &= ~BDRV_O_NATIVE_AIO;
}

//// --- Begin LibAFL code ---

/*
* Returns the options and flags that a temporary snapshot should get, based on
* the originally requested flags (the originally requested image will have
* flags like a backing file)
*/
static void bdrv_syx_cow_cache_options(int *child_flags, QDict *child_options,
int parent_flags, QDict *parent_options)
{
GLOBAL_STATE_CODE();
*child_flags = parent_flags;

/* For temporary files, unconditional cache=unsafe is fine */
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");

/* Copy the read-only and discard options from the parent */
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
qdict_copy_default(child_options, parent_options, BDRV_OPT_DISCARD);

/* aio=native doesn't work for cache.direct=off, so disable it for the
* temporary snapshot */
*child_flags &= ~BDRV_O_NATIVE_AIO;
}

//// --- End LibAFL code ---

static void GRAPH_WRLOCK bdrv_backing_attach(BdrvChild *c)
{
BlockDriverState *parent = c->opaque;
Expand Down Expand Up @@ -3870,6 +3900,33 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
return bs;
}

//// --- Begin LibAFL code ---

static BlockDriverState *bdrv_append_syx_cow_cache(BlockDriverState *bs,
int flags,
QDict *scc_options,
Error **errp)
{
BlockDriverState* bs_scc = NULL;

/* We add a syx-cow-cache layer on top of the node being opened */
qdict_put_str(scc_options, "driver", "syx-cow-cache");
qdict_put_str(scc_options, "file", bs->node_name);

/* Open the syx cow cache */
bs_scc = bdrv_open(NULL, NULL, scc_options, flags, errp);
scc_options = NULL;
if (!bs_scc) {
goto out;
}

out:
qobject_unref(scc_options);
return bs_scc;
}

//// --- End LibAFL code ---

static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
int flags,
QDict *snapshot_options,
Expand Down Expand Up @@ -3966,6 +4023,14 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
QDict *snapshot_options = NULL;
int snapshot_flags = 0;

//// --- Begin LibAFL code ---

QDict* scc_options = NULL;
int scc_flags = 0;
bool attach_syx_continue = false;

//// --- End LibAFL code ---

assert(!child_class || !flags);
assert(!child_class == !parent);
GLOBAL_STATE_CODE();
Expand Down Expand Up @@ -4152,6 +4217,24 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
* (the inverse results in an error message from bdrv_open_common()) */
assert(!(flags & BDRV_O_PROTOCOL) || !file);

//// --- Begin LibAFL code ---
if (!(flags & BDRV_O_NOSYX) && syx_snapshot_use_scc() && !strcmp(drv->format_name, "file") && bs->open_flags & BDRV_O_RDWR) {
if (snapshot_flags) {
error_setg(errp, "Syx snapshots should not be used with any QEMU snapshot option");
goto close_and_fail;
}

scc_options = qdict_new();
bdrv_syx_cow_cache_options(&scc_flags, scc_options,
flags, options);

// qdict_put_bool(options, BDRV_OPT_READ_ONLY, true);
// bs->open_flags &= ~BDRV_O_RDWR;

attach_syx_continue = true;
}
//// --- End LibAFL code ---

/* Open the image */
ret = bdrv_open_common(bs, file, options, &local_err);
if (ret < 0) {
Expand Down Expand Up @@ -4221,6 +4304,17 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
bs = snapshot_bs;
}

if (attach_syx_continue) {
BlockDriverState* scc_bs;
scc_bs = bdrv_append_syx_cow_cache(bs, scc_flags, scc_options, &local_err);
scc_options = NULL;
if (local_err) {
goto close_and_fail;
}
bdrv_unref(bs);
bs = scc_bs;
}

return bs;

fail:
Expand Down
73 changes: 3 additions & 70 deletions block/block-backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@
#include "trace.h"
#include "migration/misc.h"

//// --- Begin LibAFL code ---
#ifdef CONFIG_SOFTMMU
#include "libafl/syx-snapshot/syx-snapshot.h"
#endif
//// --- End LibAFL code ---

/* Number of coroutines to reserve per attached device model */
#define COROUTINE_POOL_RESERVATION 64

Expand All @@ -48,9 +42,6 @@ typedef struct BlockBackendAioNotifier {

struct BlockBackend {
char *name;
//// --- Begin LibAFL code ---
guint name_hash;
//// --- End LibAFL code ---
int refcnt;
BdrvChild *root;
AioContext *ctx; /* access with atomic operations only */
Expand Down Expand Up @@ -705,12 +696,6 @@ bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp)
error_setg(errp, "Device with id '%s' already exists", name);
return false;
}
//// --- Begin LibAFL code ---
if (blk_by_name_hash(g_str_hash(name))) {
error_setg(errp, "Device with name hash '%x' already exists", g_str_hash(name));
return false;
}
//// --- End LibAFL code ---
if (bdrv_find_node(name)) {
error_setg(errp,
"Device name '%s' conflicts with an existing node name",
Expand All @@ -719,9 +704,6 @@ bool monitor_add_blk(BlockBackend *blk, const char *name, Error **errp)
}

blk->name = g_strdup(name);
//// --- Begin LibAFL code ---
blk->name_hash = g_str_hash(blk->name);
//// --- End LibAFL code ---
QTAILQ_INSERT_TAIL(&monitor_block_backends, blk, monitor_link);
return true;
}
Expand Down Expand Up @@ -753,14 +735,6 @@ const char *blk_name(const BlockBackend *blk)
return blk->name ?: "";
}

//// --- Begin LibAFL code ---
guint blk_name_hash(const BlockBackend* blk)
{
IO_CODE();
return blk->name_hash;
}
//// --- End LibAFL code ---

/*
* Return the BlockBackend with name @name if it exists, else null.
* @name must not be null.
Expand All @@ -779,22 +753,6 @@ BlockBackend *blk_by_name(const char *name)
return NULL;
}

/*
* Return the BlockBackend with name hash @name_hash if it exists, else null.
*/
BlockBackend *blk_by_name_hash(guint name_hash)
{
BlockBackend *blk = NULL;

GLOBAL_STATE_CODE();
while ((blk = blk_next(blk)) != NULL) {
if (name_hash == blk->name_hash) {
return blk;
}
}
return NULL;
}

/*
* Return the BlockDriverState attached to @blk if any, else null.
*/
Expand Down Expand Up @@ -1648,21 +1606,8 @@ static void coroutine_fn blk_aio_read_entry(void *opaque)

assert(qiov->size == acb->bytes);

//// --- Begin LibAFL code ---
#ifdef CONFIG_SOFTMMU
if (!syx_snapshot_cow_cache_read_entry(rwco->blk, rwco->offset, acb->bytes, qiov, 0, rwco->flags)) {
#endif
//// --- End LibAFL code ---
rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov,
0, rwco->flags);
//// --- Begin LibAFL code ---
#ifdef CONFIG_SOFTMMU
} else {
rwco->ret = 0;
}
#endif
//// --- End LibAFL code ---

rwco->ret = blk_co_do_preadv_part(rwco->blk, rwco->offset, acb->bytes, qiov,
0, rwco->flags);
blk_aio_complete(acb);
}

Expand All @@ -1674,19 +1619,7 @@ static void coroutine_fn blk_aio_write_entry(void *opaque)

assert(!qiov || qiov->size == acb->bytes);

//// --- Begin LibAFL code ---
#ifdef CONFIG_SOFTMMU
if (!syx_snapshot_cow_cache_write_entry(rwco->blk, rwco->offset, acb->bytes, qiov, 0, rwco->flags)) {
#endif
//// --- End LibAFL code ---
rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, qiov, 0, rwco->flags);
//// --- Begin LibAFL code ---
#ifdef CONFIG_SOFTMMU
} else {
rwco->ret = 0;
}
#endif
//// --- End LibAFL code ---
rwco->ret = blk_co_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes, qiov, 0, rwco->flags);

blk_aio_complete(acb);
}
Expand Down
1 change: 1 addition & 0 deletions include/block/block-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ typedef enum {
writes in a snapshot */
#define BDRV_O_TEMPORARY 0x0010 /* delete the file after use */
#define BDRV_O_NOCACHE 0x0020 /* do not use the host page cache */
#define BDRV_O_NOSYX 0x0040
#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the
thread pool */
#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
Expand Down
14 changes: 8 additions & 6 deletions include/libafl/exit.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,22 @@ struct libafl_exit_reason_breakpoint {
};

// A synchronous exit has been triggered.
struct libafl_exit_reason_sync_exit {};
struct libafl_exit_reason_sync_exit {
};

// A timeout occured and we were asked to exit on timeout
struct libafl_exit_reason_timeout {};
struct libafl_exit_reason_timeout {
};

struct libafl_exit_reason {
enum libafl_exit_reason_kind kind;
CPUState* cpu; // CPU that triggered an exit.
vaddr next_pc; // The PC that should be stored in the CPU when re-entering.
union {
struct libafl_exit_reason_internal internal; // kind == INTERNAL
struct libafl_exit_reason_breakpoint breakpoint; // kind == BREAKPOINT
struct libafl_exit_reason_sync_exit sync_exit; // kind == SYNC_EXIT
struct libafl_exit_reason_timeout timeout; // kind == TIMEOUT
struct libafl_exit_reason_internal internal; // kind == INTERNAL
struct libafl_exit_reason_breakpoint breakpoint; // kind == BREAKPOINT
struct libafl_exit_reason_sync_exit sync_exit; // kind == SYNC_EXIT
struct libafl_exit_reason_timeout timeout; // kind == TIMEOUT
} data;
};

Expand Down
32 changes: 13 additions & 19 deletions include/libafl/syx-snapshot/syx-cow-cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
#define INITIAL_NB_CHUNKS_PER_DEVICE (1024 * 64)

typedef struct SyxCowCacheDevice {
GArray* data;
GHashTable* positions; // blk_offset -> data_position
GArray* data; // [u8]
GHashTable* positions; // blkdev offset (must be aligned on chunk_size) ->
// data offset
} SyxCowCacheDevice;

typedef struct SyxCowCacheLayer SyxCowCacheLayer;

typedef struct SyxCowCacheLayer {
GHashTable* cow_cache_devices; // H(device) -> SyxCowCacheDevice
GArray* blks; // [SyxCowCacheDevice]

uint64_t chunk_size;
uint64_t max_nb_chunks;

Expand All @@ -31,21 +31,15 @@ typedef struct SyxCowCache {

SyxCowCache* syx_cow_cache_new(void);

// lhs <- rhs
// rhs is freed and nulled.
void syx_cow_cache_move(SyxCowCache* lhs, SyxCowCache** rhs);
// Returns a SyxCowCache with a new layer on top.
// Other layers from scc are still present.
SyxCowCache* syx_cow_cache_push(SyxCowCache* scc, uint64_t chunk_size,
uint64_t max_size);

void syx_cow_cache_push_layer(SyxCowCache* scc, uint64_t chunk_size,
uint64_t max_size);
void syx_cow_cache_pop_layer(SyxCowCache* scc);
void syx_cow_cache_pop(SyxCowCache* scc);

void syx_cow_cache_flush_highest_layer(SyxCowCache* scc);
// void syx_cow_cache_pop_layer(SyxCowCache* scc);

void syx_cow_cache_read_entry(SyxCowCache* scc, BlockBackend* blk,
int64_t offset, int64_t bytes, QEMUIOVector* qiov,
size_t qiov_offset, BdrvRequestFlags flags);
void syx_cow_cache_flush_highest_layer(SyxCowCache* scc);

bool syx_cow_cache_write_entry(SyxCowCache* scc, BlockBackend* blk,
int64_t offset, int64_t bytes,
QEMUIOVector* qiov, size_t qiov_offset,
BdrvRequestFlags flags);
void syx_cow_cache_check_files_ro(void);
Loading
Loading