admin管理员组文章数量:1130349
实验项目:任务2:
在任务1基础上改写 SecureBuffer 类,模拟以下几种设计缺陷与错误操作,使用 Valgrind/AddressSanitizer或其他代码检测工具检测问题:
拷贝构造
- 为 SecureBuffer 类添加拷贝构造函数:
- 浅拷贝:直接拷贝指针
- 深拷贝:重新分配内存并复制数据
在 main() 中通过对象赋值或拷贝构造,展示浅拷贝导致的问题(修改浅拷贝,影响原对象)
悬空指针
- 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果
(如果在任务一种已完成这一步的模拟,可跳过这一步,实验报告中写上“已在任务一完成”)
重复释放
- 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
内存泄漏
- 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
提交:
1. 代码(c++)
2. 测试用例
3. 过程及结果截图
4. 文字分析(依任务要求)
目录
实验项目:任务2:
实验步骤、实验结果及结果分析:
1)拷贝构造
修改思路
测试用例
过程及结果截图
文字分析
2)悬空指针
修改思路
测试用例
过程及结果截图
文字分析
3)重复释放
修改思路
测试用例
过程及结果截图
文字分析
4)内存泄漏
修改思路
测试用例
过程及结果截图
文字分析
HEAP SUMMARY
Definitely Lost
LEAK SUMMARY
ERROR SUMMARY
实验步骤、实验结果及结果分析:
// 原始代码
#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)拷贝构造
- 为 SecureBuffer 类添加拷贝构造函数:
- 浅拷贝:直接拷贝指针
- 深拷贝:重新分配内存并复制数据
修改思路
-
浅拷贝:
- 在拷贝构造函数中直接复制指针,使新对象和原对象共享同一块内存。
- 修改新对象的数据时,原对象的数据也会被修改。
-
深拷贝:
- 在拷贝构造函数中重新分配内存,并复制原对象的数据。
- 新对象和原对象拥有独立的内存,修改新对象不会影响原对象。
// 构造函数
SecureBuffer(size_t size) : m_size(size) {
m_data = new char[m_size];
memset(m_data, 0xCC, m_size);
}
// 浅拷贝构造函数
SecureBuffer(const SecureBuffer& other) : m_size(other.m_size) {
m_data = other.m_data; // 直接复制指针
}
// 深拷贝构造函数
SecureBuffer(const SecureBuffer& other, bool deepCopy) : m_size(other.m_size) {
if (deepCopy) {
m_data = new char[m_size];
memcpy(m_data, other.m_data, m_size);
} else {
m_data = other.m_data; // 直接复制指针
}
}
测试用例
// 设置数据
void set_data(size_t index, char value) {
if (index < m_size) {
m_data[index] = value;
}
set_data方法:添加了set_data方法,用于修改m_data中的内容。- 深拷贝构造函数的调用:在
main函数中,显式调用了带有deepCopy参数的构造函数来进行深拷贝。
int main() {
SecureBuffer buffer1(10);
buffer1.set_data(0, 'A');
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
// 浅拷贝
SecureBuffer buffer2 = buffer1; // 使用拷贝构造函数
buffer2.set_data(0, 'X'); // 修改 buffer2 的数据
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
std::cout << "Buffer2 data: " << buffer2.get_data()[0] << std::endl;
// 深拷贝
SecureBuffer buffer3(buffer1, true); // 使用深拷贝构造函数
buffer3.set_data(0, 'Y'); // 修改 buffer3 的数据
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
std::cout << "Buffer3 data: " << buffer3.get_data()[0] << std::endl;
return 0;
}
过程及结果截图
文字分析
浅拷贝共享内存,而深拷贝则独立分配内存并复制数据。
2)悬空指针
- 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果
修改思路
- 使用
new创建一个SecureBuffer对象。 - 显式调用析构函数销毁对象,但不置空指针。
- 通过该指针访问成员函数,观察悬空指针带来的后果。
~SecureBuffer() {
if (m_data) {
std::memset(m_data, 0x00, m_size); // 清空内存为 0x00
delete[] m_data;
// m_data = nullptr; // 将指针置为 nullptr,避免悬空指针
}
}
int main() {
SecureBuffer* buffer3 = new SecureBuffer(10);
buffer3->~SecureBuffer();
buffer3->print_data();
return 0;
}
测试用例
int main() {
// 悬空指针测试
SecureBuffer* buffer3 = new SecureBuffer(10);
buffer3->print_data(); // 正常输出数据
buffer3->~SecureBuffer(); // 显式调用析构函数
buffer3->print_data(); // 访问悬空指针,可能导致崩溃或输出随机值
return 0;
}
过程及结果截图
文字分析
访问悬空指针会导致未定义行为,可能引发程序崩溃或输出随机值。为了避免悬空指针问题,应该在释放内存后将指针置为 nullptr,并在访问指针前检查其是否为 nullptr。
3)重复释放
- 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
修改思路
- 使用
new创建一个SecureBuffer对象。 - 调用
operator delete释放内存。 - 再次调用
operator delete对同一地址释放两次,模拟重复释放的风险行为。
int main() {
SecureBuffer* buffer4 = new SecureBuffer(10);
delete buffer4;
// delete buffer4; // 取消注释以模拟重复释放
return 0;
}
测试用例
int main() {
// 重复释放测试
SecureBuffer* buffer4 = new SecureBuffer(10);
delete buffer4; // 第一次释放
delete buffer4; // 第二次释放,模拟重复释放
return 0;
}
过程及结果截图
文字分析
重复释放会导致程序崩溃,会导致未定义行为,操作系统可能会检测到内存错误并终止程序。为了避免重复释放问题,建议在释放内存后将指针置为 nullptr,并在释放前检查指针是否为 nullptr。此外,使用智能指针(如 std::unique_ptr 或 std::shared_ptr)可以自动管理内存,避免手动释放内存带来的风险。
4)内存泄漏
- 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
修改思路
- 在
main()中批量创建 10000 个SecureBuffer临时对象。 - 不调用
delete或operator delete释放内存,导致大量内存泄漏。 - 使用 Valgrind 或 AddressSanitizer 检测内存泄漏。
int main() {
for (int i = 0; i < 10000; ++i) {
SecureBuffer* tempBuffer = new SecureBuffer(10);
}
std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
return 0;
}
使用 Valgrind 检测内存问题:
sudo apt-get install valgrind
g++ -g 2.cpp -o 2
valgrind --leak-check=full ./2
测试用例
int main() {
// 内存泄漏测试
for (int i = 0; i < 10000; ++i) {
SecureBuffer* tempBuffer = new SecureBuffer(10);
// 不释放内存
}
std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
return 0;
}
过程及结果截图
文字分析
HEAP SUMMARY
==2907== in use at exit: 260,000 bytes in 20,000 blocks
==2907== total heap usage: 20,002 allocs, 2 frees, 333,728 bytes allocated
in use at exit:程序退出时,仍有 260,000 字节的内存未被释放。total heap usage:程序总共进行了 20,002 次内存分配,但只释放了 2 次,导致大量内存未被释放。
Definitely Lost
==2907== 260,000 (160,000 direct, 100,000 indirect) bytes in 10,000 blocks are definitely lost in loss record 2 of 2
==2907== at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2907== by 0x108B0E: main (in /home/liao/1)
definitely lost:明确表示有 260,000 字节的内存泄漏。160,000 direct:直接泄漏的内存为 160,000 字节。100,000 indirect:间接泄漏的内存为 100,000 字节。
- 泄漏位置:内存泄漏发生在
main函数中,具体是在调用operator new分配内存时。
LEAK SUMMARY
==2907== definitely lost: 160,000 bytes in 10,000 blocks
==2907== indirectly lost: 100,000 bytes in 10,000 blocks
==2907== possibly lost: 0 bytes in 0 blocks
==2907== still reachable: 0 bytes in 0 blocks
==2907== suppressed: 0 bytes in 0 blocks
definitely lost:明确泄漏的内存为 160,000 字节,分布在 10,000 个块中。indirectly lost:间接泄漏的内存为 100,000 字节,分布在 10,000 个块中。possibly lost:没有可能泄漏的内存。still reachable:没有仍然可访问的内存。suppressed:没有被抑制的错误。
ERROR SUMMARY
==2907== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
ERROR SUMMARY:总共检测到 1 个错误,来自 1 个上下文。
实验项目:任务2:
在任务1基础上改写 SecureBuffer 类,模拟以下几种设计缺陷与错误操作,使用 Valgrind/AddressSanitizer或其他代码检测工具检测问题:
拷贝构造
- 为 SecureBuffer 类添加拷贝构造函数:
- 浅拷贝:直接拷贝指针
- 深拷贝:重新分配内存并复制数据
在 main() 中通过对象赋值或拷贝构造,展示浅拷贝导致的问题(修改浅拷贝,影响原对象)
悬空指针
- 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果
(如果在任务一种已完成这一步的模拟,可跳过这一步,实验报告中写上“已在任务一完成”)
重复释放
- 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
内存泄漏
- 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
提交:
1. 代码(c++)
2. 测试用例
3. 过程及结果截图
4. 文字分析(依任务要求)
目录
实验项目:任务2:
实验步骤、实验结果及结果分析:
1)拷贝构造
修改思路
测试用例
过程及结果截图
文字分析
2)悬空指针
修改思路
测试用例
过程及结果截图
文字分析
3)重复释放
修改思路
测试用例
过程及结果截图
文字分析
4)内存泄漏
修改思路
测试用例
过程及结果截图
文字分析
HEAP SUMMARY
Definitely Lost
LEAK SUMMARY
ERROR SUMMARY
实验步骤、实验结果及结果分析:
// 原始代码
#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)拷贝构造
- 为 SecureBuffer 类添加拷贝构造函数:
- 浅拷贝:直接拷贝指针
- 深拷贝:重新分配内存并复制数据
修改思路
-
浅拷贝:
- 在拷贝构造函数中直接复制指针,使新对象和原对象共享同一块内存。
- 修改新对象的数据时,原对象的数据也会被修改。
-
深拷贝:
- 在拷贝构造函数中重新分配内存,并复制原对象的数据。
- 新对象和原对象拥有独立的内存,修改新对象不会影响原对象。
// 构造函数
SecureBuffer(size_t size) : m_size(size) {
m_data = new char[m_size];
memset(m_data, 0xCC, m_size);
}
// 浅拷贝构造函数
SecureBuffer(const SecureBuffer& other) : m_size(other.m_size) {
m_data = other.m_data; // 直接复制指针
}
// 深拷贝构造函数
SecureBuffer(const SecureBuffer& other, bool deepCopy) : m_size(other.m_size) {
if (deepCopy) {
m_data = new char[m_size];
memcpy(m_data, other.m_data, m_size);
} else {
m_data = other.m_data; // 直接复制指针
}
}
测试用例
// 设置数据
void set_data(size_t index, char value) {
if (index < m_size) {
m_data[index] = value;
}
set_data方法:添加了set_data方法,用于修改m_data中的内容。- 深拷贝构造函数的调用:在
main函数中,显式调用了带有deepCopy参数的构造函数来进行深拷贝。
int main() {
SecureBuffer buffer1(10);
buffer1.set_data(0, 'A');
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
// 浅拷贝
SecureBuffer buffer2 = buffer1; // 使用拷贝构造函数
buffer2.set_data(0, 'X'); // 修改 buffer2 的数据
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
std::cout << "Buffer2 data: " << buffer2.get_data()[0] << std::endl;
// 深拷贝
SecureBuffer buffer3(buffer1, true); // 使用深拷贝构造函数
buffer3.set_data(0, 'Y'); // 修改 buffer3 的数据
std::cout << "Buffer1 data: " << buffer1.get_data()[0] << std::endl;
std::cout << "Buffer3 data: " << buffer3.get_data()[0] << std::endl;
return 0;
}
过程及结果截图
文字分析
浅拷贝共享内存,而深拷贝则独立分配内存并复制数据。
2)悬空指针
- 使用new 创建的 SecureBuffer 对象之后,显式调用析构函数销毁对象,但不置空指针,并继续通过该指针访问其成员函数,观察并输出悬垂指针带来的后果
修改思路
- 使用
new创建一个SecureBuffer对象。 - 显式调用析构函数销毁对象,但不置空指针。
- 通过该指针访问成员函数,观察悬空指针带来的后果。
~SecureBuffer() {
if (m_data) {
std::memset(m_data, 0x00, m_size); // 清空内存为 0x00
delete[] m_data;
// m_data = nullptr; // 将指针置为 nullptr,避免悬空指针
}
}
int main() {
SecureBuffer* buffer3 = new SecureBuffer(10);
buffer3->~SecureBuffer();
buffer3->print_data();
return 0;
}
测试用例
int main() {
// 悬空指针测试
SecureBuffer* buffer3 = new SecureBuffer(10);
buffer3->print_data(); // 正常输出数据
buffer3->~SecureBuffer(); // 显式调用析构函数
buffer3->print_data(); // 访问悬空指针,可能导致崩溃或输出随机值
return 0;
}
过程及结果截图
文字分析
访问悬空指针会导致未定义行为,可能引发程序崩溃或输出随机值。为了避免悬空指针问题,应该在释放内存后将指针置为 nullptr,并在访问指针前检查其是否为 nullptr。
3)重复释放
- 在销毁 SecureBuffer 对象后,手动调用 operator delete 释放其内存,再次调用 operator delete 对同一地址释放两次,模拟重复释放的风险行为,观察程序运行效果
修改思路
- 使用
new创建一个SecureBuffer对象。 - 调用
operator delete释放内存。 - 再次调用
operator delete对同一地址释放两次,模拟重复释放的风险行为。
int main() {
SecureBuffer* buffer4 = new SecureBuffer(10);
delete buffer4;
// delete buffer4; // 取消注释以模拟重复释放
return 0;
}
测试用例
int main() {
// 重复释放测试
SecureBuffer* buffer4 = new SecureBuffer(10);
delete buffer4; // 第一次释放
delete buffer4; // 第二次释放,模拟重复释放
return 0;
}
过程及结果截图
文字分析
重复释放会导致程序崩溃,会导致未定义行为,操作系统可能会检测到内存错误并终止程序。为了避免重复释放问题,建议在释放内存后将指针置为 nullptr,并在释放前检查指针是否为 nullptr。此外,使用智能指针(如 std::unique_ptr 或 std::shared_ptr)可以自动管理内存,避免手动释放内存带来的风险。
4)内存泄漏
- 在 main() 中批量创建 10000 个 SecureBuffer 临时对象,但不调用 delete 或 operator delete 释放内存,导致大量内存泄漏。输出内存泄漏提示
修改思路
- 在
main()中批量创建 10000 个SecureBuffer临时对象。 - 不调用
delete或operator delete释放内存,导致大量内存泄漏。 - 使用 Valgrind 或 AddressSanitizer 检测内存泄漏。
int main() {
for (int i = 0; i < 10000; ++i) {
SecureBuffer* tempBuffer = new SecureBuffer(10);
}
std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
return 0;
}
使用 Valgrind 检测内存问题:
sudo apt-get install valgrind
g++ -g 2.cpp -o 2
valgrind --leak-check=full ./2
测试用例
int main() {
// 内存泄漏测试
for (int i = 0; i < 10000; ++i) {
SecureBuffer* tempBuffer = new SecureBuffer(10);
// 不释放内存
}
std::cout << "Created 10000 SecureBuffer objects without releasing memory." << std::endl;
return 0;
}
过程及结果截图
文字分析
HEAP SUMMARY
==2907== in use at exit: 260,000 bytes in 20,000 blocks
==2907== total heap usage: 20,002 allocs, 2 frees, 333,728 bytes allocated
in use at exit:程序退出时,仍有 260,000 字节的内存未被释放。total heap usage:程序总共进行了 20,002 次内存分配,但只释放了 2 次,导致大量内存未被释放。
Definitely Lost
==2907== 260,000 (160,000 direct, 100,000 indirect) bytes in 10,000 blocks are definitely lost in loss record 2 of 2
==2907== at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2907== by 0x108B0E: main (in /home/liao/1)
definitely lost:明确表示有 260,000 字节的内存泄漏。160,000 direct:直接泄漏的内存为 160,000 字节。100,000 indirect:间接泄漏的内存为 100,000 字节。
- 泄漏位置:内存泄漏发生在
main函数中,具体是在调用operator new分配内存时。
LEAK SUMMARY
==2907== definitely lost: 160,000 bytes in 10,000 blocks
==2907== indirectly lost: 100,000 bytes in 10,000 blocks
==2907== possibly lost: 0 bytes in 0 blocks
==2907== still reachable: 0 bytes in 0 blocks
==2907== suppressed: 0 bytes in 0 blocks
definitely lost:明确泄漏的内存为 160,000 字节,分布在 10,000 个块中。indirectly lost:间接泄漏的内存为 100,000 字节,分布在 10,000 个块中。possibly lost:没有可能泄漏的内存。still reachable:没有仍然可访问的内存。suppressed:没有被抑制的错误。
ERROR SUMMARY
==2907== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
ERROR SUMMARY:总共检测到 1 个错误,来自 1 个上下文。
版权声明:本文标题:安全编码课程 实验4 动态内存(2) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://it.en369.cn/jiaocheng/1758633826a2782130.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。


发表评论