From: Evan Hunt Date: Mon, 6 May 2013 22:40:40 +0000 (-0700) Subject: [master] handle corrupted pointers in map files X-Git-Tag: v9.10.0a1~357 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d9f0c713fe1d50f1848ca827c5f31db79d904f04;p=thirdparty%2Fbind9.git [master] handle corrupted pointers in map files 3570. [bug] Check internal pointers are valid when loading map files. [RT #33403] --- diff --git a/CHANGES b/CHANGES index 03b67752426..0a864bdfa90 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3570. [bug] Check internal pointers are valid when loading map + files. [RT #33403] + 3569. [contrib] Ported mysql DLZ driver to dynamically-loadable module, and added multithread support. [RT #33394] diff --git a/lib/dns/include/dns/rbt.h b/lib/dns/include/dns/rbt.h index bc34421486c..85976506086 100644 --- a/lib/dns/include/dns/rbt.h +++ b/lib/dns/include/dns/rbt.h @@ -156,7 +156,9 @@ typedef isc_result_t (*dns_rbtdatawriter_t)(FILE *file, isc_uint32_t version, isc_sha1_t *sha1); -typedef void (*dns_rbtdatafixer_t)(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1); +typedef isc_result_t (*dns_rbtdatafixer_t)(dns_rbtnode_t *rbtnode, + void *base, size_t offset, + isc_sha1_t *sha1); /***** ***** Chain Info @@ -706,8 +708,8 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt, */ isc_result_t -dns_rbt_deserialize_tree(void *base_address, off_t header_offset, - isc_mem_t *mctx, +dns_rbt_deserialize_tree(void *base_address, size_t filesize, + off_t header_offset, isc_mem_t *mctx, void (*deleter)(void *, void *), void *deleter_arg, dns_rbtdatafixer_t datafixer, dns_rbtnode_t **originp, dns_rbt_t **rbtp); diff --git a/lib/dns/rbt.c b/lib/dns/rbt.c index 4206bcb55dc..007f722b949 100644 --- a/lib/dns/rbt.c +++ b/lib/dns/rbt.c @@ -365,8 +365,9 @@ addonlevel(dns_rbtnode_t *node, dns_rbtnode_t *current, int order, static void deletefromlevel(dns_rbtnode_t *delete, dns_rbtnode_t **rootp); -static void -treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name, +static isc_result_t +treefix(dns_rbt_t *rbt, void *base, size_t size, + dns_rbtnode_t *n, dns_name_t *name, dns_rbtdatafixer_t datafixer, isc_sha1_t *sha1); static isc_result_t @@ -684,19 +685,26 @@ dns_rbt_serialize_tree(FILE *file, dns_rbt_t *rbt, return (result); } -static void -treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name, - dns_rbtdatafixer_t datafixer, isc_sha1_t *sha1) +#define CONFIRM(a) do { \ + if (! (a)) { \ + result = ISC_R_INVALIDFILE; \ + goto cleanup; \ + } \ +} while(0); + +static isc_result_t +treefix(dns_rbt_t *rbt, void *base, size_t filesize, dns_rbtnode_t *n, + dns_name_t *name, dns_rbtdatafixer_t datafixer, isc_sha1_t *sha1) { - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; dns_fixedname_t fixed; dns_name_t nodename, *fullname; unsigned char *node_data; dns_rbtnode_t header; - size_t datasize; + size_t datasize, nodemax = filesize - sizeof(dns_rbtnode_t); if (n == NULL) - return; + return (ISC_R_SUCCESS); dns_name_init(&nodename, NULL); NODENAME(n, &nodename); @@ -705,47 +713,66 @@ treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name, if (!dns_name_isabsolute(&nodename)) { dns_fixedname_init(&fixed); fullname = dns_fixedname_name(&fixed); - result = dns_name_concatenate(&nodename, name, fullname, NULL); - INSIST(result == ISC_R_SUCCESS); - /* XXX: we need to catch errors better than this */ + CHECK(dns_name_concatenate(&nodename, name, fullname, NULL)); } /* memorize header contents prior to fixup */ memcpy(&header, n, sizeof(header)); - INSIST(!n->parent_is_relative || n->parent != NULL); - INSIST(!n->right_is_relative || n->right != NULL); - INSIST(!n->left_is_relative || n->left != NULL); - INSIST(!n->down_is_relative || n->down != NULL); - INSIST(!n->data_is_relative || n->data != NULL); - - n->right = getright(n, rbt->mmap_location); - n->right_is_relative = 0; + if (n->left_is_relative) { + CONFIRM(n->left <= (dns_rbtnode_t *) nodemax); + n->left = getleft(n, rbt->mmap_location); + n->left_is_relative = 0; + CONFIRM(DNS_RBTNODE_VALID(n->left)); + } else + CONFIRM(n->left == NULL); - n->left = getleft(n, rbt->mmap_location); - n->left_is_relative = 0; + if (n->right_is_relative) { + CONFIRM(n->right <= (dns_rbtnode_t *) nodemax); + n->right = getright(n, rbt->mmap_location); + n->right_is_relative = 0; + CONFIRM(DNS_RBTNODE_VALID(n->right)); + } else + CONFIRM(n->right == NULL); - n->down = getdown(n, rbt->mmap_location); - n->down_is_relative = 0; + if (n->down_is_relative) { + CONFIRM(n->down <= (dns_rbtnode_t *) nodemax); + n->down = getdown(n, rbt->mmap_location); + n->down_is_relative = 0; + CONFIRM(DNS_RBTNODE_VALID(n->down)); + } else + CONFIRM(n->down == NULL); - n->parent = getparent(n, rbt->mmap_location); - n->parent_is_relative = 0; + if (n->parent_is_relative) { + CONFIRM(n->parent <= (dns_rbtnode_t *) nodemax); + n->parent = getparent(n, rbt->mmap_location); + n->parent_is_relative = 0; + CONFIRM(DNS_RBTNODE_VALID(n->parent)); + } else + CONFIRM(n->parent == NULL); - n->data = getdata(n, rbt->mmap_location); - n->data_is_relative = 0; + if (n->data_is_relative) { + CONFIRM(n->data <= (void *) filesize); + n->data = getdata(n, rbt->mmap_location); + n->data_is_relative = 0; + } else + CONFIRM(n->data == NULL); hash_node(rbt, n, fullname); /* a change in the order (from left, right, down) will break hashing*/ if (n->left != NULL) - treefix(rbt, n->left, name, datafixer, sha1); + CHECK(treefix(rbt, base, filesize, n->left, name, + datafixer, sha1)); if (n->right != NULL) - treefix(rbt, n->right, name, datafixer, sha1); + CHECK(treefix(rbt, base, filesize, n->right, name, + datafixer, sha1)); if (n->down != NULL) - treefix(rbt, n->down, fullname, datafixer, sha1); + CHECK(treefix(rbt, base, filesize, n->down, fullname, + datafixer, sha1)); if (datafixer != NULL && n->data != NULL) - datafixer(n, sha1); + CHECK(datafixer(n, base, filesize, sha1)); node_data = (unsigned char *) n + sizeof(dns_rbtnode_t); datasize = NODE_SIZE(n) - sizeof(dns_rbtnode_t); @@ -762,29 +789,30 @@ treefix(dns_rbt_t *rbt, dns_rbtnode_t *n, dns_name_t *name, sizeof(dns_rbtnode_t)); isc_sha1_update(sha1, (const isc_uint8_t *) node_data, datasize); + + cleanup: + return (result); } isc_result_t -dns_rbt_deserialize_tree(void *base_address, off_t header_offset, - isc_mem_t *mctx, +dns_rbt_deserialize_tree(void *base_address, size_t filesize, + off_t header_offset, isc_mem_t *mctx, void (*deleter)(void *, void *), void *deleter_arg, dns_rbtdatafixer_t datafixer, dns_rbtnode_t **originp, dns_rbt_t **rbtp) { - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; file_header_t *header; unsigned char digest[ISC_SHA1_DIGESTLENGTH]; - isc_sha1_t sha1; dns_rbt_t *rbt = NULL; + isc_sha1_t sha1; REQUIRE(originp == NULL || *originp == NULL); isc_sha1_init(&sha1); - result = dns_rbt_create(mctx, deleter, deleter_arg, &rbt); - if (result != ISC_R_SUCCESS) - return (result); + CHECK(dns_rbt_create(mctx, deleter, deleter_arg, &rbt)); rbt->mmap_location = base_address; @@ -812,25 +840,33 @@ dns_rbt_deserialize_tree(void *base_address, off_t header_offset, rbt->root = (dns_rbtnode_t *)((char *)base_address + header_offset + header->first_node_offset); rbt->nodecount = header->nodecount; - treefix(rbt, rbt->root, dns_rootname, datafixer, &sha1); + + CHECK(treefix(rbt, base_address, filesize, rbt->root, + dns_rootname, datafixer, &sha1)); isc_sha1_final(&sha1, digest); #ifdef DEBUG hexdump("deserializing digest", digest, sizeof(digest)); #endif + /* Check file hash */ if (memcmp(header->digest, digest, sizeof(digest)) != 0) { - rbt->root = NULL; - rbt->nodecount = 0; - dns_rbt_destroy(&rbt); - return (ISC_R_INVALIDFILE); + result = ISC_R_INVALIDFILE; + goto cleanup; } *rbtp = rbt; if (originp != NULL) *originp = rbt->root; - return (ISC_R_SUCCESS); + cleanup: + if (result != ISC_R_SUCCESS) { + rbt->root = NULL; + rbt->nodecount = 0; + dns_rbt_destroy(&rbt); + } + + return (result); } /* diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 34898decf8c..950e2cdcf9d 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -6995,9 +6995,12 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) { return (result); } -static void -rbt_datafixer(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1) { +static isc_result_t +rbt_datafixer(dns_rbtnode_t *rbtnode, void *base, size_t filesize, + isc_sha1_t *sha1) +{ rdatasetheader_t *header; + unsigned char *limit = ((unsigned char *) base) + filesize; unsigned char *p; size_t size; @@ -7005,6 +7008,9 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1) { for (header = rbtnode->data; header != NULL; header = header->next) { p = (unsigned char *) header; + if (p == NULL) + return (ISC_R_INVALIDFILE); + size = dns_rdataslab_size(p, sizeof(*header)); isc_sha1_update(sha1, p, size); #ifdef DEBUG @@ -7019,8 +7025,13 @@ rbt_datafixer(dns_rbtnode_t *rbtnode, isc_sha1_t *sha1) { if (header->next != NULL) { header->next = (rdatasetheader_t *)(p + size); header->next_is_relative = 0; + if ((header->next < (rdatasetheader_t *) base) || + (header->next > (rdatasetheader_t *) limit)) + return (ISC_R_INVALIDFILE); } } + + return (ISC_R_SUCCESS); } /* @@ -7066,7 +7077,8 @@ deserialize(void *arg, FILE *f, off_t offset) { rbtdb->origin_node = NULL; if (header->tree != 0) { - result = dns_rbt_deserialize_tree(base, header->tree, + result = dns_rbt_deserialize_tree(base, filesize, + header->tree, rbtdb->common.mctx, delete_callback, rbtdb, rbt_datafixer, @@ -7085,7 +7097,8 @@ deserialize(void *arg, FILE *f, off_t offset) { } if (header->nsec != 0) { - result = dns_rbt_deserialize_tree(base, header->nsec, + result = dns_rbt_deserialize_tree(base, filesize, + header->nsec, rbtdb->common.mctx, delete_callback, rbtdb, rbt_datafixer, @@ -7100,7 +7113,8 @@ deserialize(void *arg, FILE *f, off_t offset) { } if (header->nsec3 != 0) { - result = dns_rbt_deserialize_tree(base, header->nsec3, + result = dns_rbt_deserialize_tree(base, filesize, + header->nsec3, rbtdb->common.mctx, delete_callback, rbtdb, rbt_datafixer, diff --git a/lib/dns/tests/rbt_test.c b/lib/dns/tests/rbt_test.c index 4a54f28ba6a..577ed49ee7e 100644 --- a/lib/dns/tests/rbt_test.c +++ b/lib/dns/tests/rbt_test.c @@ -132,10 +132,13 @@ write_data(FILE *file, unsigned char *datap, isc_uint32_t serial, return (ISC_R_SUCCESS); } -static void -fix_data(dns_rbtnode_t *p, isc_sha1_t *sha1) { +static isc_result_t +fix_data(dns_rbtnode_t *p, void *base, size_t max, isc_sha1_t *sha1) { data_holder_t *data = p->data; + UNUSED(base); + UNUSED(max); + REQUIRE(data != NULL); REQUIRE((data->len == 0 && data->data == NULL) || (data->len != 0 && data->data != NULL)); @@ -147,6 +150,8 @@ fix_data(dns_rbtnode_t *p, isc_sha1_t *sha1) { data->data = (data->len == 0) ? NULL : (char *)data + sizeof(data_holder_t); + + return (ISC_R_SUCCESS); } /* @@ -322,7 +327,8 @@ ATF_TC_BODY(isc_serialize_rbt, tc) { MAP_FILE|MAP_PRIVATE, fd, 0); ATF_REQUIRE(base != NULL && base != MAP_FAILED); - result = dns_rbt_deserialize_tree(base, 0, mctx, delete_data, NULL, + result = dns_rbt_deserialize_tree(base, filesize, 0, mctx, + delete_data, NULL, fix_data, NULL, &rbt_deserialized); /* Test to make sure we have a valid tree */