除了測(cè)試當(dāng)前指針長(zhǎng)度,否則一般不會(huì)在pointer上使用sizeof。
正確:
size_t pointer_length = sizeof(void*);
可能錯(cuò)誤:
size_t structure_length = sizeof(Foo*);
可能是:
size_t structure_length = sizeof(Foo);
關(guān)聯(lián)漏洞:
中風(fēng)險(xiǎn)-邏輯漏洞
錯(cuò)誤:
int a[3];
...;
if (a > 0)
...;
該判斷永遠(yuǎn)為真,等價(jià)于:
int a[3];
...;
if (&a[0])
...;
可能是:
int a[3];
...;
if(a[0] > 0)
...;
開(kāi)啟足夠的編譯器警告(GCC 中為 -Waddress
,并已包含在 -Wall
中),并設(shè)置為錯(cuò)誤,可以在編譯期間發(fā)現(xiàn)該問(wèn)題。
關(guān)聯(lián)漏洞:
中風(fēng)險(xiǎn)-邏輯漏洞
特殊情況需要特殊對(duì)待(比如開(kāi)發(fā)硬件固件時(shí)可能需要寫(xiě)死)
但是如果是系統(tǒng)驅(qū)動(dòng)開(kāi)發(fā)之類(lèi)的,寫(xiě)死可能會(huì)導(dǎo)致后續(xù)的問(wèn)題。
關(guān)聯(lián)漏洞:
高風(fēng)險(xiǎn)-內(nèi)存破壞
錯(cuò)誤:
*foo = 100;
if (!foo) {
ERROR("foobar");
}
正確:
if (!foo) {
ERROR("foobar");
}
*foo = 100;
錯(cuò)誤:
void Foo(char* bar) {
*bar = '\0';
}
正確:
void Foo(char* bar) {
if(bar)
*bar = '\0';
else
...;
}
關(guān)聯(lián)漏洞:
低風(fēng)險(xiǎn)-拒絕服務(wù)
在對(duì)指針進(jìn)行釋放后,需要將該指針設(shè)置為NULL,以防止后續(xù)free指針的誤用,導(dǎo)致UAF等其他內(nèi)存破壞問(wèn)題。尤其是在結(jié)構(gòu)體、類(lèi)里面存儲(chǔ)的原始指針。
錯(cuò)誤:
void foo() {
char* p = (char*)malloc(100);
memcpy(p, "hello", 6);
// 此時(shí)p所指向的內(nèi)存已被釋放,但是p所指的地址仍然不變
printf("%s\n", p);
free(p);
// 未設(shè)置為NULL,可能導(dǎo)致UAF等內(nèi)存錯(cuò)誤
if (p != NULL) { // 沒(méi)有起到防錯(cuò)作用
printf("%s\n", p); // 錯(cuò)誤使用已經(jīng)釋放的內(nèi)存
}
}
正確:
void foo() {
char* p = (char*)malloc(100);
memcpy(p, "hello", 6);
// 此時(shí)p所指向的內(nèi)存已被釋放,但是p所指的地址仍然不變
printf("%s\n", p);
free(p);
//釋放后將指針賦值為空
p = NULL;
if (p != NULL) { // 沒(méi)有起到防錯(cuò)作用
printf("%s\n", p); // 錯(cuò)誤使用已經(jīng)釋放的內(nèi)存
}
}
對(duì)于 C++ 代碼,使用 string、vector、智能指針等代替原始內(nèi)存管理機(jī)制,可以大量減少這類(lèi)錯(cuò)誤。
關(guān)聯(lián)漏洞:
高風(fēng)險(xiǎn)-內(nèi)存破壞
在對(duì)指針、對(duì)象或變量進(jìn)行操作時(shí),需要能夠正確判斷所操作對(duì)象的原始類(lèi)型。如果使用了與原始類(lèi)型不兼容的類(lèi)型進(jìn)行訪(fǎng)問(wèn),則存在安全隱患。
錯(cuò)誤:
const int NAME_TYPE = 1;
const int ID_TYPE = 2;
// 該類(lèi)型根據(jù) msg_type 進(jìn)行區(qū)分,如果在對(duì)MessageBuffer進(jìn)行操作時(shí)沒(méi)有判斷目標(biāo)對(duì)象,則存在類(lèi)型混淆
struct MessageBuffer {
int msg_type;
union {
const char *name;
int name_id;
};
};
void Foo() {
struct MessageBuffer buf;
const char* default_message = "Hello World";
// 設(shè)置該消息類(lèi)型為 NAME_TYPE,因此buf預(yù)期的類(lèi)型為 msg_type + name
buf.msg_type = NAME_TYPE;
buf.name = default_message;
printf("Pointer of buf.name is %p\n", buf.name);
// 沒(méi)有判斷目標(biāo)消息類(lèi)型是否為ID_TYPE,直接修改nameID,導(dǎo)致類(lèi)型混淆
buf.name_id = user_controlled_value;
if (buf.msg_type == NAME_TYPE) {
printf("Pointer of buf.name is now %p\n", buf.name);
// 以NAME_TYPE作為類(lèi)型操作,可能導(dǎo)致非法內(nèi)存讀寫(xiě)
printf("Message: %s\n", buf.name);
} else {
printf("Message: Use ID %d\n", buf.name_id);
}
}
正確(判斷操作的目標(biāo)是否是預(yù)期類(lèi)型):
void Foo() {
struct MessageBuffer buf;
const char* default_message = "Hello World";
// 設(shè)置該消息類(lèi)型為 NAME_TYPE,因此buf預(yù)期的類(lèi)型為 msg_type + name
buf.msg_type = NAME_TYPE;
buf.name = default_msessage;
printf("Pointer of buf.name is %p\n", buf.name);
// 判斷目標(biāo)消息類(lèi)型是否為 ID_TYPE,不是預(yù)期類(lèi)型則做對(duì)應(yīng)操作
if (buf.msg_type == ID_TYPE)
buf.name_id = user_controlled_value;
if (buf.msg_type == NAME_TYPE) {
printf("Pointer of buf.name is now %p\n", buf.name);
printf("Message: %s\n", buf.name);
} else {
printf("Message: Use ID %d\n", buf.name_id);
}
}
關(guān)聯(lián)漏洞:
高風(fēng)險(xiǎn)-內(nèi)存破壞
在使用智能指針時(shí),防止其和原始指針的混用,否則可能導(dǎo)致對(duì)象生命周期問(wèn)題,例如 UAF 等安全風(fēng)險(xiǎn)。
錯(cuò)誤例子:
class Foo {
public:
explicit Foo(int num) { data_ = num; };
void Function() { printf("Obj is %p, data = %d\n", this, data_); };
private:
int data_;
};
std::unique_ptr<Foo> fool_u_ptr = nullptr;
Foo* pfool_raw_ptr = nullptr;
void Risk() {
fool_u_ptr = make_unique<Foo>(1);
// 從獨(dú)占智能指針中獲取原始指針,<Foo>(1)
pfool_raw_ptr = fool_u_ptr.get();
// 調(diào)用<Foo>(1)的函數(shù)
pfool_raw_ptr->Function();
// 獨(dú)占智能指針重新賦值后會(huì)釋放內(nèi)存
fool_u_ptr = make_unique<Foo>(2);
// 通過(guò)原始指針操作會(huì)導(dǎo)致UAF,pfool_raw_ptr指向的對(duì)象已經(jīng)釋放
pfool_raw_ptr->Function();
}
// 輸出:
// Obj is 0000027943087B80, data = 1
// Obj is 0000027943087B80, data = -572662307
正確,通過(guò)智能指針操作:
void Safe() {
fool_u_ptr = make_unique<Foo>(1);
// 調(diào)用<Foo>(1)的函數(shù)
fool_u_ptr->function();
fool_u_ptr = make_unique<Foo>(2);
// 調(diào)用<Foo>(2)的函數(shù)
fool_u_ptr->function();
}
// 輸出:
// Obj is 000002C7BB550830, data = 1
// Obj is 000002C7BB557AF0, data = 2
關(guān)聯(lián)漏洞:
高風(fēng)險(xiǎn)-內(nèi)存破壞
更多建議: