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

2021-05-28 10:19 更新

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

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

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

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

正確例子

  1. void Foo() {
  2. int len = 1;
  3. char payload[10] = { 0 };
  4. char dst[10] = { 0 };
  5. int size = len - kMicLen;
  6. // 拷貝前對長度進(jìn)行判斷
  7. if (size > 0 && size < 10) {
  8. memcpy(dst, payload, size);
  9. printf("memcpy good\n");
  10. }
  11. }

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

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

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

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

錯誤:

  1. char firstname[20];
  2. char lastname[20];
  3. char fullname[40];
  4. fullname[0] = '\0';
  5. strncat(fullname, firstname, 20);
  6. // 第二次調(diào)用strncat()可能會追加另外20個字符。如果這20個字符沒有終止空字符,則存在安全問題
  7. strncat(fullname, lastname, 20);

正確:

  1. char firstname[20];
  2. char lastname[20];
  3. char fullname[40];
  4. fullname[0] = '\0';
  5. // 當(dāng)使用像strncat()函數(shù)時,必須在緩沖區(qū)的末尾為終止空字符留下一個空字節(jié),避免off-by-one
  6. strncat(fullname, firstname, sizeof(fullname) - strlen(fullname) - 1);
  7. strncat(fullname, lastname, sizeof(fullname) - strlen(fullname) - 1);

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

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

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

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

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

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

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

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

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

錯誤:

  1. double divide(double x, double y) {
  2. return x / y;
  3. }
  4. int divide(int x, int y) {
  5. return x / y;
  6. }

正確:

  1. double divide(double x, double y) {
  2. if (y == 0) {
  3. throw DivideByZero;
  4. }
  5. return x / y;
  6. }

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

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

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

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

錯誤例子

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

正確例子

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

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

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

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

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

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

錯誤:

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

正確:

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號