rizin/test/unit/test_core_bin.c

617 lines
25 KiB
C

// SPDX-FileCopyrightText: 2021 Florian Märkl <info@florianmaerkl.de>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_core.h>
#include "minunit.h"
// --------------------------
static bool check_buffer(RzBuffer *b) {
// match everything
return true;
}
static bool load_buffer(RzBinFile *bf, RzBinObject *obj, RzBuffer *buf, Sdb *sdb) {
return true;
}
static RzBinInfo *info(RzBinFile *bf) {
RzBinInfo *ret = RZ_NEW0(RzBinInfo);
if (!ret) {
return NULL;
}
ret->file = strdup(bf->file);
ret->has_va = 1;
return ret;
}
static RzPVector *virtual_files(RzBinFile *bf) {
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_virtual_file_free);
RzBinVirtualFile *vfile = RZ_NEW0(RzBinVirtualFile);
vfile->name = strdup("vfile0");
vfile->buf = rz_buf_new_with_bytes((const ut8 *)"\xc0\xff\xee", 3);
vfile->buf_owned = true;
rz_pvector_push(ret, vfile);
vfile = RZ_NEW0(RzBinVirtualFile);
vfile->name = strdup("vfile1");
vfile->buf = rz_buf_new_with_bytes((const ut8 *)"rizin123", 8);
vfile->buf_owned = true;
rz_pvector_push(ret, vfile);
return ret;
}
static RzPVector *maps(RzBinFile *bf) {
RzPVector *ret = rz_pvector_new((RzPVectorFree)rz_bin_map_free);
RzBinMap *map = RZ_NEW0(RzBinMap);
map->name = strdup("direct map");
map->paddr = 2;
map->vaddr = 0x100;
map->psize = 2;
map->vsize = 2;
map->perm = RZ_PERM_RX;
rz_pvector_push(ret, map);
map = RZ_NEW0(RzBinMap);
map->name = strdup("direct map with zeroes");
map->paddr = 2;
map->vaddr = 0x200;
map->psize = 2;
map->vsize = 0x30;
map->perm = RZ_PERM_R;
rz_pvector_push(ret, map);
map = RZ_NEW0(RzBinMap);
map->name = strdup("vfile map");
map->paddr = 4;
map->vaddr = 0x300;
map->psize = 4;
map->vsize = 4;
map->perm = RZ_PERM_RWX;
map->vfile_name = strdup("vfile1");
rz_pvector_push(ret, map);
map = RZ_NEW0(RzBinMap);
map->name = strdup("vfile map with zeroes");
map->paddr = 0;
map->vaddr = 0x400;
map->psize = 3;
map->vsize = 4;
map->perm = RZ_PERM_R;
map->vfile_name = strdup("vfile0");
rz_pvector_push(ret, map);
return ret;
}
RzBinPlugin mock_plugin = {
.name = "mock",
.desc = "Testing Plugin",
.license = "LGPL3",
.load_buffer = load_buffer,
.check_buffer = check_buffer,
.virtual_files = &virtual_files,
.maps = maps,
.info = info,
};
// --------------------------
bool test_map(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
RzIOMap *map = rz_pvector_at(&core->io->maps, 4);
mu_assert_streq(map->name, "mmap.vfile map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x403, "map addr");
mu_assert_eq(map->itv.size, 1, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
RzCoreIOMapInfo *info = map->user;
mu_assert_notnull(info, "map info");
mu_assert_ptreq(info->cf, f, "map info corefile");
mu_assert_eq(info->perm_orig, RZ_PERM_R, "map info perm");
map = rz_pvector_at(&core->io->maps, 5);
mu_assert_streq(map->name, "vmap.vfile map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x400, "map addr");
mu_assert_eq(map->itv.size, 3, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
map = rz_pvector_at(&core->io->maps, 3);
mu_assert_streq(map->name, "vmap.vfile map", "io map name");
mu_assert_eq(map->delta, 4, "map delta");
mu_assert_eq(map->itv.addr, 0x300, "map addr");
mu_assert_eq(map->itv.size, 4, "map size");
mu_assert_eq(map->perm, RZ_PERM_RWX, "io map perm");
info = map->user;
mu_assert_notnull(info, "map info");
mu_assert_ptreq(info->cf, f, "map info corefile");
mu_assert_eq(info->perm_orig, RZ_PERM_RWX, "map info perm");
map = rz_pvector_at(&core->io->maps, 1);
mu_assert_streq(map->name, "mmap.direct map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x202, "map addr");
mu_assert_eq(map->itv.size, 0x2e, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
info = map->user;
mu_assert_notnull(info, "map info");
mu_assert_ptreq(info->cf, f, "map info corefile");
mu_assert_eq(info->perm_orig, RZ_PERM_R, "map info perm");
map = rz_pvector_at(&core->io->maps, 2);
mu_assert_streq(map->name, "fmap.direct map with zeroes", "io map name");
mu_assert_eq(map->delta, 2, "map delta");
mu_assert_eq(map->itv.addr, 0x200, "map addr");
mu_assert_eq(map->itv.size, 2, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
map = rz_pvector_at(&core->io->maps, 0);
mu_assert_streq(map->name, "fmap.direct map", "io map name");
mu_assert_eq(map->delta, 2, "map delta");
mu_assert_eq(map->itv.addr, 0x100, "map addr");
mu_assert_eq(map->itv.size, 2, "map size");
mu_assert_eq(map->perm, RZ_PERM_RX, "io map perm");
info = map->user;
mu_assert_notnull(info, "map info");
mu_assert_ptreq(info->cf, f, "map info corefile");
mu_assert_eq(info->perm_orig, RZ_PERM_RX, "map info perm");
ut8 buf[8];
r = rz_io_read_at(core->io, 0, buf, sizeof(buf));
mu_assert_false(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", 8, "unmapped read");
r = rz_io_read_at(core->io, 0xfe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\x13\x37\xff\xff\xff\xff", 8, "direct map read");
r = rz_io_read_at(core->io, 0x1fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\x13\x37\x00\x00\x00\x00", 8, "direct map read with zeroes");
r = rz_io_read_at(core->io, 0x22e, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\x00\x00\xff\xff\xff\xff\xff\xff", 8, "direct map read with zeroes end");
r = rz_io_read_at(core->io, 0x2fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xffn123\xff\xff", 8, "virtual file read");
r = rz_io_read_at(core->io, 0x3fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xc0\xff\xee\x00\xff\xff", 8, "virtual file read");
// first vfile mapped as readonly at 0x400, write should fail and change nothing
RzBinVirtualFile *vf = rz_pvector_at(bf->o->vfiles, 0);
ut8 red = rz_buf_read_at(vf->buf, 0, buf, 3);
mu_assert_eq(red, 3, "pre-sanity check buf read size");
mu_assert_memeq(buf, (const ut8 *)"\xc0\xff\xee", 3, "pre-sanity check buf contents");
r = rz_io_write_at(core->io, 0x400, (const ut8 *)"zir", 3);
mu_assert_false(r, "io write");
r = rz_io_read_at(core->io, 0x3fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xc0\xff\xee\x00\xff\xff", 8, "virtual file read");
red = rz_buf_read_at(vf->buf, 0, buf, 3);
mu_assert_eq(red, 3, "buf read size");
mu_assert_memeq(buf, (const ut8 *)"\xc0\xff\xee", 3, "nothing changed in buf");
// second vfile mapped as rw at 0x300, write should succeed and change the buffer
vf = rz_pvector_at(bf->o->vfiles, 1);
red = rz_buf_read_at(vf->buf, 0, buf, 8);
mu_assert_eq(red, 8, "pre-sanity check buf read size");
mu_assert_memeq(buf, (const ut8 *)"rizin123", 8, "pre-sanity check buf contents");
r = rz_io_write_at(core->io, 0x301, (const ut8 *)"izi", 3);
mu_assert_true(r, "io write");
r = rz_io_read_at(core->io, 0x2fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xffnizi\xff\xff", 8, "virtual file read");
red = rz_buf_read_at(vf->buf, 0, buf, 8);
mu_assert_eq(red, 8, "buf read size");
mu_assert_memeq(buf, (const ut8 *)"rizinizi", 8, "buf written");
rz_core_free(core);
mu_end;
}
/// test behavior after closing a single RzCoreFile
bool test_cfile_close(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
mu_assert_ptreq(core->file, f, "current file");
mu_assert_eq(rz_list_length(core->bin->binfiles), 0, "no binfiles");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
mu_assert_eq(rz_list_length(core->bin->binfiles), 1, "binfile loaded");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_ptreq(core->file, f, "current file");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
rz_core_file_close(f);
mu_assert_null(core->file, "closed current file");
mu_assert_eq(rz_list_length(core->bin->binfiles), 0, "binfile deleted");
// all maps related to the file, including zero-mmaps, should be closed
mu_assert_eq(rz_pvector_len(&core->io->maps), 0, "io maps count");
RzList *descs = rz_id_storage_list(core->io->files);
mu_assert_true(rz_list_empty(descs), "no files left");
rz_list_free(descs);
ut8 buf[8];
r = rz_io_read_at(core->io, 0xfe, buf, sizeof(buf));
mu_assert_false(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", 8, "direct map read after close");
r = rz_io_read_at(core->io, 0x22e, buf, sizeof(buf));
mu_assert_false(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", 8, "direct map read with zeroes end after close");
rz_core_free(core);
mu_end;
}
/// test behavior after closing an RzCoreFile when another one is present at the same time
bool test_cfile_close_multiple(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f0 = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f0, "load core file");
mu_assert_ptreq(core->file, f0, "current file");
mu_assert_eq(rz_list_length(core->bin->binfiles), 0, "no binfiles");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
mu_assert_eq(rz_list_length(core->bin->binfiles), 1, "binfile loaded");
RzCoreFile *f1 = rz_core_file_open(core, "hex://c0ffeec0ffee", RZ_PERM_R, 0);
mu_assert_notnull(f1, "load another core file");
mu_assert_ptreq(core->file, f1, "current file");
r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
mu_assert_eq(rz_list_length(core->bin->binfiles), 2, "binfile loaded");
RzBinFile *bf1 = rz_bin_file_find_by_fd(core->bin, f1->fd);
mu_assert_notnull(bf1, "binfile");
mu_assert_ptreq(core->file, f1, "current file");
mu_assert_eq(rz_list_length(core->files), 2, "core files count");
mu_assert_eq(rz_list_length(core->bin->binfiles), 2, "bin files count");
mu_assert_eq(rz_pvector_len(&core->io->maps), 12, "io maps count");
rz_core_file_close(f0);
mu_assert_ptreq(core->file, f1, "closed non-current file");
mu_assert_eq(rz_pvector_len(&f1->extra_files), 4, "other file still has its extra file refs");
mu_assert_eq(rz_list_length(core->bin->binfiles), 1, "binfile loaded");
mu_assert_true(rz_list_contains(core->bin->binfiles, bf1), "other binfile still there");
// all maps related to the file, including zero-mmaps, should be closed
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
RzList *descs = rz_id_storage_list(core->io->files);
mu_assert_eq(rz_list_length(descs), 5, "files left");
mu_assert_eq(rz_pvector_len(&f1->extra_files), 4, "remaining core file files");
// f1 should still be alive and happy
RzIODesc *desc = rz_io_desc_get(core->io, f1->fd);
mu_assert_true(rz_list_contains(descs, desc), "remaining main file");
mu_assert_true(rz_list_contains(descs, rz_pvector_at(&f1->extra_files, 0)), "remaining extra file");
mu_assert_true(rz_list_contains(descs, rz_pvector_at(&f1->extra_files, 1)), "remaining extra file");
mu_assert_true(rz_list_contains(descs, rz_pvector_at(&f1->extra_files, 2)), "remaining extra file");
mu_assert_true(rz_list_contains(descs, rz_pvector_at(&f1->extra_files, 3)), "remaining extra file");
rz_list_free(descs);
ut8 buf[8];
r = rz_io_read_at(core->io, 0xfe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xee\xc0\xff\xff\xff\xff", 8, "direct map read");
r = rz_io_read_at(core->io, 0x22e, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\x00\x00\xff\xff\xff\xff\xff\xff", 8, "direct map read with zeroes end");
r = rz_io_read_at(core->io, 0x2fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xffn123\xff\xff", 8, "virtual file read");
r = rz_io_read_at(core->io, 0x3fe, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xc0\xff\xee\x00\xff\xff", 8, "virtual file read");
rz_core_free(core);
mu_end;
}
/// test behavior after closing an RzCoreFile when the underlying mappings have been changed manually
bool test_cfile_close_manual_maps(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
mu_assert_ptreq(core->file, f, "current file");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_ptreq(core->file, f, "current file");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
RzIOMap *map0 = rz_io_map_get(core->io, 0x100);
mu_assert_streq(map0->name, "fmap.direct map", "io map name");
mu_assert_eq(map0->delta, 2, "map delta");
mu_assert_eq(map0->itv.addr, 0x100, "map addr");
mu_assert_eq(map0->itv.size, 2, "map size");
mu_assert_eq(map0->perm, RZ_PERM_RX, "io map perm");
RzIOMap *map1 = rz_io_map_get(core->io, 0x200);
mu_assert_streq(map1->name, "fmap.direct map with zeroes", "io map name");
mu_assert_eq(map1->delta, 2, "map delta");
mu_assert_eq(map1->itv.addr, 0x200, "map addr");
mu_assert_eq(map1->itv.size, 2, "map size");
mu_assert_eq(map1->perm, RZ_PERM_R, "io map perm");
RzIOMap *map2 = rz_io_map_get(core->io, 0x202);
mu_assert_streq(map2->name, "mmap.direct map with zeroes", "io map name");
mu_assert_eq(map2->delta, 0, "map delta");
mu_assert_eq(map2->itv.addr, 0x202, "map addr");
mu_assert_eq(map2->itv.size, 0x2e, "map size");
mu_assert_eq(map2->perm, RZ_PERM_R, "io map perm");
mu_assert_eq(rz_pvector_len(&f->extra_files), 4, "tracked extra file for mmaps and vfiles");
mu_assert_eq(rz_pvector_len(&f->maps), 6, "tracked maps count");
mu_assert_true(rz_pvector_contains(&f->maps, map0), "core file ref to map");
mu_assert_true(rz_pvector_contains(&f->maps, map1), "core file ref to map");
mu_assert_true(rz_pvector_contains(&f->maps, map2), "core file ref to map");
// manually delete some of the maps
rz_io_map_del(core->io, map1->id);
mu_assert_eq(rz_pvector_len(&f->extra_files), 4, "tracked extra file for mmaps and vfiles");
mu_assert_eq(rz_pvector_len(&f->maps), 5, "tracked maps count");
mu_assert_true(rz_pvector_contains(&f->maps, map0), "core file ref to map");
mu_assert_true(rz_pvector_contains(&f->maps, map2), "core file ref to map");
rz_io_map_del(core->io, map2->id);
mu_assert_eq(rz_pvector_len(&f->extra_files), 4, "tracked extra file for mmaps and vfiles");
mu_assert_eq(rz_pvector_len(&f->maps), 4, "tracked maps count");
mu_assert_true(rz_pvector_contains(&f->maps, map0), "core file ref to map");
// and add a new one, unrelated to the core file
RzIOMap *map3;
RzIODesc *mdesc = rz_io_open_at(core->io, "hex://c0ffee", 0644, RZ_PERM_R, 0x8000, &map3);
mu_assert_notnull(mdesc, "manual io file");
mu_assert_eq(map3->itv.addr, 0x8000, "manual map addr");
mu_assert_eq(rz_pvector_len(&core->io->maps), 5, "io maps count");
rz_core_file_close(f);
mu_assert_null(core->file, "closed current file");
// all maps related to the file, including zero-mmaps, should be closed, but not the ones we created manually
mu_assert_eq(rz_pvector_len(&core->io->maps), 1, "io maps count");
mu_assert_ptreq(rz_pvector_at(&core->io->maps, 0), map3, "remaining manual map");
ut8 buf[8];
r = rz_io_read_at(core->io, 0x8000, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xc0\xff\xee\xff\xff\xff\xff\xff", 8, "untouched manual map");
rz_core_free(core);
mu_end;
}
/// test behavior after closing an RzCoreFile when the underlying fd has been closed manually
bool test_cfile_close_manual_fd(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
mu_assert_ptreq(core->file, f, "current file");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_ptreq(core->file, f, "current file");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
// manually close the low-level io fd
rz_io_fd_close(core->io, f->fd);
// io behavior: all maps directly from the fd are automatically closed with it
// The zero-mmapped one stays because core is the one who tracks it
mu_assert_eq(rz_pvector_len(&core->io->maps), 4, "io maps count");
RzIOMap *map = rz_io_map_get(core->io, 0x202);
mu_assert_streq(map->name, "mmap.direct map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x202, "map addr");
mu_assert_eq(map->itv.size, 0x2e, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
map = rz_io_map_get(core->io, 0x403);
mu_assert_streq(map->name, "mmap.vfile map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x403, "map addr");
mu_assert_eq(map->itv.size, 1, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
map = rz_io_map_get(core->io, 0x400);
mu_assert_streq(map->name, "vmap.vfile map with zeroes", "io map name");
mu_assert_eq(map->delta, 0, "map delta");
mu_assert_eq(map->itv.addr, 0x400, "map addr");
mu_assert_eq(map->itv.size, 3, "map size");
mu_assert_eq(map->perm, RZ_PERM_R, "io map perm");
map = rz_io_map_get(core->io, 0x300);
mu_assert_streq(map->name, "vmap.vfile map", "io map name");
mu_assert_eq(map->delta, 4, "map delta");
mu_assert_eq(map->itv.addr, 0x300, "map addr");
mu_assert_eq(map->itv.size, 4, "map size");
mu_assert_eq(map->perm, RZ_PERM_RWX, "io map perm");
rz_core_file_close(f);
mu_assert_null(core->file, "closed current file");
// now everything should be gone
mu_assert_eq(rz_pvector_len(&core->io->maps), 0, "io maps count");
rz_core_free(core);
mu_end;
}
/// test behavior after closing the core file when there have been additional vfile fds/maps created manually outside of it
bool test_cfile_close_manual_vfile_fd(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
mu_assert_ptreq(core->file, f, "current file");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
// the bf->id value should not be seen as a specification but it has to be consistent with the vfile uri below.
mu_assert_eq(bf->id, 0, "binfile id");
RzIOMap *map;
RzIODesc *desc = rz_io_open_at(core->io, "vfile://0/vfile0", 0644, RZ_PERM_R, 0x8000, &map);
mu_assert_notnull(desc, "vfile open");
int vfd = desc->fd; // remember fd, desc should be freed later
ut8 buf[8];
r = rz_io_read_at(core->io, 0x8000, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xc0\xff\xee\xff\xff\xff\xff\xff", 8, "manual vfile map read");
rz_core_file_close(f);
mu_assert_null(core->file, "closed current file");
// now everything should be gone, including the vfile which indirectly pointed into the core file
desc = rz_io_desc_get(core->io, vfd);
mu_assert_null(desc, "vfile closed");
mu_assert_eq(rz_pvector_len(&core->io->maps), 0, "io maps count");
r = rz_io_read_at(core->io, 0x8000, buf, sizeof(buf));
mu_assert_false(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", 8, "manual vfile map read");
rz_core_free(core);
mu_end;
}
/// test behavior after closing the core file when there have been additional maps into its vfiles created manually outside of it
bool test_cfile_close_manual_vfile_map(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f, "load core file");
mu_assert_ptreq(core->file, f, "current file");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
RzBinFile *bf = rz_bin_file_find_by_fd(core->bin, f->fd);
mu_assert_notnull(bf, "binfile");
mu_assert_streq(bf->o->plugin->name, "mock", "binfile with mock plugin");
mu_assert_eq(rz_pvector_len(&core->io->maps), 6, "io maps count");
mu_assert_eq(rz_pvector_len(&f->extra_files), 4, "extra files count");
RzIODesc *desc = rz_pvector_at(&f->extra_files, 3);
mu_assert_streq(desc->name, "vfile://0/vfile0", "vfile desc name");
RzIOMap *map = rz_io_map_add(core->io, desc->fd, RZ_PERM_R, 0, 0x8000, 0x3);
mu_assert_notnull(map, "map added");
ut8 buf[8];
r = rz_io_read_at(core->io, 0x8000, buf, sizeof(buf));
mu_assert_true(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xc0\xff\xee\xff\xff\xff\xff\xff", 8, "manual vfile map read");
rz_core_file_close(f);
mu_assert_null(core->file, "closed current file");
// now everything should be gone, including the vfile map which indirectly pointed into the core file's vfile fd
mu_assert_eq(rz_pvector_len(&core->io->maps), 0, "io maps count");
r = rz_io_read_at(core->io, 0x8000, buf, sizeof(buf));
mu_assert_false(r, "io read");
mu_assert_memeq(buf, (const ut8 *)"\xff\xff\xff\xff\xff\xff\xff\xff", 8, "manual vfile map read");
rz_core_free(core);
mu_end;
}
/// test behavior after closing an RzCoreFile with manual vfile maps added when another one is present at the same time
bool test_cfile_close_manual_cfile_map_multiple(void) {
RzCore *core = rz_core_new();
rz_bin_plugin_add(core->bin, &mock_plugin);
RzCoreFile *f0 = rz_core_file_open(core, "hex://424213374242", RZ_PERM_R, 0);
mu_assert_notnull(f0, "load core file");
mu_assert_ptreq(core->file, f0, "current file");
mu_assert_eq(rz_list_length(core->bin->binfiles), 0, "no binfiles");
bool r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
mu_assert_eq(rz_list_length(core->bin->binfiles), 1, "binfile loaded");
mu_assert_eq(rz_pvector_len(&f0->binfiles), 1, "binfile in core file");
RzBinFile *bf0 = rz_pvector_at(&f0->binfiles, 0);
mu_assert_notnull(bf0, "binfile");
mu_assert_eq(bf0->fd, f0->fd, "binfile fd");
RzCoreFile *f1 = rz_core_file_open(core, "hex://c0ffeec0ffee", RZ_PERM_R, 0);
mu_assert_notnull(f1, "load another core file");
mu_assert_ptreq(core->file, f1, "current file");
r = rz_core_bin_load(core, NULL, 0);
mu_assert_true(r, "core bin load");
mu_assert_eq(rz_list_length(core->bin->binfiles), 2, "binfile loaded");
mu_assert_eq(rz_pvector_len(&f1->binfiles), 1, "binfile in core file");
RzBinFile *bf1 = rz_pvector_at(&f1->binfiles, 0);
mu_assert_notnull(bf1, "binfile");
mu_assert_eq(bf1->fd, f1->fd, "binfile fd");
// the bf->id values should not be seen as a specification but it has to be consistent with the vfile uri below.
mu_assert_eq(bf0->id, 0, "binfile id");
mu_assert_eq(bf1->id, 1, "binfile id");
RzIOMap *vmap0;
RzIODesc *vdesc0 = rz_io_open_at(core->io, "vfile://0/vfile0", 0644, RZ_PERM_R, 0x8000, &vmap0);
mu_assert_notnull(vdesc0, "vfile open");
mu_assert_notnull(vmap0, "vfile mapped");
RzIOMap *vmap1;
RzIODesc *vdesc1 = rz_io_open_at(core->io, "vfile://1/vfile0", 0644, RZ_PERM_R, 0x9000, &vmap1);
mu_assert_notnull(vdesc1, "vfile open");
mu_assert_notnull(vmap1, "vfile mapped");
int vfd0 = vdesc0->fd; // remember fds, desc should be freed later
int vfd1 = vdesc1->fd;
rz_core_file_close(f0);
mu_assert_ptreq(core->file, f1, "closed non-current file");
mu_assert_eq(rz_pvector_len(&f1->extra_files), 4, "other file still has its extra file refs");
mu_assert_eq(rz_list_length(core->bin->binfiles), 1, "binfile loaded");
mu_assert_true(rz_list_contains(core->bin->binfiles, bf1), "other binfile still there");
vdesc0 = rz_io_desc_get(core->io, vfd0);
mu_assert_null(vdesc0, "vdesc backed by closed binfile should be gone");
vdesc1 = rz_io_desc_get(core->io, vfd1);
mu_assert_notnull(vdesc1, "vdesc backed by unclosed binfile should be there");
rz_core_free(core);
mu_end;
}
bool all_tests() {
mu_run_test(test_map);
mu_run_test(test_cfile_close);
mu_run_test(test_cfile_close_multiple);
mu_run_test(test_cfile_close_manual_maps);
mu_run_test(test_cfile_close_manual_fd);
mu_run_test(test_cfile_close_manual_vfile_fd);
mu_run_test(test_cfile_close_manual_vfile_map);
mu_run_test(test_cfile_close_manual_cfile_map_multiple);
return tests_passed != tests_run;
}
mu_main(all_tests)