Browse Source

[2107] make NSEC3Data::create() exception safe.

JINMEI Tatuya 12 years ago
parent
commit
e21efca5f3

+ 37 - 26
src/lib/datasrc/memory/tests/zone_data_unittest.cc

@@ -40,6 +40,43 @@ using namespace isc::testutils;
 
 namespace {
 
+class NSEC3DataTest : public ::testing::Test {
+protected:
+    NSEC3DataTest() : param_rdata_("1 0 12 aabbccdd")
+    {}
+    void TearDown() {
+        // detect any memory leak in the test memory segment
+        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
+    }
+
+    MemorySegmentTest mem_sgmt_;
+    NSEC3Data* nsec3_data_;
+    const generic::NSEC3PARAM param_rdata_;
+};
+
+TEST_F(NSEC3DataTest, create) {
+    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
+    EXPECT_EQ(0, nsec3_data_->getNSEC3Tree()->getNodeCount());
+    EXPECT_EQ(1, nsec3_data_->hashalg);
+    EXPECT_EQ(0, nsec3_data_->flags);
+    EXPECT_EQ(12, nsec3_data_->iterations);
+    EXPECT_EQ(param_rdata_.getSalt().size(), nsec3_data_->getSaltLen());
+    EXPECT_EQ(0, memcmp(&param_rdata_.getSalt()[0], nsec3_data_->getSaltData(),
+                        param_rdata_.getSalt().size()));
+    NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
+}
+
+TEST_F(NSEC3DataTest, throwOnCreate) {
+    // Note: below, we use our knowledge of how memory allocation happens
+    // within the NSEC3Data.
+
+    // Creating internal NSEC3 tree will succeed, but allocation of NSEC3Data
+    // will fail due to bad_alloc.  It shouldn't cause memory leak
+    // (that would be caught in TearDown()).
+    mem_sgmt_.setThrowCount(2);
+    EXPECT_THROW(NSEC3Data::create(mem_sgmt_, param_rdata_), std::bad_alloc);
+}
+
 class ZoneDataTest : public ::testing::Test {
 protected:
     ZoneDataTest() : zname_("example.com"),
@@ -105,30 +142,4 @@ TEST_F(ZoneDataTest, addRdataSets) {
 
     // TearDown() will confirm there's no leak on destroy
 }
-
-class NSEC3DataTest : public ::testing::Test {
-protected:
-    NSEC3DataTest() : param_rdata_("1 0 12 aabbccdd")
-    {}
-    void TearDown() {
-        // detect any memory leak in the test memory segment
-        EXPECT_TRUE(mem_sgmt_.allMemoryDeallocated());
-    }
-
-    MemorySegmentTest mem_sgmt_;
-    NSEC3Data* nsec3_data_;
-    const generic::NSEC3PARAM param_rdata_;
-};
-
-TEST_F(NSEC3DataTest, create) {
-    nsec3_data_ = NSEC3Data::create(mem_sgmt_, param_rdata_);
-    EXPECT_EQ(0, nsec3_data_->getNSEC3Tree()->getNodeCount());
-    EXPECT_EQ(1, nsec3_data_->hashalg);
-    EXPECT_EQ(0, nsec3_data_->flags);
-    EXPECT_EQ(12, nsec3_data_->iterations);
-    EXPECT_EQ(param_rdata_.getSalt().size(), nsec3_data_->getSaltLen());
-    EXPECT_EQ(0, memcmp(&param_rdata_.getSalt()[0], nsec3_data_->getSaltData(),
-                        param_rdata_.getSalt().size()));
-    NSEC3Data::destroy(mem_sgmt_, nsec3_data_, RRClass::IN());
-}
 }

+ 12 - 7
src/lib/datasrc/memory/zone_data.cc

@@ -58,14 +58,21 @@ NSEC3Data*
 NSEC3Data::create(util::MemorySegment& mem_sgmt,
                   const generic::NSEC3PARAM& rdata)
 {
-    ZoneTree* tree = ZoneTree::create(mem_sgmt, true);
+    // NSEC3Data allocation can throw.  To avoid leaking the tree, we manage
+    // it in the holder.
+    // Note: we won't add any RdataSet, so we use the NO-OP deleter
+    // (with an assertion check for that).
+    typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
+    detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
+        mem_sgmt, ZoneTree::create(mem_sgmt, true),
+        boost::bind(nullDeleter, _1));
 
     const size_t salt_len = rdata.getSalt().size();
 
     void* p = mem_sgmt.allocate(sizeof(NSEC3Data) + salt_len + 1);
     NSEC3Data* const param_data =
-        new(p) NSEC3Data(tree, rdata.getHashalg(), rdata.getFlags(),
-                         rdata.getIterations());
+        new(p) NSEC3Data(holder.release(), rdata.getHashalg(),
+                         rdata.getFlags(), rdata.getIterations());
     uint8_t* dp = param_data->getSaltBuf();
     *dp++ =  salt_len;
     memcpy(dp, &rdata.getSalt().at(0), salt_len); // use at for safety
@@ -86,10 +93,8 @@ NSEC3Data::destroy(util::MemorySegment& mem_sgmt, NSEC3Data* data,
 
 ZoneData*
 ZoneData::create(util::MemorySegment& mem_sgmt, const Name& zone_origin) {
-    // ZoneTree::insert() and ZoneData allocation can throw.  To avoid
-    // leaking the tree, we manage it in the holder.
-    // Note: we won't add any RdataSet, so we use the NO-OP deleter
-    // (with an assertion check for that).
+    // ZoneTree::insert() and ZoneData allocation can throw.  See also
+    // NSEC3Data::create().
     typedef boost::function<void(RdataSet*)> RdataSetDeleterType;
     detail::SegmentObjectHolder<ZoneTree, RdataSetDeleterType> holder(
         mem_sgmt, ZoneTree::create(mem_sgmt, true),