radare2/libr/util/buf_sparse.c
Riccardo Schirone 9392fc7117 Fix some RBin file format crashes ##bin
* Use st64 and ut64 instead of size_t
* Fix mach0 parsing issue
* Fix OOB write in PE parsing
* Fix crash in bin_xbe parser
* Fix crash in bin_java
Fixes clusterfuzz-testcase-minimized-ia_fuzz-5740477602594816.dms
2019-05-30 15:45:52 +02:00

220 lines
4.9 KiB
C

#include <r_util.h>
struct buf_sparse_priv {
RList *sparse;
ut64 offset;
};
static void buffer_sparse_free(void *a) {
RBufferSparse *s = (RBufferSparse *)a;
free (s->data);
free (s);
}
static bool sparse_limits(RList *l, ut64 *max) {
bool set = false;
RBufferSparse *s;
RListIter *iter;
r_list_foreach (l, iter, s) {
if (set) {
if (max && s->to > *max) {
*max = s->to;
}
} else {
set = true;
if (max) {
*max = s->to;
}
}
}
return set;
}
static RBufferSparse *sparse_append(RList *l, ut64 addr, const ut8 *data, ut64 len) {
if (l && data) {
RBufferSparse *s = R_NEW0 (RBufferSparse);
if (s) {
s->data = calloc (1, len);
if (s->data) {
s->from = addr;
s->to = addr + len;
s->size = len;
memcpy (s->data, data, len);
return r_list_append (l, s)? s: NULL;
}
free (s);
}
}
return NULL;
}
//ret -1 if failed; # of bytes copied if success
static st64 sparse_write(RList *l, ut64 addr, const ut8 *data, ut64 len) {
RBufferSparse *s;
RListIter *iter;
ut64 olen = len;
r_list_foreach (l, iter, s) {
if (addr >= s->from && addr < s->to) {
ut64 delta = addr - s->from;
ut64 reallen = s->size - delta >= len? len: s->size - delta;
memcpy (s->data + delta, data, reallen);
data += reallen;
len -= reallen;
addr += reallen;
}
if (len == 0) {
return olen;
}
}
if (len > 0 && !sparse_append (l, addr, data, len)) {
return -1;
}
return olen;
}
static inline struct buf_sparse_priv *get_priv_sparse(RBuffer *b) {
struct buf_sparse_priv *priv = (struct buf_sparse_priv *)b->priv;
r_warn_if_fail (priv);
return priv;
}
static bool buf_sparse_init(RBuffer *b, const void *user) {
struct buf_sparse_priv *priv = R_NEW0 (struct buf_sparse_priv);
if (!priv) {
return false;
}
priv->sparse = r_list_newf (buffer_sparse_free);
priv->offset = 0;
b->priv = priv;
return true;
}
static bool buf_sparse_fini(RBuffer *b) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
r_list_free (priv->sparse);
R_FREE (b->priv);
return true;
}
static bool buf_sparse_resize(RBuffer *b, ut64 newsize) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
RListIter *iter, *tmp;
RBufferSparse *s;
r_list_foreach_safe (priv->sparse, iter, tmp, s) {
if (s->from >= newsize) {
r_list_delete (priv->sparse, iter);
} else if (s->to >= newsize) {
RBufferSparse *ns = R_NEW (RBufferSparse);
ns->from = s->from;
ns->to = newsize;
ns->size = ns->to - ns->from;
ut8 *tmp = realloc (s->data, s->size);
if (!tmp) {
free (ns);
return false;
}
// otherwise it will be double-freed by r_list_delete
s->data = NULL;
ns->data = tmp;
ns->written = s->written;
r_list_append (priv->sparse, ns);
r_list_delete (priv->sparse, iter);
}
}
ut64 max;
max = sparse_limits (priv->sparse, &max)? max: 0;
if (max < newsize) {
return !!sparse_write (priv->sparse, newsize - 1, (ut8 *)&b->Oxff_priv, 1);
}
return true;
}
static ut64 buf_sparse_size(RBuffer *b) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
ut64 max;
return sparse_limits (priv->sparse, &max)? max: 0;
}
static st64 buf_sparse_read(RBuffer *b, ut8 *buf, ut64 len) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
RBufferSparse *c;
RListIter *iter;
ut64 max = 0;
memset (buf, b->Oxff_priv, len);
r_list_foreach (priv->sparse, iter, c) {
if (max < c->to) {
max = c->to;
}
if (priv->offset < c->to && c->from < priv->offset + len) {
if (priv->offset < c->from) {
ut64 l = R_MIN (priv->offset + len - c->from, c->size);
memcpy (buf + c->from - priv->offset, c->data, l);
} else {
ut64 l = R_MIN (c->to - priv->offset, len);
memcpy (buf, c->data + priv->offset - c->from, l);
}
}
}
if (priv->offset > max) {
return -1;
}
ut64 r = R_MIN (max - priv->offset, len);
priv->offset += r;
return r;
}
static st64 buf_sparse_write(RBuffer *b, const ut8 *buf, ut64 len) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
st64 r = sparse_write (priv->sparse, priv->offset, buf, len);
priv->offset += r;
return r;
}
static st64 buf_sparse_seek(RBuffer *b, st64 addr, int whence) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
ut64 max;
if (addr < 0 && (-addr) > (st64)priv->offset) {
return -1;
}
switch (whence) {
case R_BUF_CUR:
priv->offset += addr;
break;
case R_BUF_SET:
priv->offset = addr;
break;
case R_BUF_END:
if (!sparse_limits (priv->sparse, &max)) {
max = 0;
}
priv->offset = max + addr;
break;
default:
r_warn_if_reached ();
return -1;
}
return priv->offset;
}
static RList *buf_sparse_nonempty_list(RBuffer *b) {
struct buf_sparse_priv *priv = get_priv_sparse (b);
return r_list_clone (priv->sparse);
}
static const RBufferMethods buffer_sparse_methods = {
.init = buf_sparse_init,
.fini = buf_sparse_fini,
.read = buf_sparse_read,
.write = buf_sparse_write,
.get_size = buf_sparse_size,
.resize = buf_sparse_resize,
.seek = buf_sparse_seek,
.nonempty_list = buf_sparse_nonempty_list,
};