- Fixes to add integer overflow checks on allocation (defense in depth).

git-svn-id: file:///svn/unbound/trunk@3372 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2015-03-20 15:36:25 +00:00
parent 0a0b37be65
commit 6feb8fb6a5
12 changed files with 66 additions and 8 deletions

View file

@ -223,6 +223,8 @@ copy_msg(struct regional* region, struct lruhash_entry* e,
struct query_info** k, struct reply_info** d)
{
struct reply_info* rep = (struct reply_info*)e->data;
if(rep->rrset_count > RR_COUNT_MAX)
return 0; /* to protect against integer overflow */
*d = (struct reply_info*)regional_alloc_init(region, e->data,
sizeof(struct reply_info) +
sizeof(struct rrset_ref) * (rep->rrset_count-1) +
@ -470,6 +472,10 @@ load_rrset(SSL* ssl, sldns_buffer* buf, struct worker* worker)
log_warn("bad rrset without contents");
return 0;
}
if(rr_count > RR_COUNT_MAX || rrsig_count > RR_COUNT_MAX) {
log_warn("bad rrset with too many rrs");
return 0;
}
d->count = (size_t)rr_count;
d->rrsig_count = (size_t)rrsig_count;
d->security = (enum sec_status)security;
@ -649,6 +655,10 @@ load_msg(SSL* ssl, sldns_buffer* buf, struct worker* worker)
rep.an_numrrsets = (size_t)an;
rep.ns_numrrsets = (size_t)ns;
rep.ar_numrrsets = (size_t)ar;
if(an > RR_COUNT_MAX || ns > RR_COUNT_MAX || ar > RR_COUNT_MAX) {
log_warn("error too many rrsets");
return 0; /* protect against integer overflow in alloc */
}
rep.rrset_count = (size_t)an+(size_t)ns+(size_t)ar;
rep.rrsets = (struct ub_packed_rrset_key**)regional_alloc_zero(
region, sizeof(struct ub_packed_rrset_key*)*rep.rrset_count);

View file

@ -590,6 +590,10 @@ dns64_synth_aaaa_data(const struct ub_packed_rrset_key* fk,
* for the RRs themselves. Each RR has a length, TTL, pointer to wireformat
* data, 2 bytes of data length, and 16 bytes of IPv6 address.
*/
if(fd->count > RR_COUNT_MAX) {
*dd_out = NULL;
return; /* integer overflow protection in alloc */
}
if (!(dd = *dd_out = regional_alloc(region,
sizeof(struct packed_rrset_data)
+ fd->count * (sizeof(size_t) + sizeof(time_t) +
@ -713,6 +717,8 @@ dns64_adjust_a(int id, struct module_qstate* super, struct module_qstate* qstate
if(i<rep->an_numrrsets && fk->rk.type == htons(LDNS_RR_TYPE_A)) {
/* also sets dk->entry.hash */
dns64_synth_aaaa_data(fk, fd, dk, &dd, super->region, dns64_env);
if(!dd)
return;
/* Delete negative AAAA record from cache stored by
* the iterator module */
rrset_cache_remove(super->env->rrset_cache, dk->rk.dname,

View file

@ -1,3 +1,6 @@
20 March 2015: Wouter
- Fixed to add integer overflow checks on allocation (defense in depth).
19 March 2015: Wouter
- Add ip-transparent config option for bind to non-local addresses.

View file

@ -308,6 +308,8 @@ iter_prepend(struct iter_qstate* iq, struct dns_msg* msg,
if(num_an + num_ns == 0)
return 1;
verbose(VERB_ALGO, "prepending %d rrsets", (int)num_an + (int)num_ns);
if(num_an > RR_COUNT_MAX || num_ns > RR_COUNT_MAX ||
msg->rep->rrset_count > RR_COUNT_MAX) return 0; /* overflow */
sets = regional_alloc(region, (num_an+num_ns+msg->rep->rrset_count) *
sizeof(struct ub_packed_rrset_key*));
if(!sets)
@ -2549,6 +2551,12 @@ processClassResponse(struct module_qstate* qstate, int id,
/* copy appropriate rcode */
to->rep->flags = from->rep->flags;
/* copy rrsets */
if(from->rep->rrset_count > RR_COUNT_MAX ||
to->rep->rrset_count > RR_COUNT_MAX) {
log_err("malloc failed (too many rrsets) in collect ANY");
foriq->state = FINISHED_STATE;
return; /* integer overflow protection */
}
dest = regional_alloc(forq->region, sizeof(dest[0])*n);
if(!dest) {
log_err("malloc failed in collect ANY");

View file

@ -366,6 +366,8 @@ dns_msg_create(uint8_t* qname, size_t qnamelen, uint16_t qtype,
sizeof(struct reply_info)-sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
if(capacity > RR_COUNT_MAX)
return NULL; /* integer overflow protection */
msg->rep->flags = BIT_QR; /* with QR, no AA */
msg->rep->qdcount = 1;
msg->rep->rrsets = (struct ub_packed_rrset_key**)
@ -453,6 +455,8 @@ gen_dns_msg(struct regional* region, struct query_info* q, size_t num)
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!msg->rep)
return NULL;
if(num > RR_COUNT_MAX)
return NULL; /* integer overflow protection */
msg->rep->rrsets = (struct ub_packed_rrset_key**)
regional_alloc(region,
num * sizeof(struct ub_packed_rrset_key*));

View file

@ -304,7 +304,7 @@ rrset_array_unlock_touch(struct rrset_cache* r, struct regional* scratch,
{
hashvalue_t* h;
size_t i;
if(!(h = (hashvalue_t*)regional_alloc(scratch,
if(count > RR_COUNT_MAX || !(h = (hashvalue_t*)regional_alloc(scratch,
sizeof(hashvalue_t)*count)))
log_warn("rrset LRU: memory allocation failed");
else /* store hash values */

View file

@ -915,7 +915,10 @@ read_data_chunk(SSL* ssl, size_t len)
{
size_t got = 0;
int r;
char* data = malloc(len+1);
char* data;
if(len >= 0xfffffff0)
return NULL; /* to protect against integer overflow in malloc*/
data = malloc(len+1);
if(!data) {
if(verb) printf("out of memory\n");
return NULL;

View file

@ -367,8 +367,12 @@ void *unbound_stat_malloc(size_t size)
/** calloc with stats */
void *unbound_stat_calloc(size_t nmemb, size_t size)
{
size_t s = (nmemb*size==0)?(size_t)1:nmemb*size;
void* res = calloc(1, s+16);
size_t s;
void* res;
if(INT_MAX/nmemb < size)
return NULL; /* integer overflow check */
s = (nmemb*size==0)?(size_t)1:nmemb*size;
res = calloc(1, s+16);
if(!res) return NULL;
log_info("stat %p=calloc(%u, %u)", res+16, (unsigned)nmemb, (unsigned)size);
unbound_mem_alloc += s;
@ -503,8 +507,12 @@ void *unbound_stat_malloc_lite(size_t size, const char* file, int line,
void *unbound_stat_calloc_lite(size_t nmemb, size_t size, const char* file,
int line, const char* func)
{
size_t req = nmemb * size;
void* res = malloc(req+lite_pad*2+sizeof(size_t));
size_t req;
void* res;
if(INT_MAX/nmemb < size)
return NULL; /* integer overflow check */
req = nmemb * size;
res = malloc(req+lite_pad*2+sizeof(size_t));
if(!res) return NULL;
memmove(res, lite_pre, lite_pad);
memmove(res+lite_pad, &req, sizeof(size_t));

View file

@ -87,7 +87,7 @@ construct_reply_info_base(struct regional* region, uint16_t flags, size_t qd,
/* rrset_count-1 because the first ref is part of the struct. */
size_t s = sizeof(struct reply_info) - sizeof(struct rrset_ref) +
sizeof(struct ub_packed_rrset_key*) * total;
if(total >= 0xffffff) return NULL; /* sanity check on numRRS*/
if(total >= RR_COUNT_MAX) return NULL; /* sanity check on numRRS*/
if(region)
rep = (struct reply_info*)regional_alloc(region, s);
else rep = (struct reply_info*)malloc(s +
@ -278,7 +278,11 @@ parse_create_rrset(sldns_buffer* pkt, struct rrset_parse* pset,
struct packed_rrset_data** data, struct regional* region)
{
/* allocate */
size_t s = sizeof(struct packed_rrset_data) +
size_t s;
if(pset->rr_count > RR_COUNT_MAX || pset->rrsig_count > RR_COUNT_MAX ||
pset->size > RR_COUNT_MAX)
return 0; /* protect against integer overflow */
s = sizeof(struct packed_rrset_data) +
(pset->rr_count + pset->rrsig_count) *
(sizeof(size_t)+sizeof(uint8_t*)+sizeof(time_t)) +
pset->size;

View file

@ -58,6 +58,12 @@ typedef uint64_t rrset_id_t;
* from the SOA in the answer section from a direct SOA query or ANY query. */
#define PACKED_RRSET_SOA_NEG 0x4
/** number of rrs and rrsets for integer overflow protection. More than
* this is not really possible (64K packet has much less RRs and RRsets) in
* a message. And this is small enough that also multiplied there is no
* integer overflow. */
#define RR_COUNT_MAX 0xffffff
/**
* The identifying information for an RRset.
*/

View file

@ -1079,6 +1079,8 @@ int rrset_canonical_equal(struct regional* region,
fd.rr_data = fdata;
rbtree_init(&sortree1, &canonical_tree_compare);
rbtree_init(&sortree2, &canonical_tree_compare);
if(d1->count > RR_COUNT_MAX || d2->count > RR_COUNT_MAX)
return 1; /* protection against integer overflow */
rrs1 = regional_alloc(region, sizeof(struct canon_rr)*d1->count);
rrs2 = regional_alloc(region, sizeof(struct canon_rr)*d2->count);
if(!rrs1 || !rrs2) return 1; /* alloc failure */
@ -1135,6 +1137,8 @@ rrset_canonical(struct regional* region, sldns_buffer* buf,
sizeof(rbtree_t));
if(!*sortree)
return 0;
if(d->count > RR_COUNT_MAX)
return 0; /* integer overflow protection */
rrs = regional_alloc(region, sizeof(struct canon_rr)*d->count);
if(!rrs) {
*sortree = NULL;

View file

@ -226,6 +226,8 @@ val_new_getmsg(struct module_qstate* qstate, struct val_qstate* vq)
sizeof(struct reply_info) - sizeof(struct rrset_ref));
if(!vq->chase_reply)
return NULL;
if(vq->orig_msg->rep->rrset_count > RR_COUNT_MAX)
return NULL; /* protect against integer overflow */
vq->chase_reply->rrsets = regional_alloc_init(qstate->region,
vq->orig_msg->rep->rrsets, sizeof(struct ub_packed_rrset_key*)
* vq->orig_msg->rep->rrset_count);