Understanding and using c pointers 第七章读书笔记
2013-09-23 22:14
537 查看
tonybai在他的网站上写了一个本书的知识点纲要
安全问题与指针的不恰当使用
本章从三个方面来讨论安全问题
1.指针声明与初始化
2.指针的不当使用
3.内存回收问题(deallocation problems)
第一部分:指针的声明与初始化
指针声明不当。如:
作者本意是声明两个int指针,但是实际结果是只有ptr1被声明为指针,ptr2没有被声明为指针,所以应该这样声明int *ptr1,*ptr2;
指针使用前没有初始化
使用未经初始化的指针可能会导致运行时错误。有时也称为野指针
处理未初始化的指针
1.初始化指针时将其置为NULL
2.使用assert函数
3.使用第三方工具
第二部分:指针的使用问题
该部分会讨论有关字符串、结构和函数指针等等,许多安全问题都是以缓冲区溢出这个概念为中心。
缓冲区溢出可通过以下几种方式发生
1.数组中使用索引访问元素时不检查索引的值
2.使用数组指针进行指针运算时粗心大意
3.使用像gets之类的函数从标准输入中读取字符串
4.strcpy和strcat的不当使用
检查是否为NULL(Test for NULL)
对使用malloc函数返回的值一定要进行防空判断如:
(误用解引用操作符*)Misuse of the Deference Operator
悬挂指针(Dangling Poiners第二章说过)
数组访问越界(Accessing Memory Outside the Bounds of an Array)
其一种可能的内存分配如下图:
数组大小计算错误(Calculating the Array Size Incorrectly)
误用sizeof操作符(Misusing the sizeof Operator)
永远匹配指针类型(Always Match Pointer Types)
It is a good idea to always use the appropriate pointer type for the data.
小端法如下图:
有界限的指针(Bounded Pointers)
C语言并没有提供直接的支持,但是可以像下面一样由程序员控制
使用这种方法很繁琐(tedious),可以使用静态分析等等
字符串相关的安全问题(String Security Issues)
strcpy和strcat可能会导致缓冲溢出,C11中新引入的strcpy_s和strcat_s目前只有微软Visual C++支持,这两个新函数在发生缓冲区溢出时返回错误
同样还有新引入的scanf_s和wscanf_s
The use of some functions can result in an attacker accessing memory using a technique known as format string attacks.
作者在这里举了一个printf的例子
更多这方面的信息请参阅hackerproof.org
指针运算与结构(Pointer Arithmetic and Structures)
对于数组可以放心使用指针运算,因为数组保证内存分配是连续的,而对于结构其位域可能不在连续的内存上(as the structure's fields may not be allocated in consecutive regions of memory)
比如下面结构:
name分配了10个字节,其后跟着一个int,但是int要对齐到能被4整除的位置,所以name和age之间就有位填补(gap)。如图:
Even if the memory within a structure is contiguous, it is not a good practice to use pointer arithmetic with the structure’s fields.
即使结构的内存是连续的,最好也不要使用指针运算
函数指针问题(Function Pointers Issues)
函数指针误用与滥用会导致不可预测的结果,考虑如下函数:
判断系统状态是否为0的最佳方法如下:
但如果忘了写()的话,代码运行就不正常了
这样写的话也不科学:
Do not assign a function to a function pointer when their signatures differ.This can result in undefined behaviour.例子如下:
第三部分:内存回收问题(Memory Deallocation Issues)
多次释放(Double free)
cert.org上有有关这方面的更多资料,一种简单的避免双重释放就是释放完后即置为NULL,也可以写自己的free函数
清除敏感数据(Clearing Sensitive Data)
程序退出时清除敏感数据有助于隐私保存,像陈老师那样把照片没彻底删除再跑去修电脑实在是太危险了。如:
如果name被声明为指针,那么在回收之前要清除其内存如:
使用静态分析工具(Using Static Analysis Tools)
有许多静态分析工具可用于检测指针的不当使用。GCC编译器使用-Wall报告所有的编译警告。
安全问题与指针的不恰当使用
本章从三个方面来讨论安全问题
1.指针声明与初始化
2.指针的不当使用
3.内存回收问题(deallocation problems)
第一部分:指针的声明与初始化
指针声明不当。如:
int* ptr1,ptr2;
作者本意是声明两个int指针,但是实际结果是只有ptr1被声明为指针,ptr2没有被声明为指针,所以应该这样声明int *ptr1,*ptr2;
#define PINT int* PINT ptr1,ptr2; //宏替换是代码的替换,所以依然只有ptr1被声明为指针,ptr2没有被声明为指针 typedef int* PINT; PINT ptr1,ptr2; //使用typedef这样就将ptr1和ptr2都声明为指针了
指针使用前没有初始化
使用未经初始化的指针可能会导致运行时错误。有时也称为野指针
int *p1; ... printf("%d\n",*pi); //pi在使用时根本没有初始化,里面存的是垃圾
处理未初始化的指针
1.初始化指针时将其置为NULL
2.使用assert函数
3.使用第三方工具
如: int *pi = NULL; //置为NULL ... if(pi == NULL) //判断是否为NULL { } else { } 也可以使用assert,调试时非常有用 assert(pi != NULL);
第二部分:指针的使用问题
该部分会讨论有关字符串、结构和函数指针等等,许多安全问题都是以缓冲区溢出这个概念为中心。
缓冲区溢出可通过以下几种方式发生
1.数组中使用索引访问元素时不检查索引的值
2.使用数组指针进行指针运算时粗心大意
3.使用像gets之类的函数从标准输入中读取字符串
4.strcpy和strcat的不当使用
检查是否为NULL(Test for NULL)
对使用malloc函数返回的值一定要进行防空判断如:
float *vector = malloc(sizeof(float) * 20); if(vector == NULL){ //内存分配失败 } else { //处理vector }
(误用解引用操作符*)Misuse of the Deference Operator
正确情况 int num; int *pi = &m; /*这里的*是用来声明指针*/ 错误情况: int num; int *pi; /*声明了一个指针*/ *pi = # /*这里的*是解引用,应该改为pi = #*/
悬挂指针(Dangling Poiners第二章说过)
数组访问越界(Accessing Memory Outside the Bounds of an Array)
char firstName[8] = "1234567"; char middleName[8] = "1234567"; char lastName[8] = "1234567"; middleName[-2] = 'X'; middleName[0] = 'X'; middleName[10] = 'X'; printf("%p %s\n",firstName,firstName); printf("%p %s\n",middleName,middleName); printf("%p %s\n",lastName,lastName);
其一种可能的内存分配如下图:
数组大小计算错误(Calculating the Array Size Incorrectly)
#include <stdio.h> #include <ctype.h> #include <string.h> void replace(char buffer[],char replacement,size_t size); main() { char name[8]; strcpy(name,"Alexander");/* 越界了,因为name数组只能有8个char,减去一个NUL字符,实际只能有7个char,但是Alexander有8个字符 */ replace(name,'+',sizeof(name));/* 这里数组元素个数应该用sizeof(name) / sizeof(char),这样做图方便,因为sizeof(char)就是1 */ printf("%s\n",name); /* 输出++++++++r */ } void replace(char buffer[],char replacement,size_t size) { size_t count = 0; while(*buffer != '\0' && count++ < size) { *buffer = replacement; buffer++; } }
误用sizeof操作符(Misusing the sizeof Operator)
int buffer[20]; int *pbuffer = buffer; int i; for(i = 0; i < sizeof(buffer); i++) /* sizeof(buffer)的使用造成越界,应该用sizeof(buffer) / sizeof(int) */ { *(pbuffer++) = 0; }
永远匹配指针类型(Always Match Pointer Types)
It is a good idea to always use the appropriate pointer type for the data.
/* 这个例子非常适合在计算机对不同数值的表示中讲,且涉及到了机器的大小端法表设计,非常好 */ int num = 2147483647; int *pi = # short *ps = (short*)pi; printf("pi: %p,Value(16):%x,Value(10):%d\n",pi,*pi,*pi); printf("ps: %p,Value(16):%hx,Value(10):%hd\n",ps,(unsigned short)*ps,(unsigned short)*ps);
小端法如下图:
有界限的指针(Bounded Pointers)
C语言并没有提供直接的支持,但是可以像下面一样由程序员控制
#define SIZE 32 char name[SIZE]; char *p = name; if(name != NULL) { if(p > name && p < name + SIZE) { //合法指针 } else { //不合法指针 } }
使用这种方法很繁琐(tedious),可以使用静态分析等等
字符串相关的安全问题(String Security Issues)
strcpy和strcat可能会导致缓冲溢出,C11中新引入的strcpy_s和strcat_s目前只有微软Visual C++支持,这两个新函数在发生缓冲区溢出时返回错误
同样还有新引入的scanf_s和wscanf_s
char firstName[8]; int result; result = strcpy_s(firstName,sizeof(firstName),"Alexander");
The use of some functions can result in an attacker accessing memory using a technique known as format string attacks.
作者在这里举了一个printf的例子
int main(int argc,char** argv) { printf(argv[1]); /* 这里就可能会造成字符串攻击 */ ... }
更多这方面的信息请参阅hackerproof.org
指针运算与结构(Pointer Arithmetic and Structures)
对于数组可以放心使用指针运算,因为数组保证内存分配是连续的,而对于结构其位域可能不在连续的内存上(as the structure's fields may not be allocated in consecutive regions of memory)
比如下面结构:
typedef struct _employee { char name[10]; int age; } Employee;
name分配了10个字节,其后跟着一个int,但是int要对齐到能被4整除的位置,所以name和age之间就有位填补(gap)。如图:
Even if the memory within a structure is contiguous, it is not a good practice to use pointer arithmetic with the structure’s fields.
即使结构的内存是连续的,最好也不要使用指针运算
typedef struct _item { int partNumber; int quantity; int binNumber; } Item; Item part = {12345, 35, 107}; int *pi = &part.partNumber; printf("Part number: %d\n",*pi); pi++; printf("Quantity: %d\n",*pi); pi++; printf("Bin number: %d\n",*pi); 正常情况下输出结果是我们期待的,但它并不能保证总是起作用(but it is not guaranteed to work)下面是一种较好的办法 int *pi = &part.partNumber; printf("Part number: %d\n",*pi); pi = &part.quantity; printf("Quantity: %d\n",*pi); pi = &part.binNumber; printf("Bin number: %d\n",*pi); 更好的办法是: printf("Part number: %d\n",part.partNumber); printf("Quantity: %d\n",part.quantity); printf("Bin number: %d\n",part.binNumber);
函数指针问题(Function Pointers Issues)
函数指针误用与滥用会导致不可预测的结果,考虑如下函数:
int getSystemStatus() { int status; ... return status; }
判断系统状态是否为0的最佳方法如下:
if(getSystemStatus() == 0) { printf("Status is 0\n"); } else { printf("Status is not 0\n"); }
但如果忘了写()的话,代码运行就不正常了
if(getSystemStatus == 0) { printf("Status is 0\n"); } else { printf("Status is not 0\n"); /* 总会执行着这句 */ }
这样写的话也不科学:
if(getSystemStatus) /*函数指针,肯定不为0*/ { //总会执行这段代码 }
Do not assign a function to a function pointer when their signatures differ.This can result in undefined behaviour.例子如下:
int (*fptrCompute)(int,int); int add(int n1,int n2,int n3) { return n1 + n2 + n3; } fptrCompute = add; fptrCompute(2,5); /* 实际需要3个参数,但却只传了2个 ,代码会编译通过gcc给出了警告,但是结果却很难预料了 */
第三部分:内存回收问题(Memory Deallocation Issues)
多次释放(Double free)
char *name = (char*) malloc(...); ... free(name); /* 第一次释放 */ ... free(name); /* 第二次释放 */
cert.org上有有关这方面的更多资料,一种简单的避免双重释放就是释放完后即置为NULL,也可以写自己的free函数
char *name = (char *) malloc(...); ... free(name); name = NULL;
清除敏感数据(Clearing Sensitive Data)
程序退出时清除敏感数据有助于隐私保存,像陈老师那样把照片没彻底删除再跑去修电脑实在是太危险了。如:
char name[32]; int userID; char *securityQuestion; //assign values ... //delete sensitive information memset(name,0,sizeof(name)); userID = 0; memset(securityQuestion,0,strlen(securityQuestion));
如果name被声明为指针,那么在回收之前要清除其内存如:
char *name = (char*) malloc(...); ... memset(name,0,sizeof(name)); free(name);
使用静态分析工具(Using Static Analysis Tools)
有许多静态分析工具可用于检测指针的不当使用。GCC编译器使用-Wall报告所有的编译警告。
相关文章推荐
- Understanding and using c pointers 第四章读书笔记
- Understanding and using c pointers 第六章读书笔记
- Understanding and using c pointers 第三章读书笔记
- Understanding and using c pointers 第八章读书笔记
- Understanding and using c pointers 第一章读书笔记
- Understanding and using c pointers 第二章读书笔记
- Understanding and using c pointers 第五章读书笔记
- Understanding and Using Pointers in Delphi
- 《Understanding and Using C Pointers》要点先睹为快 系统总结
- Pointers on C——12 Using Structures and Pointers.3
- Understanding ELF using readelf and objdump
- Understanding ELF using readelf and objdump
- Understanding and Using OpenGL Texture Objects
- Pointers on C——12 Using Structures and Pointers.3
- ReactiveCocoa Essentials: Understanding and Using RACCommand
- Understanding and Using the SharePoint 2013 REST Interface
- Understanding and Using Servlet Filters
- Understanding ELF using readelf and objdump
- 第十二天(Understanding and Using Events in Sencha Touch)
- Understanding and Using HRMS Security in Oracle HRMS