summaryrefslogtreecommitdiff
path: root/chromium/components/zucchini
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/zucchini
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/zucchini')
-rw-r--r--chromium/components/zucchini/BUILD.gn6
-rw-r--r--chromium/components/zucchini/disassembler_elf.cc25
-rw-r--r--chromium/components/zucchini/fuzzers/BUILD.gn46
-rw-r--r--chromium/components/zucchini/reloc_elf.cc8
-rw-r--r--chromium/components/zucchini/reloc_elf_unittest.cc203
5 files changed, 204 insertions, 84 deletions
diff --git a/chromium/components/zucchini/BUILD.gn b/chromium/components/zucchini/BUILD.gn
index 00bacd8d19a..54b06ab6500 100644
--- a/chromium/components/zucchini/BUILD.gn
+++ b/chromium/components/zucchini/BUILD.gn
@@ -207,9 +207,7 @@ test("zucchini_unittests") {
}
test("zucchini_integration_test") {
- sources = [
- "integration_test.cc",
- ]
+ sources = [ "integration_test.cc" ]
deps = [
":zucchini_lib",
@@ -218,6 +216,8 @@ test("zucchini_integration_test") {
"//base/test:test_support",
"//testing/gtest",
]
+
+ data = [ "testdata" ]
}
# Group to build and depend on all the Zucchini related fuzzers.
diff --git a/chromium/components/zucchini/disassembler_elf.cc b/chromium/components/zucchini/disassembler_elf.cc
index fdc104f2c14..4727d1cf697 100644
--- a/chromium/components/zucchini/disassembler_elf.cc
+++ b/chromium/components/zucchini/disassembler_elf.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/logging.h"
+#include "base/numerics/checked_math.h"
#include "base/numerics/safe_conversions.h"
#include "components/zucchini/abs32_utils.h"
#include "components/zucchini/algorithm.h"
@@ -44,6 +45,15 @@ enum SectionJudgement : int {
// from SectionJudgement values.
template <class Traits>
int JudgeSection(size_t image_size, const typename Traits::Elf_Shdr* section) {
+ // BufferRegion uses |size_t| this can be 32-bit in some cases. For Elf64
+ // |sh_addr|, |sh_offset| and |sh_size| are 64-bit this can result in
+ // overflows in the subsequent validation steps.
+ if (!base::IsValueInRangeForNumericType<size_t>(section->sh_addr) ||
+ !base::IsValueInRangeForNumericType<size_t>(section->sh_offset) ||
+ !base::IsValueInRangeForNumericType<size_t>(section->sh_size)) {
+ return SECTION_IS_MALFORMED;
+ }
+
// Examine RVA range: Reject if numerical overflow may happen.
if (!BufferRegion{section->sh_addr, section->sh_size}.FitsIn(kSizeBound))
return SECTION_IS_MALFORMED;
@@ -219,6 +229,9 @@ std::unique_ptr<ReferenceWriter> DisassemblerElf<Traits>::MakeWriteRelocs(
template <class Traits>
bool DisassemblerElf<Traits>::ParseHeader() {
BufferSource source(image_);
+ // Ensure any offsets will fit within the |image_|'s bounds.
+ if (!base::IsValueInRangeForNumericType<offset_t>(image_.size()))
+ return false;
// Ensures |header_| is valid later on.
if (!QuickDetect(image_))
@@ -263,10 +276,16 @@ bool DisassemblerElf<Traits>::ParseHeader() {
// Visits |segments_| to get estimate on |offset_bound|.
for (const typename Traits::Elf_Phdr* segment = segments_;
segment != segments_ + segments_count_; ++segment) {
- if (!image_.covers({segment->p_offset, segment->p_filesz}))
+ // |image_.covers()| is a sufficient check except when size_t is 32 bit and
+ // parsing ELF64. In such cases a value-in-range check is needed on the
+ // segment. This fixes crbug/1035603.
+ offset_t segment_end;
+ base::CheckedNumeric<offset_t> checked_segment_end = segment->p_offset;
+ checked_segment_end += segment->p_filesz;
+ if (!checked_segment_end.AssignIfValid(&segment_end) ||
+ !image_.covers({segment->p_offset, segment->p_filesz})) {
return false;
- offset_t segment_end =
- base::checked_cast<offset_t>(segment->p_offset + segment->p_filesz);
+ }
offset_bound = std::max(offset_bound, segment_end);
}
diff --git a/chromium/components/zucchini/fuzzers/BUILD.gn b/chromium/components/zucchini/fuzzers/BUILD.gn
index effc22daadb..90c436e8aca 100644
--- a/chromium/components/zucchini/fuzzers/BUILD.gn
+++ b/chromium/components/zucchini/fuzzers/BUILD.gn
@@ -21,9 +21,7 @@ static_library("zucchini_fuzz_utils") {
# gs://clusterfuzz-corpus/libfuzzer/zucchini_disassembler_dex_fuzzer \
# components/zucchini/fuzzing/testdata/disassembler_dex_fuzzer/
fuzzer_test("zucchini_disassembler_dex_fuzzer") {
- sources = [
- "disassembler_dex_fuzzer.cc",
- ]
+ sources = [ "disassembler_dex_fuzzer.cc" ]
deps = [
"//base",
"//components/zucchini:zucchini_lib",
@@ -35,9 +33,7 @@ fuzzer_test("zucchini_disassembler_dex_fuzzer") {
# gs://clusterfuzz-corpus/libfuzzer/zucchini_disassembler_win32_fuzzer \
# components/zucchini/fuzzing/testdata/disassembler_win32_fuzzer/
fuzzer_test("zucchini_disassembler_win32_fuzzer") {
- sources = [
- "disassembler_win32_fuzzer.cc",
- ]
+ sources = [ "disassembler_win32_fuzzer.cc" ]
deps = [
":zucchini_fuzz_utils",
"//base",
@@ -50,9 +46,7 @@ fuzzer_test("zucchini_disassembler_win32_fuzzer") {
# gs://clusterfuzz-corpus/libfuzzer/zucchini_disassembler_elf_fuzzer \
# components/zucchini/fuzzing/testdata/disassembler_elf_fuzzer/
fuzzer_test("zucchini_disassembler_elf_fuzzer") {
- sources = [
- "disassembler_elf_fuzzer.cc",
- ]
+ sources = [ "disassembler_elf_fuzzer.cc" ]
deps = [
":zucchini_fuzz_utils",
"//base",
@@ -61,9 +55,7 @@ fuzzer_test("zucchini_disassembler_elf_fuzzer") {
}
fuzzer_test("zucchini_patch_fuzzer") {
- sources = [
- "patch_fuzzer.cc",
- ]
+ sources = [ "patch_fuzzer.cc" ]
deps = [
"//base",
"//components/zucchini:zucchini_lib",
@@ -72,9 +64,7 @@ fuzzer_test("zucchini_patch_fuzzer") {
}
proto_library("zucchini_file_pair_proto") {
- sources = [
- "file_pair.proto",
- ]
+ sources = [ "file_pair.proto" ]
}
# Ensure protoc is available.
@@ -108,9 +98,8 @@ if (current_toolchain == host_toolchain && !is_win) {
]
# Outputs: necessary for validation.
- outputs = [
- "$target_gen_dir/testdata/apply_fuzzer/raw_apply_seed_proto.bin",
- ]
+ outputs =
+ [ "$target_gen_dir/testdata/apply_fuzzer/raw_apply_seed_proto.bin" ]
deps = [
"//components/zucchini:zucchini",
"//third_party/protobuf:protoc",
@@ -144,9 +133,8 @@ if (current_toolchain == host_toolchain && !is_win) {
]
# Outputs: necessary for validation.
- outputs = [
- "$target_gen_dir/testdata/apply_fuzzer/ztf_apply_seed_proto.bin",
- ]
+ outputs =
+ [ "$target_gen_dir/testdata/apply_fuzzer/ztf_apply_seed_proto.bin" ]
deps = [
"//components/zucchini:zucchini",
"//third_party/protobuf:protoc",
@@ -155,9 +143,7 @@ if (current_toolchain == host_toolchain && !is_win) {
# Apply Fuzzer:
fuzzer_test("zucchini_apply_fuzzer") {
- sources = [
- "apply_fuzzer.cc",
- ]
+ sources = [ "apply_fuzzer.cc" ]
deps = [
":zucchini_file_pair_proto",
"//base",
@@ -180,9 +166,7 @@ if (current_toolchain == host_toolchain && !is_win) {
# <new file>: testdata/new.ztf
# <out file>: testdata/raw_or_ztf_gen_fuzzer/seed.asciipb
fuzzer_test("zucchini_raw_gen_fuzzer") {
- sources = [
- "raw_gen_fuzzer.cc",
- ]
+ sources = [ "raw_gen_fuzzer.cc" ]
deps = [
":zucchini_file_pair_proto",
"//base",
@@ -197,9 +181,7 @@ if (current_toolchain == host_toolchain && !is_win) {
# <new file>: testdata/new.ztf
# <out file>: testdata/raw_or_ztf_gen_fuzzer/seed.asciipb
fuzzer_test("zucchini_ztf_gen_fuzzer") {
- sources = [
- "ztf_gen_fuzzer.cc",
- ]
+ sources = [ "ztf_gen_fuzzer.cc" ]
deps = [
":zucchini_file_pair_proto",
"//base",
@@ -216,9 +198,7 @@ if (current_toolchain == host_toolchain && !is_win) {
# <imposed>: 17+420=388+347,452+420=27+347
# This is a mapping of regions old_offset+old_size=new_offset+new_size,...
fuzzer_test("zucchini_imposed_ensemble_matcher_fuzzer") {
- sources = [
- "imposed_ensemble_matcher_fuzzer.cc",
- ]
+ sources = [ "imposed_ensemble_matcher_fuzzer.cc" ]
deps = [
":zucchini_file_pair_proto",
"//base",
diff --git a/chromium/components/zucchini/reloc_elf.cc b/chromium/components/zucchini/reloc_elf.cc
index eef1c96b1e8..d4857e86c47 100644
--- a/chromium/components/zucchini/reloc_elf.cc
+++ b/chromium/components/zucchini/reloc_elf.cc
@@ -119,10 +119,14 @@ base::Optional<Reference> RelocReaderElf::GetNext() {
}
if (target_rva == kInvalidRva)
continue;
+ // TODO(huangs): Make the check more strict: The reference body should not
+ // straddle section boundary.
+ offset_t target = target_rva_to_offset_.Convert(target_rva);
+ if (target == kInvalidOffset)
+ continue;
// |target| will be used to obtain abs32 references, so we must ensure that
// it lies inside |image_|.
- offset_t target = target_rva_to_offset_.Convert(target_rva);
- if (target == kInvalidOffset || !image_.covers({target, sizeof(offset_t)}))
+ if (!image_.covers({target, WidthOf(bitness_)}))
continue;
offset_t location = cursor_;
cursor_ += cur_entry_size;
diff --git a/chromium/components/zucchini/reloc_elf_unittest.cc b/chromium/components/zucchini/reloc_elf_unittest.cc
index 5f34ca74e05..acbc1863c9f 100644
--- a/chromium/components/zucchini/reloc_elf_unittest.cc
+++ b/chromium/components/zucchini/reloc_elf_unittest.cc
@@ -8,11 +8,13 @@
#include <algorithm>
#include <memory>
+#include <utility>
#include <vector>
#include "base/numerics/safe_conversions.h"
#include "components/zucchini/address_translator.h"
#include "components/zucchini/algorithm.h"
+#include "components/zucchini/disassembler_elf.h"
#include "components/zucchini/image_utils.h"
#include "components/zucchini/test_utils.h"
#include "components/zucchini/type_elf.h"
@@ -45,79 +47,194 @@ SectionDimensionsElf MakeSectionDimensions(const BufferRegion& region,
}};
}
+// Helper to manipulate an image with one or more relocation tables.
+template <class ElfIntelTraits>
+class FakeImageWithReloc {
+ public:
+ struct RelocSpec {
+ offset_t start;
+ std::vector<uint8_t> data;
+ };
+
+ FakeImageWithReloc(size_t image_size,
+ rva_t base_rva,
+ const std::vector<RelocSpec>& reloc_specs)
+ : image_data_(image_size, 0xFF),
+ mutable_image_(&image_data_[0], image_data_.size()) {
+ translator_.Initialize({{0, image_size, base_rva, image_size}});
+ // Set up test image with reloc sections.
+ for (const RelocSpec& reloc_spec : reloc_specs) {
+ BufferRegion reloc_region = {reloc_spec.start, reloc_spec.data.size()};
+ std::copy(reloc_spec.data.begin(), reloc_spec.data.end(),
+ image_data_.begin() + reloc_region.lo());
+ section_dimensions_.emplace_back(
+ MakeSectionDimensions<typename ElfIntelTraits::Elf_Shdr>(
+ reloc_region, ElfIntelTraits::kVAWidth));
+ reloc_regions_.push_back(reloc_region);
+ }
+ }
+
+ std::vector<Reference> ExtractRelocReferences() {
+ const size_t image_size = image_data_.size();
+ ConstBufferView image = {image_data_.data(), image_size};
+
+ // Make RelocReaderElf.
+ auto reader = std::make_unique<RelocReaderElf>(
+ image, ElfIntelTraits::kBitness, section_dimensions_,
+ ElfIntelTraits::kRelType, 0, image_size, translator_);
+
+ // Read all references and check.
+ std::vector<Reference> refs;
+ for (base::Optional<Reference> ref = reader->GetNext(); ref.has_value();
+ ref = reader->GetNext()) {
+ refs.push_back(ref.value());
+ }
+ return refs;
+ }
+
+ std::unique_ptr<RelocWriterElf> MakeRelocWriter() {
+ return std::move(std::make_unique<RelocWriterElf>(
+ mutable_image_, ElfIntelTraits::kBitness, translator_));
+ }
+
+ std::vector<uint8_t> GetRawRelocData(int reloc_index) {
+ BufferRegion reloc_region = reloc_regions_[reloc_index];
+ return Sub(image_data_, reloc_region.lo(), reloc_region.hi());
+ }
+
+ private:
+ std::vector<uint8_t> image_data_;
+ MutableBufferView mutable_image_;
+ std::vector<BufferRegion> reloc_regions_;
+ std::vector<SectionDimensionsElf> section_dimensions_;
+ AddressTranslator translator_;
+};
+
} // namespace
TEST(RelocElfTest, ReadWrite32) {
// Set up mock image: Size = 0x3000, .reloc at 0x600. RVA is 0x40000 + offset.
+ constexpr size_t kImageSize = 0x3000;
constexpr rva_t kBaseRva = 0x40000;
- std::vector<uint8_t> image_data(0x3000, 0xFF);
+
+ constexpr offset_t kRelocStart0 = 0x600;
// "C0 10 04 00 08 00 00 00" represents
// (r_sym, r_type, r_offset) = (0x000000, 0x08, 0x000410C0).
- // r_type = 0x08 = R_386_RELATIVE, and under this r_offset is an RVA
- // 0x000410C0. Zucchini does not care about r_sym.
- std::vector<uint8_t> reloc_data1 = ParseHexString(
+ // r_type = 0x08 = R_386_RELATIVE, and so |r_offset| is an RVA 0x000410C0.
+ // Zucchini does not care about |r_sym|.
+ std::vector<uint8_t> reloc_data0 = ParseHexString(
"C0 10 04 00 08 00 00 00 " // R_386_RELATIVE.
"F8 10 04 00 08 AB CD EF " // R_386_RELATIVE.
"00 10 04 00 00 AB CD EF " // R_386_NONE.
"00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT.
- BufferRegion reloc_region1 = {0x600, reloc_data1.size()};
- std::copy(reloc_data1.begin(), reloc_data1.end(),
- image_data.begin() + reloc_region1.lo());
- std::vector<uint8_t> reloc_data2 = ParseHexString(
+ constexpr offset_t kRelocStart1 = 0x620;
+ std::vector<uint8_t> reloc_data1 = ParseHexString(
"BC 20 04 00 08 00 00 00 " // R_386_RELATIVE.
"A0 20 04 00 08 AB CD EF"); // R_386_RELATIVE.
- BufferRegion reloc_region2 = {0x620, reloc_data2.size()};
- std::copy(reloc_data2.begin(), reloc_data2.end(),
- image_data.begin() + reloc_region2.lo());
-
- ConstBufferView image = {image_data.data(), image_data.size()};
- offset_t image_size = base::checked_cast<offset_t>(image_data.size());
-
- AddressTranslator translator;
- translator.Initialize({{0, image_size, kBaseRva, image_size}});
-
- std::vector<SectionDimensionsElf> section_dimensions{
- MakeSectionDimensions<elf::Elf32_Shdr>(reloc_region1, 8),
- MakeSectionDimensions<elf::Elf32_Shdr>(reloc_region2, 8)};
-
- // Make RelocReaderElf.
- auto reader = std::make_unique<RelocReaderElf>(
- image, kBit32, section_dimensions, elf::R_386_RELATIVE, 0, image_size,
- translator);
-
- // Read all references and check.
- std::vector<Reference> refs;
- for (base::Optional<Reference> ref = reader->GetNext(); ref.has_value();
- ref = reader->GetNext()) {
- refs.push_back(ref.value());
- }
+
+ FakeImageWithReloc<Elf32IntelTraits> fake_image(
+ kImageSize, kBaseRva,
+ {{kRelocStart0, reloc_data0}, {kRelocStart1, reloc_data1}});
+
// Only R_386_RELATIVE references are extracted. Targets are translated from
// address (e.g., 0x000420BC) to offset (e.g., 0x20BC).
std::vector<Reference> exp_refs{
{0x600, 0x10C0}, {0x608, 0x10F8}, {0x620, 0x20BC}, {0x628, 0x20A0}};
- EXPECT_EQ(exp_refs, refs);
+ EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences());
// Write reference, extract bytes and check.
- MutableBufferView mutable_image(&image_data[0], image_data.size());
- auto writer =
- std::make_unique<RelocWriterElf>(mutable_image, kBit32, translator);
+ std::unique_ptr<RelocWriterElf> writer = fake_image.MakeRelocWriter();
writer->PutNext({0x608, 0x1F83});
- std::vector<uint8_t> exp_reloc_data1 = ParseHexString(
+ std::vector<uint8_t> exp_reloc_data0 = ParseHexString(
"C0 10 04 00 08 00 00 00 " // R_386_RELATIVE.
"83 1F 04 00 08 AB CD EF " // R_386_RELATIVE (address modified).
"00 10 04 00 00 AB CD EF " // R_386_NONE.
"00 10 04 00 07 AB CD EF"); // R_386_JMP_SLOT.
- EXPECT_EQ(exp_reloc_data1,
- Sub(image_data, reloc_region1.lo(), reloc_region1.hi()));
+ EXPECT_EQ(exp_reloc_data0, fake_image.GetRawRelocData(0));
writer->PutNext({0x628, 0x2950});
- std::vector<uint8_t> exp_reloc_data2 = ParseHexString(
+ std::vector<uint8_t> exp_reloc_data1 = ParseHexString(
"BC 20 04 00 08 00 00 00 " // R_386_RELATIVE.
"50 29 04 00 08 AB CD EF"); // R_386_RELATIVE (address modified).
- EXPECT_EQ(exp_reloc_data2,
- Sub(image_data, reloc_region2.lo(), reloc_region2.hi()));
+ EXPECT_EQ(exp_reloc_data1, fake_image.GetRawRelocData(1));
+}
+
+TEST(RelocElfTest, Limit32) {
+ constexpr size_t kImageSize = 0x3000;
+ constexpr offset_t kBaseRva = 0x40000;
+ constexpr offset_t kRelocStart = 0x600;
+ // All R_386_RELATIVE.
+ std::vector<uint8_t> reloc_data = ParseHexString(
+ // Strictly within file.
+ "00 00 04 00 08 00 00 00 "
+ "00 10 04 00 08 00 00 00 "
+ "F0 2F 04 00 08 00 00 00 "
+ "F8 2F 04 00 08 00 00 00 "
+ "FC 2F 04 00 08 00 00 00 "
+ // Straddles end of file.
+ "FD 2F 04 00 08 00 00 00 "
+ "FE 2F 04 00 08 00 00 00 "
+ "FF 2F 04 00 08 00 00 00 "
+ // Beyond end of file.
+ "00 30 04 00 08 00 00 00 "
+ "01 30 04 00 08 00 00 00 "
+ "FC FF FF 7F 08 00 00 00 "
+ "FE FF FF 7F 08 00 00 00 "
+ "00 00 00 80 08 00 00 00 "
+ "FC FF FF FF 08 00 00 00 "
+ "FF FF FF FF 08 00 00 00 "
+ // Another good reference.
+ "34 12 04 00 08 00 00 00");
+
+ FakeImageWithReloc<Elf32IntelTraits> fake_image(kImageSize, kBaseRva,
+ {{kRelocStart, reloc_data}});
+
+ std::vector<Reference> exp_refs{{0x600, 0x0000}, {0x608, 0x1000},
+ {0x610, 0x2FF0}, {0x618, 0x2FF8},
+ {0x620, 0x2FFC}, {0x678, 0x1234}};
+ EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences());
+}
+
+TEST(RelocElfTest, Limit64) {
+ constexpr size_t kImageSize = 0x3000;
+ constexpr offset_t kBaseRva = 0x40000;
+
+ constexpr offset_t kRelocStart = 0x600;
+ // All R_X86_64_RELATIVE.
+ std::vector<uint8_t> reloc_data = ParseHexString(
+ // Strictly within file.
+ "00 00 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "00 10 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "F0 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "F4 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "F8 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ // Straddles end of file.
+ "F9 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FC 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FF 2F 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ // Beyond end of file.
+ "00 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "01 30 04 00 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FC FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FE FF FF 7F 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "00 00 00 80 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FC FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "FF FF FF FF 00 00 00 00 08 00 00 00 00 00 00 00 "
+ "00 00 04 00 01 00 00 00 08 00 00 00 00 00 00 00 "
+ "FF FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 "
+ "F8 FF FF FF FF FF FF FF 08 00 00 00 00 00 00 00 "
+ // Another good reference.
+ "34 12 04 00 00 00 00 00 08 00 00 00 00 00 00 00");
+
+ FakeImageWithReloc<Elf64IntelTraits> fake_image(kImageSize, kBaseRva,
+ {{kRelocStart, reloc_data}});
+
+ std::vector<Reference> exp_refs{{0x600, 0x0000}, {0x610, 0x1000},
+ {0x620, 0x2FF0}, {0x630, 0x2FF4},
+ {0x640, 0x2FF8}, {0x720, 0x1234}};
+ EXPECT_EQ(exp_refs, fake_image.ExtractRelocReferences());
}
} // namespace zucchini