Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions deflate/deflate_memory_safety_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include <string>
#include <utility>

#include "flate2.h"
#include "gzip_wrapper.h"
#include "testing/base/public/gmock.h"
#include "testing/base/public/gunit.h"
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"

namespace security::deflate {
namespace {

std::string CreateSampleCompressedGzip() {
std::string input =
absl::StrCat("Hello FFI Memory Safety! ", std::string(200, 'X'));
auto wrapper = CompressGzip(input, 6);
CHECK_OK(wrapper.status());
return std::string(wrapper->as_string_view());
}

// ============================================================================
// PROOFS OF ABSENCE: Baseline Safe Lifecycle Invariants (Enabled)
// ============================================================================

TEST(DeflateMemorySafetyTest, Proof_Of_Absence_Lvalue_VecU8Wrapper_Safe) {
std::string compressed = CreateSampleCompressedGzip();
ASSERT_OK_AND_ASSIGN(VecU8Wrapper decompressed_wrapper,
UncompressGzip(compressed));

absl::string_view view = decompressed_wrapper.as_string_view();
EXPECT_FALSE(view.empty());
EXPECT_THAT(view, testing::StartsWith("Hello FFI Memory Safety!"));
}

TEST(DeflateMemorySafetyTest, Proof_Of_Absence_Rvalue_Cord_Extraction_Safe) {
std::string compressed = CreateSampleCompressedGzip();
ASSERT_OK_AND_ASSIGN(VecU8Wrapper decompressed_wrapper,
UncompressGzip(compressed));

absl::Cord cord = std::move(decompressed_wrapper).as_cord();
EXPECT_FALSE(cord.empty());
EXPECT_THAT(std::string(cord),
testing::StartsWith("Hello FFI Memory Safety!"));
}

TEST(DeflateMemorySafetyTest, Proof_Of_Absence_ReadDecoder_Lvalue_Safe) {
std::string compressed = CreateSampleCompressedGzip();
auto decoder = read::GzDecoder::create(compressed);
ASSERT_OK_AND_ASSIGN(VecU8Wrapper wrapper, decoder.read_to_end());

absl::string_view view = wrapper.as_string_view();
EXPECT_FALSE(view.empty());
EXPECT_THAT(view, testing::StartsWith("Hello FFI Memory Safety!"));
}

// ============================================================================
// PASS 1: REPRODUCIBLE MEMORY SAFETY HAZARDS (Disabled Under Baseline CI)
// ============================================================================

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreturn-stack-address"
#pragma clang diagnostic ignored "-Wdangling-gsl"

TEST(DeflateMemorySafetyTest,
DISABLED_Hazard_H3_UncompressGzip_TemporaryChaining_UAF) {
std::string compressed = CreateSampleCompressedGzip();

absl::string_view dangling_view;
{
auto get_chained_view = [&]() -> absl::string_view {
return UncompressGzip(compressed)->as_string_view();
};
dangling_view = get_chained_view();
}

volatile char c = dangling_view[0];
(void)c;
}

TEST(DeflateMemorySafetyTest,
DISABLED_Hazard_H3_CompressGzip_TemporaryChaining_UAF) {
std::string input = "Temporary Chaining Compress Hazard Payload";

absl::string_view dangling_view;
{
auto get_chained_view = [&]() -> absl::string_view {
return CompressGzip(input, 6)->as_string_view();
};
dangling_view = get_chained_view();
}

volatile char c = dangling_view[0];
(void)c;
}

TEST(DeflateMemorySafetyTest,
DISABLED_Hazard_H3_ReadGzDecoder_TemporaryChaining_UAF) {
std::string compressed = CreateSampleCompressedGzip();
auto decoder = read::GzDecoder::create(compressed);

absl::string_view dangling_view;
{
auto get_chained_view = [&]() -> absl::string_view {
return decoder.read_to_end()->as_string_view();
};
dangling_view = get_chained_view();
}

volatile char c = dangling_view[0];
(void)c;
}

TEST(DeflateMemorySafetyTest,
DISABLED_Hazard_H3_WriteGzEncoder_Finish_TemporaryChaining_UAF) {
std::string input = "Write GzEncoder Finish Chaining Hazard";
auto encoder = write::GzEncoder::create(Compression::best());
ASSERT_OK(encoder.write_all(input));

absl::string_view dangling_view;
{
auto get_chained_view = [&]() -> absl::string_view {
return std::move(encoder).finish()->as_string_view();
};
dangling_view = get_chained_view();
}

volatile char c = dangling_view[0];
(void)c;
}

#pragma clang diagnostic pop

} // namespace
} // namespace security::deflate
2 changes: 1 addition & 1 deletion deflate/flate2.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using ::security::crubit_helpers::StringViewFromVecU8;
VecU8Wrapper::VecU8Wrapper(rust::vec_u8::VecU8 vec_u8)
: vec_u8_(std::move(vec_u8)) {}

absl::string_view VecU8Wrapper::as_string_view() const {
absl::string_view VecU8Wrapper::as_string_view() const& {
return StringViewFromVecU8(vec_u8_);
}

Expand Down
3 changes: 2 additions & 1 deletion deflate/flate2.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ class GzHeader final {
class VecU8Wrapper {
public:
explicit VecU8Wrapper(rust::vec_u8::VecU8 vec_u8);
absl::string_view as_string_view() const ABSL_ATTRIBUTE_LIFETIME_BOUND;
absl::string_view as_string_view() const& ABSL_ATTRIBUTE_LIFETIME_BOUND;
absl::string_view as_string_view() const&& = delete;
absl::Cord as_cord() &&;

private:
Expand Down
Loading