C/C++ 指針操作

2021-05-28 10:22 更新

8.1 【建議】檢查在pointer上使用sizeof

除了測試當(dāng)前指針長度,否則一般不會在pointer上使用sizeof。

正確:

size_t pointer_length = sizeof(void*);

可能錯誤:

size_t structure_length = sizeof(Foo*);

可能是:

size_t structure_length = sizeof(Foo);

關(guān)聯(lián)漏洞:

  • 中風(fēng)險-邏輯漏洞

8.2 【必須】檢查直接將數(shù)組和0比較的代碼

錯誤:

int a[3];
...;


if (a > 0)
  ...;

該判斷永遠(yuǎn)為真,等價于:

int a[3];
...;


if (&a[0])
  ...;

可能是:

int a[3];
...;


if(a[0] > 0)
  ...;

開啟足夠的編譯器警告(GCC 中為 -Waddress,并已包含在 -Wall 中),并設(shè)置為錯誤,可以在編譯期間發(fā)現(xiàn)該問題。

關(guān)聯(lián)漏洞:

  • 中風(fēng)險-邏輯漏洞

8.3 【必須】不應(yīng)當(dāng)向指針賦予寫死的地址

特殊情況需要特殊對待(比如開發(fā)硬件固件時可能需要寫死)

但是如果是系統(tǒng)驅(qū)動開發(fā)之類的,寫死可能會導(dǎo)致后續(xù)的問題。

關(guān)聯(lián)漏洞:

  • 高風(fēng)險-內(nèi)存破壞

8.4 【必須】檢查空指針

錯誤:

*foo = 100;


if (!foo) {
  ERROR("foobar");
}

正確:

if (!foo) {
  ERROR("foobar");
}


*foo = 100;

錯誤:

void Foo(char* bar) {
  *bar = '\0';
}

正確:

void Foo(char* bar) {
  if(bar)
    *bar = '\0';
  else
    ...;
}

關(guān)聯(lián)漏洞:

  • 低風(fēng)險-拒絕服務(wù)

8.5 【必須】釋放完后置空指針

在對指針進(jìn)行釋放后,需要將該指針設(shè)置為NULL,以防止后續(xù)free指針的誤用,導(dǎo)致UAF等其他內(nèi)存破壞問題。尤其是在結(jié)構(gòu)體、類里面存儲的原始指針。

錯誤:

void foo() {
  char* p = (char*)malloc(100);
  memcpy(p, "hello", 6);
  // 此時p所指向的內(nèi)存已被釋放,但是p所指的地址仍然不變
  printf("%s\n", p);
  free(p);
  // 未設(shè)置為NULL,可能導(dǎo)致UAF等內(nèi)存錯誤


  if (p != NULL) {  // 沒有起到防錯作用
    printf("%s\n", p); // 錯誤使用已經(jīng)釋放的內(nèi)存
  }
}

正確:

void foo() {
  char* p = (char*)malloc(100);
  memcpy(p, "hello", 6);
  // 此時p所指向的內(nèi)存已被釋放,但是p所指的地址仍然不變
  printf("%s\n", p);
  free(p);
  //釋放后將指針賦值為空
  p = NULL;
  if (p != NULL)  { // 沒有起到防錯作用
    printf("%s\n", p); // 錯誤使用已經(jīng)釋放的內(nèi)存
  }
}

對于 C++ 代碼,使用 string、vector、智能指針等代替原始內(nèi)存管理機(jī)制,可以大量減少這類錯誤。

關(guān)聯(lián)漏洞:

  • 高風(fēng)險-內(nèi)存破壞

8.6 【必須】防止錯誤的類型轉(zhuǎn)換(type confusion)

在對指針、對象或變量進(jìn)行操作時,需要能夠正確判斷所操作對象的原始類型。如果使用了與原始類型不兼容的類型進(jìn)行訪問,則存在安全隱患。

錯誤:

const int NAME_TYPE = 1;
const int ID_TYPE = 2;


// 該類型根據(jù) msg_type 進(jìn)行區(qū)分,如果在對MessageBuffer進(jìn)行操作時沒有判斷目標(biāo)對象,則存在類型混淆
struct MessageBuffer {
  int msg_type;
  union {
    const char *name;
    int name_id;
  };
};


void Foo() {
  struct MessageBuffer buf;
  const char* default_message = "Hello World";
  // 設(shè)置該消息類型為 NAME_TYPE,因此buf預(yù)期的類型為 msg_type + name
  buf.msg_type = NAME_TYPE;
  buf.name = default_message;
  printf("Pointer of buf.name is %p\n", buf.name);


  // 沒有判斷目標(biāo)消息類型是否為ID_TYPE,直接修改nameID,導(dǎo)致類型混淆
  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作為類型操作,可能導(dǎo)致非法內(nèi)存讀寫
    printf("Message: %s\n", buf.name);
  } else {
    printf("Message: Use ID %d\n", buf.name_id);
  }
}

正確(判斷操作的目標(biāo)是否是預(yù)期類型):

void Foo() {
  struct MessageBuffer buf;
  const char* default_message = "Hello World";
  // 設(shè)置該消息類型為 NAME_TYPE,因此buf預(yù)期的類型為 msg_type + name
  buf.msg_type = NAME_TYPE;
  buf.name = default_msessage;
  printf("Pointer of buf.name is %p\n", buf.name);


  // 判斷目標(biāo)消息類型是否為 ID_TYPE,不是預(yù)期類型則做對應(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)險-內(nèi)存破壞

8.7 【必須】智能指針使用安全

在使用智能指針時,防止其和原始指針的混用,否則可能導(dǎo)致對象生命周期問題,例如 UAF 等安全風(fēng)險。

錯誤例子:

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ú)占智能指針重新賦值后會釋放內(nèi)存
  fool_u_ptr = make_unique<Foo>(2);
  // 通過原始指針操作會導(dǎo)致UAF,pfool_raw_ptr指向的對象已經(jīng)釋放
  pfool_raw_ptr->Function();
}




// 輸出:
// Obj is 0000027943087B80, data = 1
// Obj is 0000027943087B80, data = -572662307

正確,通過智能指針操作:

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)險-內(nèi)存破壞
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號