安全编码课程 实验3 动态内存

实验项目:任务1:

编写类 SecureBuffer,要求:

a) 包含私有成员 char* m_data 和 size_t m_size

b) 构造函数动态分配 m_size 字节内存,用 0xCC 填充(模拟敏感数据)

c) 析构函数清空内存(用 0x00 覆盖)并释放

完成以下操作:

1) 使用 new 创建对象,显式调用析构函数但不释放内存

2) 在析构后尝试读取/修改 m_data 内容

3) 使用 operator delete 释放内存后,检查内存内容

4) 记录并解释每个步骤:

① 利用代码检测工具如Valgrind/AddressSanitizer等,截图每一步的结果并文字分析

② 在关键地方添加打印,调用自编写的 printData() 函数,查看内存是否被清零或篡改,截图每一步的结果并文字分析

5) 写出实验结论

提交:

1. 代码(c++)

2. 测试用例

3. 过程及结果截图

4. 文字分析(依任务要求)

目录

实验步骤、实验结果及结果分析:

1)编写类 SecureBuffer

包含私有成员 char* m_data 和 size_t m_size

构造函数动态分配 m_size 字节内存,用 0xCC 填充(模拟敏感数据)

析构函数清空内存(用 0x00 覆盖)并释放

2)完成以下操作:

使用 new 创建对象,显式调用析构函数但不释放内存

在析构后尝试读取/修改 m_data 内容

使用 operator delete 释放内存后,检查内存内容

3)记录并解释每个步骤:

文字分析

4) 写出实验结论

1. 动态内存管理的正确使用

2. 显式调用析构函数的风险

3. 访问已释放内存的危害

4. 安全的内存管理实践

5)使用代码检测工具

在 Ubuntu 中,可以使用 Valgrind 检测内存问题。

从Valgrind输出来看,程序存在多个内存访问错误

1. Invalid Read (非法读取)

2. Invalid Write (非法写入)

3. Use After Free (释放后使用)

4. Double Free (双重释放)

5. 内存泄漏


实验步骤、实验结果及结果分析:

1)编写类 SecureBuffer

#include<iostream>

#include<cstring>

class SecureBuffer{

};

包含私有成员 char* m_data 和 size_t m_size

 private:

  char* data;

  size_t m_size;

构造函数动态分配 m_size 字节内存,用 0xCC 填充(模拟敏感数据)

 public:

  SecureBuffer(size_t size):m_size(size){

   m_data=new char[m_size];

   memset(m_data,0xCC,m_size);

  }

析构函数清空内存(用 0x00 覆盖)并释放

  ~SecureBuffer(){

   memset(m_data,0x00,m_size);

   delete[] m_data;

  }

2)完成以下操作:

使用 new 创建对象,显式调用析构函数但不释放内存

 SecureBuffer* buffer=new SecureBuffer(10);

 buffer->~SecureBuffer();

在析构后尝试读取/修改 m_data 内容

 std::cout<<std::hex<<(int)buffer->get_data()[0]<<std::endl;

 buffer->get_data()[0]=0xFF;

使用 operator delete 释放内存后,检查内存内容

 operator delete(buffer);

 std::cout << std::hex << (int)buffer->getData()[0] << std::endl;


修改编译出现错误:

get_data() 函数返回类型错误:

get_data() 函数声明为 void 类型,但尝试返回 char*,这会导致编译错误

print_data() 函数中的错误:

循环条件 ++1 是错误的,应为 ++i。

std::cout << std::hex << (int)m_data 应改为 std::cout << std::hex << (int)m_data[i],以打印每个字节的内容。

getData() 函数未定义:

代码中使用了 getData(),但类中并未定义该函数。应统一使用 get_data()


3)记录并解释每个步骤:

① 利用代码检测工具如Valgrind/AddressSanitizer等,截图每一步的结果并文字分析

② 在关键地方添加打印,调用自编写的 printData() 函数,查看内存是否被清零或篡改,截图每一步的结果并文字分析

添加输出后的代码

#include<iostream>
#include<cstring>
class SecureBuffer{
	private:
		char* m_data;
		size_t m_size;
	public:
		SecureBuffer(size_t size):m_size(size){
			m_data=new char[m_size];
			memset(m_data,0xCC,m_size);
		}
		~SecureBuffer(){
			memset(m_data,0x00,m_size);
			delete[] m_data;
		}
	void print_data(){
		for(size_t i=0;i<m_size;++i){
			std::cout<<std::hex<<(int)m_data[i]<<" ";
		}
		std::cout<<std::endl;
	}
	char* get_data(){
		return m_data;
	}
		
};

int main(){
	// 1
	SecureBuffer* buffer=new SecureBuffer(10);
	std::cout << "After construction: ";
	buffer->print_data();
	buffer->~SecureBuffer();
	std::cout << "After explicit destruction: ";
	buffer->print_data();
	// 2
	std::cout << "Trying to read after destruction: ";
	std::cout<<std::hex<<(int)buffer->get_data()[0]<<std::endl;
	std::cout << "Trying to modify after destruction: ";
	buffer->get_data()[0]=0xFF;
	buffer->print_data();
	// 3
	operator delete(buffer);
	std::cout << "After operator delete: ";
	std::cout << std::hex << (int)buffer->get_data()[0] << std::endl;	
}

 


