C語言是效率最高的高級語言,也是最不安全的高級語言。
作為強類型語言,且類型檢查是其安全和效率所在,但與此同時,類型的隱式轉換(表達式計算、賦值、函數的實參與形參的結合與返回以及邏輯類型),簡單粗暴的顯式類型轉換也會造成安全隱患。
C語言的設計就效率與安全的考量而言,是將效率放在首位的,而假定程序員能避開所有安全的陷阱。
1 指針變量的語法設計和動態內存的直接使用
指針能直接操作內存,是其便捷所在,但也是一把雙刃劍,缺乏內存檢查機制,其所帶來的安全隱患也是很大的;
常見的指針問題有訪問空指針、使用野指針、返回指向臨時變量的指針,試圖修改指針指向的常量等。
動態內存分配是否成功並不做自動檢查。不像C++的new,C的malloc()沒有內置了sizeof、類型轉換和類型安全檢查功能。
2 變量的初始化
C編譯器只初始化全局或靜態變量(包括此種類型的指針變量),其它變量(包括指針變量)都不做初始化,未初始化的變量其值總是隨機的,有很大的安全隱患,特別是將未初始化的值用做下標或與指針一起運算時。
3 變量的值溢出並不做邊界檢查
數據類型都是有值域或邊界的,C編譯器並做這種邊界檢查(上溢或下溢),如:
\tunsigned int i = 0;
\tcout<<~i+1;
\tchar c='f';
\tcout<<(char)(c+555);
\tint fac =1;
\tfor(int i=1;i<18;i++)
\t\tfac*=i;
4 數組邊界與緩衝區溢出
數組、C風格字符串及其標準庫的相關字符串函數、甚至vector類都不做邊界檢查。內在越界訪問有讀越界與寫越界(也就是緩衝區溢出)。字符串緩衝區溢出不單只是程序本身運行的安全問題,還很容易被利用,覆蓋棧幀上寄存器EIP的值,而跳轉到shellcode(一段16進制編碼的語句塊,偽裝成了字符串)。
#include <iostream>
using namespace std;
void main()
{
\tint i =555;
\tint arr[]={1,2,3,4};
\tcout<<arr[4]<<endl;
\tchar str[4];
\tstrcpy(str,"abcde");
\tcout<<str<<endl;
\tgets(str);
\tcout<<str<<endl;
system("pause");
}
輸出:
555
abcde
1234567
1234567
5 邏輯運算符的短路運算
int a=2,b=4,i=0,x;
x=a>b&&++i;
6 邏輯型的語法定義
C語言將非0值做為真值,0值做為假值,帶來使得的同時,也會帶來一些問題。
7 廣義上的整型包括字符、邏輯型;
8 free的簡單性處理
free只是簡單切斷了指針與內在塊的聯繫,指針並沒有置NULL,內在塊的地址屬性也並沒有改變,只是將這塊內置重新鏈接到了堆的可用鏈接表中。
9 預處理無法做類型檢查
其用#define定義的常量並沒有類型檢查,其#define定義的含參數的含義的安全性也沒有C++的inline好。
10 函數參數與返回值的有效性並沒有什麼檢查
在函數體的“入口處”,並沒有對參數的有效性進行檢查,return 語句是否返回指向“棧內存”的“指針”或者“引用”也並沒有檢查。
11 文件指針也並不做NULL檢查
C編譯器做open()函數是否返回NULL也並不做檢查。
-End-