rizin/librz/util/buf.c
Khairul Azhar Kasmiran 1abe2dce99
Prevent RzBuffer's Oxff_priv from overriding io.0xff (#6371)
* Prevent RzBuffer's `Oxff_priv` from overriding `io.0xff`
* Use io from RZ_BUFFER_IO and RZ_BUFFER_IO_FD buffers
* Add `RzIO *` param to rz_buf_new_mmap()
* pe: Discard incomplete import directory
2026-05-27 06:18:45 +08:00

1604 lines
39 KiB
C

// SPDX-FileCopyrightText: 2009-2020 ret2libc <sirmy15@gmail.com>
// SPDX-FileCopyrightText: 2009-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_types.h>
#include <rz_util.h>
#include <rz_io.h>
#include "buf_file.c"
#include "buf_sparse.c"
#include "buf_bytes.c"
#include "buf_mmap.c"
#include "buf_io_fd.c"
#include "buf_io.c"
#include "buf_ref.c"
#define GET_STRING_BUFFER_SIZE 32
static void buf_whole_buf_free(RzBuffer *b) {
// free the whole_buf only if it was initially allocated by the buf types
if (b->methods->get_whole_buf) {
if (b->methods->free_whole_buf) {
b->methods->free_whole_buf(b);
}
} else {
RZ_FREE(b->whole_buf);
}
}
static bool buf_init(RzBuffer *b, const void *user) {
rz_return_val_if_fail(b, false);
if (b->type == RZ_BUFFER_BYTES) {
return buf_bytes_init(b, user);
} else if (b->type == RZ_BUFFER_MMAP) {
return buf_mmap_init(b, user);
} else {
rz_return_val_if_fail(b->methods, false);
return b->methods->init ? b->methods->init(b, user) : true;
}
}
static bool buf_fini(RzBuffer *b) {
rz_return_val_if_fail(b, false);
if (b->type == RZ_BUFFER_BYTES) {
return buf_bytes_fini(b);
} else if (b->type == RZ_BUFFER_MMAP) {
return buf_mmap_fini(b);
} else {
rz_return_val_if_fail(b->methods, false);
return b->methods->fini ? b->methods->fini(b) : true;
}
}
static ut64 buf_get_size(RzBuffer *b) {
rz_return_val_if_fail(b, UT64_MAX);
if (b->type == RZ_BUFFER_BYTES || b->type == RZ_BUFFER_MMAP) {
return buf_bytes_get_size(b);
} else {
rz_return_val_if_fail(b->methods, UT64_MAX);
return b->methods->get_size ? b->methods->get_size(b) : 0;
}
}
static st64 buf_read(RzBuffer *b, ut8 *buf, size_t len) {
rz_return_val_if_fail(b, -1);
if (b->type == RZ_BUFFER_BYTES || b->type == RZ_BUFFER_MMAP) {
return buf_bytes_read(b, buf, len);
} else {
rz_return_val_if_fail(b->methods, -1);
return b->methods->read ? b->methods->read(b, buf, len) : -1;
}
}
static st64 buf_write(RzBuffer *b, const ut8 *buf, size_t len) {
rz_return_val_if_fail(b && b->methods, -1);
buf_whole_buf_free(b);
return b->methods->write ? b->methods->write(b, buf, len) : -1;
}
static st64 buf_seek(RzBuffer *b, st64 addr, int whence) {
rz_return_val_if_fail(b, -1);
if (b->type == RZ_BUFFER_BYTES || b->type == RZ_BUFFER_MMAP) {
return buf_bytes_seek(b, addr, whence);
} else {
rz_return_val_if_fail(b->methods, -1);
return b->methods->seek ? b->methods->seek(b, addr, whence) : -1;
}
}
static bool buf_resize(RzBuffer *b, ut64 newsize) {
rz_return_val_if_fail(b, -1);
if (b->type == RZ_BUFFER_BYTES) {
return buf_bytes_resize(b, newsize);
} else if (b->type == RZ_BUFFER_MMAP) {
return buf_mmap_resize(b, newsize);
} else {
rz_return_val_if_fail(b->methods, -1);
return b->methods->resize ? b->methods->resize(b, newsize) : false;
}
}
typedef struct {
ut16 repeat;
ut8 type_size;
ut8 big_endian;
} BufFormatToken;
static st64 buf_format(RzBuffer *dst, RzBuffer *src, const char *fmt, int n) {
RzVector /*<BufFormatToken>*/ tokens;
rz_vector_init(&tokens, sizeof(BufFormatToken), NULL, NULL);
unsigned repeat = 1;
for (size_t j = 0; fmt[j]; j++) {
unsigned tsize = 0;
bool bigendian = false;
switch (fmt[j]) {
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (repeat == 1) {
char *end = (char *)&fmt[j + 1];
repeat = strtoul(&fmt[j], &end, 10);
j += (ptrdiff_t)(end - &fmt[j]) - 1;
}
continue;
case 'S':
bigendian = true;
/* fall-thru */
case 's':
tsize = 2;
break;
case 'I':
bigendian = true;
/* fall-thru */
case 'i':
tsize = 4;
break;
case 'L':
bigendian = true;
/* fall-thru */
case 'l':
tsize = 8;
break;
case 'c':
tsize = 1;
break;
default:
goto err_exit;
}
if (!tsize || repeat > UT16_MAX) {
goto err_exit;
}
BufFormatToken tok = {
.repeat = repeat,
.type_size = tsize,
.big_endian = bigendian
};
if (!rz_vector_push(&tokens, &tok)) {
goto err_exit;
}
repeat = 1;
}
if (rz_vector_empty(&tokens)) {
goto err_exit;
}
st64 res = 0;
for (int i = 0; i < n; i++) {
const BufFormatToken *tok;
rz_vector_foreach (&tokens, tok) {
if (tok->type_size == 1) {
ut8 tmp[8];
size_t nbytes = tok->repeat;
while (nbytes != 0) {
size_t block_sz = RZ_MIN(sizeof(tmp), nbytes);
if (rz_buf_read(src, tmp, block_sz) != block_sz) {
goto err_exit;
}
if (rz_buf_write(dst, tmp, block_sz) != block_sz) {
goto err_exit;
}
res += block_sz;
nbytes -= block_sz;
}
continue;
}
for (int k = 0; k < tok->repeat; k++) {
ut8 tmp[sizeof(ut64)];
st64 r = rz_buf_read(src, tmp, tok->type_size);
if (r < tok->type_size) {
goto err_exit;
}
if ((RZ_HOST_IS_BIG_ENDIAN != (bool)tok->big_endian) && tok->type_size > 1) {
// just swap endianness if the host endianness
// is not the same and is not one byte
switch (tok->type_size) {
case 2: {
ut16 value = rz_read_ble16(tmp, tok->big_endian);
rz_write_ble16(tmp, value, RZ_HOST_IS_BIG_ENDIAN);
break;
}
case 4: {
ut32 value = rz_read_ble32(tmp, tok->big_endian);
rz_write_ble32(tmp, value, RZ_HOST_IS_BIG_ENDIAN);
break;
}
case 8: {
ut64 value = rz_read_ble64(tmp, tok->big_endian);
rz_write_ble64(tmp, value, RZ_HOST_IS_BIG_ENDIAN);
break;
}
default:
goto err_exit;
}
}
r = rz_buf_write(dst, tmp, tok->type_size);
if (r < tok->type_size) {
goto err_exit;
}
res += r;
}
}
}
rz_vector_fini(&tokens);
return res;
err_exit:
rz_vector_fini(&tokens);
return -1;
}
static bool buf_move_back(RZ_NONNULL RzBuffer *b, ut64 addr, ut64 length) {
rz_return_val_if_fail(b, -1);
ut64 size = rz_buf_size(b);
if (size < addr) {
return false;
}
ut8 *tmp = RZ_NEWS(ut8, size - addr);
if (!tmp) {
return false;
}
bool res = false;
st64 tmp_length = rz_buf_read_at(b, addr, tmp, size - addr);
if (tmp_length < 0) {
goto err;
}
if (!rz_buf_resize(b, size + length)) {
goto err;
}
if (rz_buf_write_at(b, addr + length, tmp, tmp_length) < 0) {
goto err;
}
res = true;
err:
free(tmp);
return res;
}
static ut8 *get_whole_buf(RzBuffer *b, ut64 *size) {
rz_return_val_if_fail(b && size, NULL);
buf_whole_buf_free(b);
if (b->type == RZ_BUFFER_BYTES) {
return buf_bytes_get_whole_buf(b, size);
} else if (b->type == RZ_BUFFER_MMAP) {
return buf_mmap_get_whole_buf(b, size);
} else {
rz_return_val_if_fail(b && size && b->methods, NULL);
if (b->methods->get_whole_buf) {
return b->methods->get_whole_buf(b, size);
}
ut64 buf_size = rz_buf_size(b);
if (buf_size == UT64_MAX) {
return NULL;
}
b->whole_buf = RZ_NEWS(ut8, buf_size);
if (!b->whole_buf) {
return NULL;
}
if (rz_buf_read_at(b, 0, b->whole_buf, buf_size) < 0) {
RZ_FREE(b->whole_buf);
return NULL;
}
*size = buf_size;
return b->whole_buf;
}
}
static RzBuffer *new_buffer(RzBufferType type, void *user) {
const RzBufferMethods *methods = NULL;
switch (type) {
case RZ_BUFFER_BYTES:
methods = &buffer_bytes_methods;
break;
case RZ_BUFFER_MMAP:
methods = &buffer_mmap_methods;
break;
case RZ_BUFFER_SPARSE:
methods = &buffer_sparse_methods;
break;
case RZ_BUFFER_FILE:
methods = &buffer_file_methods;
break;
case RZ_BUFFER_IO_FD:
methods = &buffer_io_fd_methods;
break;
case RZ_BUFFER_IO:
methods = &buffer_io_methods;
break;
case RZ_BUFFER_REF:
methods = &buffer_ref_methods;
break;
default:
rz_warn_if_reached();
return NULL;
}
return rz_buf_new_with_methods(methods, user, type);
}
/**
* \brief Creates a new empty buffer with a predefined size;
* \param len The length in byte of the new buffer.
* \return Return the new allocated buffer.
*
* The function creates a new buffer of the specified length in memory, filled
* with \0.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_empty(ut64 len) {
ut8 *buf = RZ_NEWS0(ut8, len);
if (!buf) {
return NULL;
}
struct buf_bytes_user u = { 0 };
u.data_steal = buf;
u.length = len;
u.steal = true;
RzBuffer *res = new_buffer(RZ_BUFFER_BYTES, &u);
if (!res) {
free(buf);
}
return res;
}
/**
* \brief Creates a new buffer from a file.
* \param file The filename used to create the new buffer.
* \param perm Same meaning than the symbolic constants defined in sys/stat.h.
* \param mode Same meaning than the symbolic constants use with open (fcntl.h).
* \return Return the new allocated buffer.
*
* \see rz_sys_open()
*
* The function creates a new buffer synchronized with the file content,
* opening it with rz_sys_open and using regular read/write operations to
* change its content.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_file(const char *file, int perm, int mode) {
struct buf_file_user u = { 0 };
u.file = file;
u.perm = perm;
u.mode = mode;
return new_buffer(RZ_BUFFER_FILE, &u);
}
/**
* \brief Creates a new buffer from a file using rz_file_mmap.
* \param file The filename used to create the new buffer.
* \param perm Same meaning than the symbolic constants defined in sys/stat.h.
* \param mode Same meaning than the symbolic constants use with open (fcntl.h).
* \param io Optional RzIO for configuration variables.
* \return Return the new allocated buffer.
*
* \see rz_file_mmap()
*
* The function creates a new buffer synchronized with the file content, using
* mmap to access the file.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_mmap(const char *filename, int perm, int mode, RZ_NULLABLE void /* RzIO */ *io) {
rz_return_val_if_fail(filename, NULL);
struct buf_mmap_user u = { 0 };
u.filename = filename;
u.perm = perm;
u.mode = mode;
u.io = io;
return new_buffer(RZ_BUFFER_MMAP, &u);
}
/**
* \brief Creates a new buffer from a slice of another buffer.
* \param b The source buffer used to create the sub buffer.
* \param offset The starting offset in the source buffer.
* \param size The number of bytes that will be extract from the source buffer.
* \return Return the new allocated buffer.
*
* \see rz_buf_ref()
*
* The function creates a new buffer which shows just a slice of another one,
* passed as argument. The allocated buffer will reference the source buffer.
* So both buffer are synchronized.
*
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_slice(RzBuffer *b, ut64 offset, ut64 size) {
struct buf_ref_user u = { 0 };
u.parent = b;
u.offset = offset;
u.size = size;
return new_buffer(RZ_BUFFER_REF, &u);
}
// TODO: rename to new_from_file ?
/**
* \brief Creates a new buffer from a file.
* \param file The filename used to create the new buffer.
* \return Return the new allocated buffer.
*
* The function creates a new buffer in memory, initializing it with the whole
* content of the specified file.
*
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_slurp(const char *file) {
size_t len;
char *tmp = rz_file_slurp(file, &len);
if (!tmp) {
return NULL;
}
struct buf_bytes_user u = { 0 };
u.data_steal = (ut8 *)tmp;
u.length = (ut64)len;
u.steal = true;
return new_buffer(RZ_BUFFER_BYTES, &u);
}
/**
* \brief Creates a sparse buffer.
* \param Oxff The byte used to fill unpopulated bytes.
* \return Return the new allocated buffer.
*
* The function creates a new allocated buffer using the RZ_BUFFER_SPARSE back end.
* Creates a new sparse RzBuffer where unpopulated bytes are filled with Oxff parameter.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_sparse(ut8 Oxff) {
RzBuffer *b = new_buffer(RZ_BUFFER_SPARSE, NULL);
if (b) {
b->Oxff_priv = Oxff;
}
return b;
}
/**
* \brief Creates a sparse buffer from a already populated buffer.
* \param b The source buffer used to create the sub buffer.
* \param write_mode Defined how byte are written to the buffer.
* \return Return the new allocated buffer.
*
* The function creates a new allocated buffer using the RZ_BUFFER_SPARSE back end.
* Creates a new sparse RzBuffer where unpopulated bytes are taken as-is from b
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_sparse_overlay(RzBuffer *b, RzBufferSparseWriteMode write_mode) {
rz_return_val_if_fail(b, NULL);
SparseInitConfig cfg = {
.base = b,
.write_mode = write_mode
};
return new_buffer(RZ_BUFFER_SPARSE, &cfg);
}
/**
* \brief Creates a new buffer from a source buffer.
* \param b The source buffer used to create the sub buffer.
* \return Return the new allocated buffer.
*
* \see rz_buf_new_with_bytes()
*
* The function creates a new allocated buffer using the RZ_BUFFER_BYTES back end.
* WARNING: this function doesn't use reference counting, this can lead to a lot
* of memory overhead.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_buf(RzBuffer *b) {
ut64 size = 0;
const ut8 *tmp = get_whole_buf(b, &size);
return rz_buf_new_with_bytes(tmp, size);
}
/**
* \brief Creates a new buffer with a bytes array.
* \param bytes The bytes array used to initialized the buffer.
* \param len The length of the bytes array.
* \return Return the new allocated buffer.
*
* The function creates a new buffer in memory, initializing it with the bytes
* passed as argument. The bytes parameter can be NULL, but the length should
* be set to 0.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_bytes(RZ_NULLABLE RZ_BORROW const ut8 *bytes, ut64 len) {
rz_return_val_if_fail(bytes || !len, NULL); // if bytes == NULL, then len must be 0
struct buf_bytes_user u = { 0 };
u.data = bytes;
u.length = len;
return new_buffer(RZ_BUFFER_BYTES, &u);
}
/**
* \brief Creates a new buffer with a bytes array.
* The buffer takes ownership of the bytes array.
*
* \param bytes The bytes array used to initialized the buffer.
* \param len The length of the bytes array.
* \return Return the new allocated buffer.
*
* The function creates a new buffer in memory, initializing it with the bytes
* passed as argument. The bytes parameter can be NULL, but the length should
* be set to 0.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_from_bytes(RZ_NULLABLE RZ_OWN ut8 *bytes, ut64 len) {
rz_return_val_if_fail((bytes && len) || (!bytes && !len), NULL);
struct buf_bytes_user u = { 0 };
u.length = len;
u.data_steal = bytes;
u.steal = true;
return new_buffer(RZ_BUFFER_BYTES, &u);
}
// TODO: Optimize to use memcpy when buffers are not in range..
// check buf boundaries and offsets and use memcpy or memmove
// copied from librz/io/cache.c:rz_io_cache_read
// ret # of bytes copied
/**
* \brief Creates a new buffer wrapping a file descriptor accessed through RzIOBind.
* \param iob Pointer to RzIOBind structure.
* \param fd File descriptor to wrap in the buffer.
* \return Return the new allocated buffer.
*
* The function creates a new buffer wrapping access to a file descriptor
* through the RzIOBind methods specified in librz/io.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_io_fd(RZ_NONNULL void *iob, int fd) {
rz_return_val_if_fail(iob && fd >= 0, NULL);
struct buf_io_fd_user u = { 0 };
u.iob = (RzIOBind *)iob;
u.fd = fd;
return new_buffer(RZ_BUFFER_IO_FD, &u);
}
/**
* \brief Creates a new buffer wrapping the memory map exposed by RzIOBind.
* \param iob Pointer to RzIOBind structure.
* \return Return the new allocated buffer.
*
* This buffer will use `rz_io_read_at_mapped()`/`rz_io_write_at()` as implemented by
* the RzIOBind given.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_io(RZ_NONNULL void *iob) {
rz_return_val_if_fail(iob, NULL);
return new_buffer(RZ_BUFFER_IO, iob);
}
/**
* \brief Creates a new buffer with a specific back end.
* \param methods A struct holding the list of methods to use.
* \param init_user Some implementation specific information.
* \return Return the new allocated buffer.
*
* \see RzBufferMethods
*
* The function creates a new allocated buffer using a custom back end. This function
* should only be used when no other back end are appropriate.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_methods(RZ_NONNULL const RzBufferMethods *methods, void *init_user, RzBufferType type) {
RzBuffer *b = RZ_NEW0(RzBuffer);
if (!b) {
return NULL;
}
b->type = type;
b->methods = methods;
if (!buf_init(b, init_user)) {
free(b);
return NULL;
}
return b;
}
/**
* \brief Creates a new buffer with a bytes array.
* \param bytes The bytes array used to initialized the buffer.
* \param len The length of the bytes array.
* \param steal If the boolean is true the function take the ownership of the data.
* \return Return the new allocated buffer.
*
* The function creates a new buffer in memory. The argument \p bytes is used
* as the memory backend if \p steal is true, otherwise a new buffer is
* allocated and its content is initialized with the one from \p bytes.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_pointers(const ut8 *bytes, ut64 len, bool steal) {
struct buf_bytes_user u = { 0 };
u.data_steal = bytes;
u.length = len;
u.steal = steal;
return new_buffer(RZ_BUFFER_BYTES, &u);
}
/**
* \brief Creates a new buffer from a string.
* \param msg The source string used to create the buffer.
* \return Return the new allocated buffer.
*
* The function creates a new buffer in memory, initializing it with the string
* passed as argument.
*/
RZ_API RZ_OWN RzBuffer *rz_buf_new_with_string(RZ_NONNULL const char *msg) {
return rz_buf_new_with_bytes((const ut8 *)msg, (ut64)strlen(msg));
}
/**
* \brief Get a string with a max length from the buffer
* \param b RzBuffer pointer
* \param addr The address of the string
* \param size The max length authorized
* \return A string with a length <= size or NULL
*
* Return an heap-allocated string read from the RzBuffer b at address addr. The
* length depends on the first '\0' found and the arguments size in the buffer.
* If there is no '\0' in the buffer, there is no string, thus NULL is returned.
*/
RZ_API RZ_OWN char *rz_buf_get_nstring(RZ_NONNULL RzBuffer *b, ut64 addr, size_t size) {
rz_return_val_if_fail(b, NULL);
RzStrBuf *buf = rz_strbuf_new(NULL);
while (true) {
char tmp[GET_STRING_BUFFER_SIZE + 1];
st64 r = rz_buf_read_at(b, addr, (ut8 *)tmp, sizeof(tmp) - 1);
if (r < 1) {
rz_strbuf_free(buf);
return NULL;
}
size_t count = rz_str_nlen(tmp, r);
rz_strbuf_append_n(buf, tmp, count);
if (count > size) {
rz_strbuf_free(buf);
return NULL;
}
if (count != r) {
break;
}
addr += r;
size -= count;
}
char *result = rz_strbuf_drain(buf);
return result;
}
/**
* \brief Get a string from the buffer
* \param b RzBuffer pointer
* \param addr The address of the string
* \return A string with a length <= size or NULL
*
* Return an heap-allocated string read from the RzBuffer b at address addr. The
* length depends on the first '\0' found in the buffer. If there is no '\0' in
* the buffer, there is no string, thus NULL is returned.
*/
RZ_API RZ_OWN char *rz_buf_get_string(RZ_NONNULL RzBuffer *b, ut64 addr) {
rz_return_val_if_fail(b, NULL);
return rz_buf_get_nstring(b, addr, rz_buf_size(b));
}
/**
* \brief Get a string from the buffer
* \param b RzBuffer pointer
* \param s The pointer to output the string
* \return Length of the string
*
* Return an heap-allocated string read from \p b. The length depends on
* the first '\0' found in the buffer. If there is no '\0' in
* the buffer, there is no string, thus 0 is returned.
*/
RZ_API ut64 rz_buf_read_string(RZ_NONNULL RzBuffer *b, RZ_BORROW RZ_NULLABLE char **s) {
rz_return_val_if_fail(b, 0);
char *tmp = rz_buf_get_string(b, rz_buf_tell(b));
if (!tmp) {
return 0;
}
const ut64 string_len = strlen(tmp) + 1;
rz_buf_seek(b, string_len, RZ_BUF_CUR);
if (s) {
*s = tmp;
} else {
free(tmp);
}
return string_len;
}
/**
* \brief Stringify the buffer
* \param b RzBuffer pointer
* \return Return an allocated string.
*
* The string is guaranteed to be NULL terminated even if the buffer doesn't have
* any NULL bytes.
*/
RZ_API RZ_OWN char *rz_buf_to_string(RZ_NONNULL RzBuffer *b) {
rz_return_val_if_fail(b, NULL);
ut64 size = rz_buf_size(b);
char *result = RZ_NEWS(char, size + 1);
if (!result) {
return NULL;
}
if (rz_buf_read_at(b, 0, (ut8 *)result, size) < 0) {
free(result);
return NULL;
}
result[size] = '\0';
return result;
}
/**
* \brief Increment the reference count of the buffer
* \param b RzBuffer to reference
* \return Return the same value \p b
*
* The function increment the reference count of the buffer
*/
RZ_API RzBuffer *rz_buf_ref(RzBuffer *b) {
if (b) {
b->refctr++;
}
return b;
}
/**
* \brief Append the content of the buffer a to the buffer b.
* \param dst the destination buffer with write permission
* \param src the source buffer
* \return Return the status of the operation.
*
* The function append the content of the buffer a, the two buffer don't need to
* have the same back end. WARNING: This function can allocate a lot of memory.
*/
RZ_API bool rz_buf_append_buf(RZ_NONNULL RzBuffer *b, RZ_NONNULL RzBuffer *a) {
rz_return_val_if_fail(b && a && !b->readonly, false);
ut64 size = 0;
const ut8 *tmp = get_whole_buf(a, &size);
return rz_buf_append_bytes(b, tmp, size);
}
/**
* \brief Append a slice of the buffer a to the buffer b.
* \param dst the destination buffer with write permission
* \param src the source buffer
* \param offset The starting offset in the buffer a.
* \param size The number of bytes that will be extract from the buffer a.
* \return Return the status of the operation.
*
* The function append a slice of the buffer a, the two buffer don't need to
* have the same back end. WARNING: This function can allocate a lot of memory.
*/
RZ_API bool rz_buf_append_buf_slice(RZ_NONNULL RzBuffer *b, RZ_NONNULL RzBuffer *a, ut64 offset, ut64 size) {
rz_return_val_if_fail(b && a && !b->readonly, false);
ut8 *tmp = RZ_NEWS(ut8, size);
if (!tmp) {
return false;
}
st64 r = rz_buf_read_at(a, offset, tmp, size);
if (r < 0) {
free(tmp);
return false;
}
bool result = rz_buf_append_bytes(b, tmp, r);
free(tmp);
return result;
}
/**
* \brief Append an array of bytes to the buffer.
* \param dst the destination buffer with write permission
* \param src the source buffer
* \param length ...
* \return Return the status of the operation.
*/
RZ_API bool rz_buf_append_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 length) {
rz_return_val_if_fail(b && buf && !b->readonly, false);
if (rz_buf_seek(b, 0, RZ_BUF_END) < 0) {
return false;
}
return rz_buf_write(b, buf, length) == length;
}
/**
* \brief Extend the size of the buffer.
* \param b A buffer with write permission.
* \param length The number of bytes to add at the end of the buffer.
* \return Return the status of the operation.
*/
RZ_API bool rz_buf_append_nbytes(RZ_NONNULL RzBuffer *b, ut64 length) {
rz_return_val_if_fail(b && !b->readonly, false);
return rz_buf_resize(b, rz_buf_size(b) + length);
}
/**
* \brief Add a ut16 number at the end of the buffer.
* \param b A buffer with write permission.
* \param n
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_append_ut16(RZ_NONNULL RzBuffer *b, ut16 n) {
rz_return_val_if_fail(b && !b->readonly, false);
return rz_buf_append_bytes(b, (const ut8 *)&n, sizeof(n));
}
/**
* \brief Add a ut32 number at the end of the buffer.
* \param b A buffer with write permission.
* \param n
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_append_ut32(RZ_NONNULL RzBuffer *b, ut32 n) {
rz_return_val_if_fail(b && !b->readonly, false);
return rz_buf_append_bytes(b, (const ut8 *)&n, sizeof(n));
}
/**
* \brief Add a ut64 number at the end of the buffer.
* \param b A buffer with write permission.
* \param n
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_append_ut64(RZ_NONNULL RzBuffer *b, ut64 n) {
rz_return_val_if_fail(b && !b->readonly, false);
return rz_buf_append_bytes(b, (const ut8 *)&n, sizeof(n));
}
/**
* \brief Dump the content of the buffer to a file.
* \param b A buffer with write permission.
* \param file The output file where the buffer content while be outputted.
* \return Return the status of the operation.
*
* \see rz_file_dump()
*
* ...
*/
RZ_API bool rz_buf_dump(RZ_NONNULL RzBuffer *b, RZ_NONNULL const char *file) {
rz_return_val_if_fail(b && file, false);
ut64 size = 0;
const ut8 *tmp = get_whole_buf(b, &size);
return rz_file_dump(file, tmp, size, 0);
}
/**
* \brief Free all internal data hold by the buffer.
* \param b A buffer with write permission.
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_fini(RzBuffer *b) {
if (!b) {
return false;
}
if (b->refctr > 0) {
b->refctr--;
return false;
}
buf_whole_buf_free(b);
return buf_fini(b);
}
/**
* \brief Prepend an array of bytes to the buffer.
* \param b A buffer with write permission.
* \param buf ...
* \param length ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_prepend_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 length) {
rz_return_val_if_fail(b && buf && !b->readonly, false);
return rz_buf_insert_bytes(b, 0, buf, length) >= 0;
}
/**
* \brief Read a byte at the cursor in the buffer.
* \param b ...
* \param result ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_read8(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut8 *result) {
rz_return_val_if_fail(b && result, false);
return rz_buf_read(b, result, sizeof(ut8)) == sizeof(ut8);
}
/**
* \brief Read a byte at the specified address in the buffer.
* \param b ...
* \param addr ...
* \param result ...
* \return Return the status of the operation.
*
* \see rz_buf_read8()
* ...
*/
RZ_API bool rz_buf_read8_at(RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *result) {
rz_return_val_if_fail(b && result, false);
return rz_buf_read_at(b, addr, result, sizeof(ut8)) == sizeof(ut8);
}
/**
* \brief Resize the buffer size.
* \param b ...
* \param newsize ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_resize(RZ_NONNULL RzBuffer *b, ut64 newsize) {
rz_return_val_if_fail(b, false);
return buf_resize(b, newsize);
}
/**
* \brief Replace the content of the buffer with the bytes array.
* \param b A buffer with write permission.
* \param buf ...
* \param length ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_set_bytes(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 length) {
rz_return_val_if_fail(b && buf && !b->readonly, false);
if (!rz_buf_resize(b, 0)) {
return false;
}
if (rz_buf_seek(b, 0, RZ_BUF_SET) < 0) {
return false;
}
if (!rz_buf_append_bytes(b, buf, length)) {
return false;
}
return rz_buf_seek(b, 0, RZ_BUF_SET) >= 0;
}
/**
* \brief Write a byte at the cursor in the buffer.
* \param b ...
* \param result ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API bool rz_buf_write8(RZ_NONNULL RzBuffer *b, ut8 value) {
rz_return_val_if_fail(b, false);
return rz_buf_write(b, &value, sizeof(ut8)) == sizeof(ut8);
}
/**
* \brief Write a byte at the specified address in the buffer.
* \param b ...
* \param addr ...
* \param value ...
* \return Return the status of the operation.
*
* \see rz_buf_write8()
*
* ...
*/
RZ_API bool rz_buf_write8_at(RZ_NONNULL RzBuffer *b, ut64 addr, ut8 value) {
rz_return_val_if_fail(b, false);
return rz_buf_write_at(b, addr, &value, sizeof(ut8)) == sizeof(ut8);
}
/**
* \brief Append a string to the buffer.
* \param b A buffer with write permission.
* \param str ...
* \return Return the status of the operation.
*
* ...
*/
RZ_API st64 rz_buf_append_string(RZ_NONNULL RzBuffer *b, RZ_NONNULL const char *str) {
rz_return_val_if_fail(b && str && !b->readonly, false);
return rz_buf_append_bytes(b, (const ut8 *)str, strlen(str));
}
/**
* \brief ...
* \param b ...
* \param buf ...
* \param fmt ...
* \param n ...
* \return ...
*
* ...
*/
RZ_API st64 rz_buf_fread(RZ_NONNULL RzBuffer *b, RZ_NONNULL ut8 *buf, RZ_NONNULL const char *fmt, int n) {
rz_return_val_if_fail(b && buf && fmt, -1);
// XXX: we assume the caller knows what he's doing
RzBuffer *dst = rz_buf_new_with_pointers(buf, UT64_MAX, false);
st64 res = buf_format(dst, b, fmt, n);
rz_buf_free(dst);
return res;
}
/**
* \brief ...
* \param b ...
* \param addr ...
* \param buf ...
* \param fmt ...
* \param n ...
* \return ...
*
* ...
*/
RZ_API st64 rz_buf_fread_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL ut8 *buf, RZ_NONNULL const char *fmt, int n) {
rz_return_val_if_fail(b && buf && fmt, -1);
st64 tmp = rz_buf_tell(b);
if (tmp < 0) {
return -1;
}
if (rz_buf_seek(b, addr, RZ_BUF_SET) < 0) {
return -1;
}
st64 result = rz_buf_fread(b, buf, fmt, n);
if (rz_buf_seek(b, tmp, RZ_BUF_SET) < 0) {
return -1;
}
return result;
}
/**
* \brief ...
* \param b ...
* \param addr ...
* \param buf ...
* \param fmt ...
* \param n ...
* \return ...
*
* ...
*/
RZ_API st64 rz_buf_fwrite(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, RZ_NONNULL const char *fmt, int n) {
rz_return_val_if_fail(b && buf && fmt && !b->readonly, -1);
// XXX: we assume the caller knows what he's doing
RzBuffer *src = rz_buf_new_with_pointers(buf, UT64_MAX, false);
st64 result = buf_format(b, src, fmt, n);
rz_buf_free(src);
return result;
}
/**
* \brief ...
* \param b ...
* \param addr ...
* \param buf ...
* \param fmt ...
* \param n ...
* \return ...
*
* ...
*/
RZ_API st64 rz_buf_fwrite_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, RZ_NONNULL const char *fmt, int n) {
rz_return_val_if_fail(b && buf && fmt && !b->readonly, -1);
st64 tmp = rz_buf_tell(b);
if (tmp < 0) {
return -1;
}
if (rz_buf_seek(b, addr, RZ_BUF_SET) < 0) {
return -1;
}
st64 result = rz_buf_fwrite(b, buf, fmt, n);
if (rz_buf_seek(b, tmp, RZ_BUF_SET) < 0) {
return -1;
}
return result;
}
/**
* \brief Insert an array of bytes in the buffer.
* \param b A buffer with write permission.
* \param addr ...
* \param buf ...
* \param length ...
* \return Return the number of bytes written.
*
* ...
*/
RZ_API st64 rz_buf_insert_bytes(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 length) {
rz_return_val_if_fail(b && !b->readonly, -1);
if (!buf_move_back(b, addr, length)) {
return -1;
}
st64 result = rz_buf_write_at(b, addr, buf, length);
if (result < 0) {
return -1;
}
return result;
}
static RzIO *get_io_from_buffer(RZ_NONNULL RzBuffer *b) {
rz_return_val_if_fail(b, NULL);
switch (b->type) {
case RZ_BUFFER_IO:
case RZ_BUFFER_IO_FD: {
RzIOBind *iob = b->type == RZ_BUFFER_IO ? ((BufIOPriv *)b->priv)->iob : ((struct buf_io_fd_priv *)b->priv)->iob;
rz_return_val_if_fail(iob, NULL);
return iob->io;
}
case RZ_BUFFER_MMAP:
return ((struct buf_mmap_priv *)b->priv)->io;
default:
return NULL;
}
}
/**
* \brief Reads \p len bytes from buffer \p b into \p buf.
* \p buf should have enough space to contain the bytes.
* The seek of \p b is advanced by \p len bytes.
* EXCEPT: RZ_BUF_IO_FD, RZ_BUF_FILE.
* Because they were implemented without seek advancement.
* And changing it breaks everything. Sorry :/
*
* \param b The buffer to read from.
* \param buf The array to move te bytes into.
* \param len The number of bytes to read from the buffer.
*
* \return The number of bytes read. -1 in case of error and 0 for EOF reached.
*/
RZ_API st64 rz_buf_read(RZ_NONNULL RzBuffer *b, RZ_NONNULL ut8 RZ_OUT *buf, ut64 len) {
rz_return_val_if_fail(b && buf, -1);
st64 result = buf_read(b, buf, len);
if (result < 0) {
return -1;
}
if (len > result) {
ut8 Oxff = b->Oxff_priv;
RzIO *io = get_io_from_buffer(b);
if (io) {
if (!io->ff) {
RZ_LOG_ERROR("Incomplete buffer read: expected 0x%" PFMT64x " bytes, got %" PFMT64d " bytes.\n", len, result);
return -1;
}
Oxff = io->Oxff;
}
memset(buf + result, Oxff, len - result);
}
return result;
}
/**
* \brief Read len bytes of the buffer at the specified address.
* \param b ...
* \param addr ...
* \param buf ...
* \param len ...
* \return Return the number of bytes read.
*
* ...
*/
RZ_API st64 rz_buf_read_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL RZ_OUT ut8 *buf, ut64 len) {
rz_return_val_if_fail(b && buf, -1);
st64 tmp = rz_buf_tell(b);
if (tmp < 0) {
return -1;
}
st64 result = -1;
if (rz_buf_seek(b, addr, RZ_BUF_SET) >= 0) {
result = rz_buf_read(b, buf, len);
}
if (rz_buf_seek(b, tmp, RZ_BUF_SET) < 0) {
return -1;
}
return result;
}
/**
* \brief Modify the current cursor position in the buffer.
* \param b ...
* \param addr ...
* \param whence The relative position of the address.
* \return Return the new cursor position
*
* ...
*/
RZ_API st64 rz_buf_seek(RZ_NONNULL RzBuffer *b, st64 addr, int whence) {
rz_return_val_if_fail(b, -1);
return buf_seek(b, addr, whence);
}
/**
* \brief Write len bytes of the buffer at the cursor.
* \param b A buffer with write permission.
* \param buf ...
* \param len ...
* \return Return the number of bytes written.
*
* ...
*/
RZ_API st64 rz_buf_write(RZ_NONNULL RzBuffer *b, RZ_NONNULL const ut8 *buf, ut64 len) {
rz_return_val_if_fail(b && buf && !b->readonly, -1);
return buf_write(b, buf, len);
}
/**
* \brief Write len bytes of the buffer at the specified address.
* \param b A buffer with write permission.
* \param addr ...
* \param buf ...
* \param len ...
* \return Return the number of bytes written.
*
* ...
*/
RZ_API st64 rz_buf_write_at(RZ_NONNULL RzBuffer *b, ut64 addr, RZ_NONNULL const ut8 *buf, ut64 len) {
rz_return_val_if_fail(b && buf && !b->readonly, -1);
st64 tmp = rz_buf_tell(b);
if (tmp < 0) {
return -1;
}
if (rz_buf_seek(b, addr, RZ_BUF_SET) < 0) {
return -1;
}
st64 result = rz_buf_write(b, buf, len);
if (rz_buf_seek(b, tmp, RZ_BUF_SET) < 0) {
return -1;
}
return result;
}
/**
* \brief Return the size of the buffer
* \param b ...
* \return ...
*
* ...
*/
RZ_API ut64 rz_buf_size(RZ_NONNULL RzBuffer *b) {
rz_return_val_if_fail(b, 0);
return buf_get_size(b);
}
/**
* \brief Return the current cursor position.
* \param b ...
* \return ...
*
* ...
*/
RZ_API ut64 rz_buf_tell(RZ_NONNULL RzBuffer *b) {
rz_return_val_if_fail(b, 0);
return rz_buf_seek(b, 0, RZ_BUF_CUR);
}
/**
* \brief Free all internal data hold by the buffer and the buffer.
* \param b ...
* \return Return the status of the operation.
*
* \see rz_buf_fini()
*
* ...
*/
RZ_API void rz_buf_free(RzBuffer *b) {
if (!b) {
return;
}
if (rz_buf_fini(b)) {
free(b);
}
}
/**
* \brief Change the overflow byte used in the RZ_BUFFER_SPARSE.
* \param b ...
* \param Oxff The new byte filling value.
*
* \see rz_buf_new_sparse()
* \see rz_buf_new_sparse_overlay()
*
* Set the content that bytes read outside the buffer bounds should have.
*/
RZ_API void rz_buf_set_overflow_byte(RZ_NONNULL RzBuffer *b, ut8 Oxff) {
rz_return_if_fail(b);
b->Oxff_priv = Oxff;
}
/**
* \brief Returns true if \b is a bytes buffer.
*
* \param b The buffer to check.
*
* \return True if the buffer is a raw bytes buffer. False otherwise.
*/
RZ_API bool rz_buf_is_bytes_buf(const RzBuffer *b) {
return b->type == RZ_BUFFER_BYTES;
}
/**
* \brief Return a borrowed array of bytes representing the buffer data.
* \param b Buffer to get the data from.
* \param size Size of the returned data.
*
* WARNING: this function should be used with care because it may allocate the
* entire buffer in memory. Consider using the rz_buf_read* APIs or rz_buf_fwd_scan API instead and
* read only the chunks you need.
*/
RZ_DEPRECATE RZ_API RZ_BORROW ut8 *rz_buf_data(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut64 *size) {
rz_return_val_if_fail(b && size, NULL);
return get_whole_buf(b, size);
}
/**
* \brief Scans buffer linearly in chunks calling \p fwd_scan for each chunk.
*
* \param b RzBuffer to read
* \param start Start address
* \param amount Amount of bytes to read
* \param fwd_scan Function to call for each chunk
* \param user User data to pass to fwd_scan
* \return Number of bytes read
*/
RZ_API ut64 rz_buf_fwd_scan(RZ_NONNULL RzBuffer *b, ut64 start, ut64 amount, RZ_NONNULL RzBufferFwdScan fwd_scan, RZ_NULLABLE void *user) {
rz_return_val_if_fail(b && fwd_scan, 0);
if (!amount) {
return 0;
}
if (b->methods->get_whole_buf) {
ut64 sz;
ut8 *buf = NULL;
if (b->type == RZ_BUFFER_BYTES) {
buf = buf_bytes_get_whole_buf(b, &sz);
} else if (b->type == RZ_BUFFER_MMAP) {
buf = buf_mmap_get_whole_buf(b, &sz);
} else {
b->methods->get_whole_buf(b, &sz);
}
if (buf && (sz <= start)) {
return 0;
}
if (buf) {
return fwd_scan(buf + start, RZ_MIN(sz - start, amount), user);
}
}
const ut64 size = rz_buf_size(b);
if (size <= start) {
return 0;
}
if (b->whole_buf) {
return fwd_scan(b->whole_buf + start, RZ_MIN(size - start, amount), user);
}
ut64 addr = start;
const ut64 user_end = UT64_ADD_OVFCHK(start, amount) ? UT64_MAX : start + amount;
const ut64 end = RZ_MIN(user_end, size);
const size_t buf_size = RZ_MIN(end - start, 0x1000);
ut8 *buf = malloc(buf_size);
if (!buf) {
return 0;
}
while (addr < end) {
const ut64 read_amount = RZ_MIN(buf_size, end - addr);
rz_buf_read_at(b, addr, buf, read_amount);
ut64 read = fwd_scan(buf, read_amount, user);
if (!read) {
break;
}
addr += read;
}
free(buf);
return addr - start;
}
/**
* \brief Get the whole buffer for scanning in hot paths.
* Please use `rz_buf_fwd_scan()` or `rz_buf_read()`, if your use case is NOT performance critical.
*
* \param buffer RzBuffer to read.
* \param sz Size of returned data in bytes.
* \return Immutable pointer to the whole buffer or NULL in case of failure.
*/
RZ_API RZ_BORROW const ut8 *rz_buf_get_whole_hot_paths(RZ_NONNULL RzBuffer *b, RZ_NONNULL RZ_OUT ut64 *sz) {
rz_return_val_if_fail(b && b->methods && b->methods->get_whole_buf, NULL);
return b->methods->get_whole_buf(b, sz);
}
/**
* \brief Decodes ULEB128 from RzBuffer
*
* \param buffer Buffer used to decode the ULEB128.
* \param value Decoded value.
* \return The number of bytes used.
*/
RZ_API st64 rz_buf_uleb128(RZ_NONNULL RzBuffer *buffer, RZ_NONNULL ut64 *value) {
rz_return_val_if_fail(buffer && value, -1);
ut64 sum = 0, used = 0, slice;
ut32 shift = 0;
ut8 byte = 0;
do {
if (rz_buf_read(buffer, &byte, sizeof(byte)) < 1) {
// malformed uleb128 due end of buffer
return -1;
}
used++;
slice = byte & 0x7f;
if (shift >= 64 || (shift == 63 && slice > 1ULL) || ((slice << shift) >> shift) != slice) {
// uleb128 too big for ut64
return -1;
}
sum += slice << shift;
shift += 7;
} while (byte >= 128);
*value = sum;
return used;
}
/**
* \brief Decodes SLEB128 from RzBuffer
*
* \param buffer Buffer used to decode the SLEB128.
* \param value Decoded value.
* \return The number of bytes used.
*/
RZ_API st64 rz_buf_sleb128(RZ_NONNULL RzBuffer *buffer, RZ_NONNULL st64 *value) {
rz_return_val_if_fail(buffer && value, -1);
ut64 used = 0, slice;
st64 sum = 0;
ut32 shift = 0;
ut8 byte = 0;
do {
if (rz_buf_read(buffer, &byte, sizeof(byte)) < 1) {
// malformed sleb128 due end of buffer
return -1;
}
used++;
slice = byte & 0x7f;
if ((shift >= 64 && slice != (sum < 0 ? 0x7f : 0x00)) ||
(shift == 63 && slice != 0 && slice != 0x7f)) {
// sleb128 too big for st64
return -1;
}
sum |= slice << shift;
shift += 7;
} while (byte >= 128);
if (shift < 64 && (byte & 0x40)) {
// extends negative sign
sum |= (-1ull) << shift;
}
*value = sum;
return used;
}
RZ_API RzBufferType rz_buf_type(RZ_NONNULL const RzBuffer *b) {
rz_return_val_if_fail(b, RZ_BUFFER_INVALID);
return b->type;
}