- C和C++安全編碼(原書(shū)第2版)
- (美)Robert C.Seacord
- 1577字
- 2020-10-30 17:56:42
2.4.3 動(dòng)態(tài)分配函數(shù)
第二種內(nèi)存管理模型(由被調(diào)用者分配,由調(diào)用者釋放)是由ISO/IEC TR 24731-2定義的動(dòng)態(tài)分配函數(shù)實(shí)現(xiàn)。ISO/IEC TR 24731-2定義了許多標(biāo)準(zhǔn)C字符串處理函數(shù)的替代品,這些替代品使用動(dòng)態(tài)分配的內(nèi)存,以確保不會(huì)發(fā)生緩沖區(qū)溢出。因?yàn)槭褂眠@樣的函數(shù)需要引入隨后的釋放緩沖區(qū)的額外調(diào)用,所以這些函數(shù)更適用于新的開(kāi)發(fā),而不是改造現(xiàn)有代碼。
在一般情況下,因?yàn)樵贗SO/IEC TR 24731-2中描述的函數(shù),總是自動(dòng)調(diào)整緩沖區(qū)大小以容納所需的數(shù)據(jù),所以這些函數(shù)更好地確保了不會(huì)發(fā)生緩沖區(qū)溢出問(wèn)題。但是,使用動(dòng)態(tài)內(nèi)存分配的應(yīng)用程序,可能會(huì)遭受拒絕服務(wù)攻擊,因?yàn)槠渲械臄?shù)據(jù)會(huì)一直存在,直到內(nèi)存耗盡。它們也更容易出現(xiàn)動(dòng)態(tài)內(nèi)存管理錯(cuò)誤,這也可能導(dǎo)致安全漏洞。
例2.1可以使用動(dòng)態(tài)分配函數(shù)實(shí)現(xiàn),如例2.7中所示。
例2.7 使用函數(shù)getline()從stdin中讀入數(shù)據(jù)
01 #define __STDC_WANT_LIB_EXT2__ 1 02 #include <stdio.h> 03 #include <stdlib.h> 04 05 void get_y_or_n(void) { 06 char *response = NULL; 07 size_t len; 08 09 puts("Continue? [y] n: "); 10 if ((getline(&response, &len, stdin) < 0) || 11 (len && response[0] == 'n')) { 12 free(response); 13 exit(0); 14 } 15 free(response); 16 }
此程序?qū)τ谌魏屋斎攵季哂幸讯x的行為,包括一個(gè)假定,即假定一個(gè)非常長(zhǎng)的、需要耗盡所有可用內(nèi)存才能容納的行,應(yīng)被視為一個(gè)“no”回應(yīng)。因?yàn)閷?duì)getline()函數(shù)動(dòng)態(tài)地分配response緩沖區(qū),所以程序必須調(diào)用free()來(lái)釋放已分配的內(nèi)存。
ISO/IEC TR 24731-2允許在不相應(yīng)地打開(kāi)文件的情況下定義流。這種類(lèi)型的流從內(nèi)存緩沖區(qū)取得輸入或把輸出寫(xiě)入到內(nèi)存緩沖區(qū)。例如,GNU C庫(kù)使用這些流來(lái)實(shí)現(xiàn)sprintf()和sscanf()函數(shù)。
與內(nèi)存緩沖區(qū)相關(guān)的流和與外部文件關(guān)聯(lián)的文本文件流,具有相同的操作。此外,數(shù)據(jù)流的方向也是用完全相同的方式確定的。
你可以明確地使用fmemopen()、open_memstream()或open_wmemstream()函數(shù)創(chuàng)建一個(gè)字符串流。這些函數(shù)允許你對(duì)字符串或內(nèi)存緩沖區(qū)執(zhí)行I/O操作。fmemopen()和open_memstream()函數(shù)在<stdio.h>中被聲明,如下所示。
1 FILE *fmemopen( 2 void * restrict buf, size_t size, const char * restrict mode 3 ); 4 FILE *open_memstream( 5 char ** restrict bufp, size_t * restrict sizep 6 );
open_wmemstream()函數(shù)是在<wchar.h>中定義的,并具有以下簽名:
FILE *open_wmemstream(wchar_t **bufp, size_t *sizep);
fmemopen()函數(shù)打開(kāi)一個(gè)流,使你可以讀取或?qū)懭胫付ǖ木彌_區(qū)。open_memstream()函數(shù)打開(kāi)一個(gè)面向字節(jié)的流來(lái)寫(xiě)入一個(gè)緩沖區(qū),而open_wmemstream()函數(shù)創(chuàng)建一個(gè)面向?qū)捵址牧鳌.?dāng)用fclose()關(guān)閉流或用fflush()刷新流時(shí),bufp和sizep被更新,以包含緩沖區(qū)的指針及其大小。只要沒(méi)有進(jìn)一步的輸出流發(fā)生,這些值仍然有效。如果執(zhí)行額外的輸出,必須再次刷新流來(lái)存儲(chǔ)新的值,才能再次使用它們。一個(gè)空字符被寫(xiě)入緩沖區(qū)的末尾,但它存儲(chǔ)在sizep中的size值中不包括它。
通過(guò)調(diào)用fmemopen()、open_memstream()或open_wmemstream()創(chuàng)建的一個(gè)與內(nèi)存緩沖區(qū)相關(guān)聯(lián)的流的輸入和輸出操作,發(fā)生在內(nèi)存緩沖區(qū)的范圍內(nèi),受限于實(shí)現(xiàn)。對(duì)于用open_memstream()或open_wmemstream()打開(kāi)的流的情況,內(nèi)存區(qū)域動(dòng)態(tài)增長(zhǎng),以適應(yīng)必要的寫(xiě)操作。對(duì)于輸出,在刷新或關(guān)閉操作期間,數(shù)據(jù)從函數(shù)setvbuf()提供的緩沖區(qū)移動(dòng)到內(nèi)存流。如果沒(méi)有足夠的內(nèi)存來(lái)增長(zhǎng)內(nèi)存區(qū)域,或者操作需要訪問(wèn)相關(guān)內(nèi)存區(qū)域以外的地方,相關(guān)的操作失敗。
例2.8中的程序在第6行打開(kāi)一個(gè)流來(lái)寫(xiě)入到內(nèi)存。
例2.8 打開(kāi)一個(gè)流來(lái)寫(xiě)入內(nèi)存
01 #include <stdio.h> 02 03 int main(void) { 04 char *buf; 05 size_t size; 06 FILE *stream; 07 08 stream = open_memstream(&buf, &size); 09 if (stream == NULL) { /* handle error */ }; 10 fprintf(stream, "hello"); 11 fflush(stream); 12 printf("buf = '%s', size = %zu\n", buf, size); 13 fprintf(stream, ", world"); 14 fclose(stream); 15 printf("buf = '%s', size = %zu\n", buf, size); 16 free(buf); 17 return 0; 18 }
在第10行把字符串“hello”寫(xiě)入到流,并且在第11行刷新該流。fflush()的調(diào)用更新buf和size,以便第12行的printf()函數(shù)輸出:
buf = 'hello', size = 5
在第13行把字符串".world"寫(xiě)入流后,在第14行關(guān)閉流。關(guān)閉流的同時(shí)也更新buf和size,以便第15行的printf()函數(shù)輸出:
buf = 'hello, world', size = 12
size是緩沖區(qū)的累計(jì)(總數(shù))大小。open_memstream()函數(shù)提供了一個(gè)更安全的寫(xiě)入內(nèi)存機(jī)制,因?yàn)樗捎昧烁鶕?jù)需要?jiǎng)討B(tài)分配內(nèi)存的方法。但是,它確實(shí)要求調(diào)用者來(lái)釋放分配的內(nèi)存,如例子的第16行所示。
在安全關(guān)鍵的系統(tǒng)中,往往是不允許動(dòng)態(tài)分配的。例如,MISRA標(biāo)準(zhǔn)要求,“不得使用動(dòng)態(tài)堆內(nèi)存分配”[MISRA 2005]。一些安全關(guān)鍵系統(tǒng)在初始化過(guò)程中可以利用動(dòng)態(tài)內(nèi)存分配,但在操作過(guò)程中不允許。例如,航空電子軟件在初始化飛機(jī)時(shí)可以動(dòng)態(tài)地分配內(nèi)存,但在飛行過(guò)程中不允許。
動(dòng)態(tài)分配函數(shù)從廣泛應(yīng)用的現(xiàn)有實(shí)現(xiàn)中取得,許多這類(lèi)函數(shù)都包含在POSIX中。
- C++面向?qū)ο蟪绦蛟O(shè)計(jì)(第三版)
- Implementing Modern DevOps
- Java多線程編程實(shí)戰(zhàn)指南:設(shè)計(jì)模式篇(第2版)
- 國(guó)際大學(xué)生程序設(shè)計(jì)競(jìng)賽中山大學(xué)內(nèi)部選拔真題解(二)
- 薛定宇教授大講堂(卷Ⅳ):MATLAB最優(yōu)化計(jì)算
- Python高效開(kāi)發(fā)實(shí)戰(zhàn):Django、Tornado、Flask、Twisted(第2版)
- C語(yǔ)言程序設(shè)計(jì)實(shí)踐教程
- Python機(jī)器學(xué)習(xí)經(jīng)典實(shí)例
- 精通網(wǎng)絡(luò)視頻核心開(kāi)發(fā)技術(shù)
- Visual FoxPro程序設(shè)計(jì)
- PHP從入門(mén)到精通(第4版)(軟件開(kāi)發(fā)視頻大講堂)
- 區(qū)塊鏈技術(shù)進(jìn)階與實(shí)戰(zhàn)(第2版)
- 軟件測(cè)試綜合技術(shù)
- SpringBoot從零開(kāi)始學(xué)(視頻教學(xué)版)
- Node.js實(shí)戰(zhàn):分布式系統(tǒng)中的后端服務(wù)開(kāi)發(fā)