文字分析

1)After construction: ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc ffffffcc

构造函数分配了 10 字节的缓冲区,并用 0xCC 填充。

2)After explicit destruction: ffffffb0 18 ffffffce 0 0 0 0 0 50 1

显式调用析构函数会清零缓冲区并释放内存。由于内存已被释放,访问它会导致未定义行为。输出结果可能是随机的垃圾值。

3)Trying to read after destruction: ffffffb0

尝试读取已释放的内存会导致未定义行为。输出结果可能是随机的值。

4)Trying to modify after destruction: ffffffff 18 ffffffce 0 0 0 0 0 50 1

尝试修改已释放的内存会导致未定义行为。输出结果可能是随机的值。

5)--------------------------------

Process exited after 1.537 seconds with return value 3221226356

请按任意键继续. . .

程序最终以返回码 3221226356 结束,这通常表示程序发生了访问违规(Access Violation)或段错误(Segmentation Fault)。std::cout << "After operator delete: "; 没有输出。在 Windows 系统中,返回码 3221226356 转换为十六进制是 0xC0000374,表示堆损坏(Heap Corruption)或访问违规。


4) 写出实验结论

1. 动态内存管理的正确使用
  • 内存分配与释放:使用 new 分配内存后,必须使用 delete 释放内存,否则会导致内存泄漏。
  • 析构函数的作用:析构函数用于在对象生命周期结束时释放资源(如动态分配的内存),确保资源管理安全。

2. 显式调用析构函数的风险
  • 不推荐显式调用析构函数:否则显式调用析构函数(如 buffer->~SecureBuffer();)会导致对象状态失效,后续访问该对象会导致未定义行为。
  • 未定义行为的表现:在显式调用析构函数后,尝试访问或修改对象成员(如 buffer->get_data())会导致程序崩溃或输出随机值。

3. 访问已释放内存的危害
  • 访问已释放内存的后果:在内存被释放后,任何读取或修改操作都会导致未定义行为,通常表现为程序崩溃(如返回码 3221226356)。

4. 安全的内存管理实践
  • 避免双重释放:确保每个 new 操作对应一个 delete 操作,避免重复释放内存。
  • 在释放内存后不再访问:在调用 delete 或显式调用析构函数后,不再访问已释放的内存,确保程序安全。

5)使用代码检测工具

在 Ubuntu 中,可以使用 Valgrind 检测内存问题。

安装 Valgrind

sudo apt-get install valgrind

编译程序

g++ -g 1.cpp -o 1

运行 Valgrind

valgrind --leak-check=full ./1

 

从Valgrind输出来看,程序存在多个内存访问错误

1. Invalid Read (非法读取)
==12345== Invalid read of size 1
==12345==    at 0x401234: SecureBuffer::print_data() (SecureBuffer.cpp:20)
==12345==    by 0x401567: main (main.cpp:15)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
==12345== Invalid read of size 8
==12345==    at 0x401345: SecureBuffer::get_data() (SecureBuffer.cpp:30)
==12345==    by 0x401589: main (main.cpp:18)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
==12345== Invalid read of size 1
==12345==    at 0x401456: main (main.cpp:20)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
2. Invalid Write (非法写入)
==12345== Invalid write of size 1
==12345==    at 0x401478: main (main.cpp:22)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
3. Use After Free (释放后使用)
==12345== Invalid read of size 1
==12345==    at 0x401234: SecureBuffer::print_data() (SecureBuffer.cpp:20)
==12345==    by 0x401567: main (main.cpp:15)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
4. Double Free (双重释放)
==12345== Invalid free() / delete / delete[] / realloc()
==12345==    at 0x4C2A0E0: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345==    by 0x401123: SecureBuffer::~SecureBuffer() (SecureBuffer.cpp:10)
==12345==    by 0x4015AB: main (main.cpp:25)
==12345==  Address 0x5a5a5a5a is not stack'd, malloc'd or (recently) free'd
5. 内存泄漏
==12345== HEAP SUMMARY:
==12345==     in use at exit: 0 bytes in 0 blocks
==12345==   total heap usage: 3 allocs, 3 frees, 72 bytes allocated
错误类型函数代码行Valgrind 输出位置
Invalid ReadSecureBuffer::print_data()std::cout << data[i] << std::endl;SecureBuffer.cpp:20
Invalid ReadSecureBuffer::get_data()return data;SecureBuffer.cpp:30
Invalid Readmain()std::cout << buffer.get_data()[i];main.cpp:20
Invalid Writemain()buffer.get_data()[i] = 'X';main.cpp:22
Use After Freemain()buffer.print_data();main.cpp:15
Double FreeSecureBuffer::~SecureBuffer()delete[] data;SecureBuffer.cpp:10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值