C/C++ 數(shù)字操作

2021-05-28 10:19 更新

7.1 【必須】防止整數(shù)溢出

在計算時需要考慮整數(shù)溢出的可能,尤其在進行內(nèi)存操作時,需要對分配、拷貝等大小進行合法校驗,防止整數(shù)溢出導(dǎo)致的漏洞。

錯誤(該例子在計算時產(chǎn)生整數(shù)溢出)

const kMicLen = 4;
// 整數(shù)溢出
void Foo() {
  int len = 1;
  char payload[10] = { 0 };
  char dst[10] = { 0 };
  // Bad, 由于len小于4字節(jié),導(dǎo)致計算拷貝長度時,整數(shù)溢出
  // len - MIC_LEN == 0xfffffffd
  memcpy(dst, payload, len - kMicLen);
}

正確例子

void Foo() {
  int len = 1;
  char payload[10] = { 0 };
  char dst[10] = { 0 };
  int size = len - kMicLen;
  // 拷貝前對長度進行判斷
  if (size > 0 && size < 10) {
    memcpy(dst, payload, size);
    printf("memcpy good\n");
  }
}

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

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

7.2 【必須】防止Off-By-One

在進行計算或者操作時,如果使用的最大值或最小值不正確,使得該值比正確值多1或少1,可能導(dǎo)致安全風(fēng)險。

錯誤:

char firstname[20];
char lastname[20];
char fullname[40];


fullname[0] = '\0';


strncat(fullname, firstname, 20);
// 第二次調(diào)用strncat()可能會追加另外20個字符。如果這20個字符沒有終止空字符,則存在安全問題
strncat(fullname, lastname, 20);

正確:

char firstname[20];
char lastname[20];
char fullname[40];


fullname[0] = '\0';


// 當使用像strncat()函數(shù)時,必須在緩沖區(qū)的末尾為終止空字符留下一個空字節(jié),避免off-by-one
strncat(fullname, firstname, sizeof(fullname) - strlen(fullname) - 1);
strncat(fullname, lastname, sizeof(fullname) - strlen(fullname) - 1);

對于 C++ 代碼,再次強烈建議使用 stringvector 等組件代替原始指針和數(shù)組操作。

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

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

7.3 【必須】避免大小端錯誤

在一些涉及大小端數(shù)據(jù)處理的場景,需要進行大小端判斷,例如從大段設(shè)備取出的值,要以大段進行處理,避免端序錯誤使用。

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

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

7.4 【必須】檢查除以零異常

在進行除法運算時,需要判斷被除數(shù)是否為零,以防導(dǎo)致程序不符合預(yù)期或者崩潰。

錯誤:

double divide(double x, double y) {
  return x / y;
}


int divide(int x, int y) {
  return x / y;
}

正確:

double divide(double x, double y) {
  if (y == 0) {
    throw DivideByZero;
  }
  return x / y;
}

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

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

7.5 【必須】防止數(shù)字類型的錯誤強轉(zhuǎn)

在有符號和無符號數(shù)字參與的運算中,需要注意類型強轉(zhuǎn)可能導(dǎo)致的邏輯錯誤,建議指定參與計算時數(shù)字的類型或者統(tǒng)一類型參與計算。

錯誤例子

int Foo() {
  int len = 1;
  unsigned int size = 9;
  // 1 < 9 - 10 ? 由于運算中無符號和有符號混用,導(dǎo)致計算結(jié)果以無符號計算
  if (len < size - 10) {
    printf("Bad\n");
  } else {
    printf("Good\n");
  }
}

正確例子

void Foo() {
  // 統(tǒng)一兩者計算類型為有符號
  int len = 1;
  int size = 9;
  if (len < size - 10) {
    printf("Bad\n");
  } else {
    printf("Good\n");
  }
}

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

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

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

7.6 【必須】比較數(shù)據(jù)大小時加上最小/最大值的校驗

在進行數(shù)據(jù)大小比較時,要合理地校驗數(shù)據(jù)的區(qū)間范圍,建議根據(jù)數(shù)字類型,對其進行最大和最小值的判斷,以防止非預(yù)期錯誤。

錯誤:

void Foo(int index) {
  int a[30] = {0};
  // 此處index是int型,只考慮了index小于數(shù)組大小,但是并未判斷是否大于0
  if (index < 30) {
    // 如果index為負數(shù),則越界
    a[index] = 1;
  }
}

正確:

void Foo(int index) {
  int a[30] = {0};
  // 判斷index的最大最小值
  if (index >=0 && index < 30) {
    a[index] = 1;
  }
}

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

  • 高風(fēng)險-內(nèi)存破壞
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號