]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] handle corrupted pointers in map files
authorEvan Hunt <each@isc.org>
Mon, 6 May 2013 22:40:40 +0000 (15:40 -0700)
committerEvan Hunt <each@isc.org>
Mon, 6 May 2013 22:40:40 +0000 (15:40 -0700)
3570. [bug] Check internal pointers are valid when loading map
                        files. [RT #33403]

CHANGES
lib/dns/include/dns/rbt.h
lib/dns/rbt.c
lib/dns/rbtdb.c
lib/dns/tests/rbt_test.c

diff --git a/CHANGES b/CHANGES
index 03b67752426a557a3a36a09acb7437e9ca710baf..0a864bdfa90374baf5ff339060625fe693e3f20f 100644 (file)
--- 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]
 
index bc34421486cbdc33219b7d97c0cb3c1dcfbec8ec..85976506086106c38998428fd07fc4d0daaa3411 100644 (file)
@@ -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);
index 4206bcb55dc3ca81ffe73bdf5333bdd9bd31461a..007f722b949236e5552bff29b11f2f1b1b009a20 100644 (file)
@@ -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);
 }
 
 /*
index 34898decf8cbd265443e950630b3fa588cee36a1..950e2cdcf9d869ee3d255caeb3235196050827eb 100644 (file)
@@ -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,
index 4a54f28ba6aac65a443ccf34a1be6ce5b73f8e75..577ed49ee7ed7c09b7b6b35eda40a8720aa9cde1 100644 (file)
@@ -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 */