CWE-193: Off-by-one Error

本文详细解析了C语言中常见的缓冲区溢出错误,并通过多个实例展示了如何正确使用字符串复制和拼接函数,以及如何避免此类错误。文章包括了错误代码示例、修复建议和相关知识点,旨在帮助开发者理解和预防缓冲区溢出问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://cwe.mitre.org/data/definitions/193.html


    char pData[501]="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    char pData2[501]="";
    CMD_GP_LogonError LogonError;
    int xx=CountArray(LogonError.szErrorDescribe);
    int x2=sizeof(pData);
    strncpy(pData2,pData,sizeof(pData));
    
    DBR_GP_LogonError * pLogonError=(DBR_GP_LogonError *)pData;
    pLogonError->szErrorDescribe[CountArray(pLogonError->szErrorDescribe)-1]=0;
    lstrcpyn(LogonError.szErrorDescribe,pLogonError->szErrorDescribe,CountArray(LogonError.szErrorDescribe));
    lstrcpyn(LogonError.szErrorDescribe,pLogonError->szErrorDescribe,sizeof(pData));
    return 0;

测试成功


Example 1

The following code allocates memory for a maximum number of widgets.It then gets a user-specified number of widgets, making sure that the userdoes not request too many. It then initializes the elements of the arrayusing InitializeWidget(). Because the number of widgets can vary for eachrequest, the code inserts a NULL pointer to signify the location of the lastwidget.

(Bad Code)
ExampleLanguage:
int i;
unsigned int numWidgets;
Widget **WidgetList;

numWidgets = GetUntrustedSizeValue();
if ((numWidgets == 0) || (numWidgets > MAX_NUM_WIDGETS)){
ExitError("Incorrect number of widgets requested!");
}
WidgetList = (Widget **)malloc(numWidgets * sizeof(Widget*));
printf("WidgetList ptr=%p\n", WidgetList);
for(i=0; i<numWidgets; i++) {
WidgetList[i] = InitializeWidget();
}
WidgetList[numWidgets] = NULL;
showWidgets(WidgetList);

However, this code contains an off-by-one calculation error. It allocates exactly enough space to contain the specified number of widgets, but it does not include the space for the NULL pointer. As a result, the allocated buffer is smaller than it is supposed to be (CWE-131). So if the user ever requests MAX_NUM_WIDGETS, there is an off-by-one buffer overflow when the NULL is assigned. Depending on the environment and compilation settings, this could cause memory corruption.

Example 2

The following C/C++ example demonstrates the Off-by-one error in themain method of a pattern matching utility that looks for a specific patternwithin a specific file. The main method uses the string copy method,strncpy, to copy the command line user input file name and pattern to theFilename and Pattern character arrays respectively.

(Bad Code)
ExampleLanguage:
int main(int argc, char **argv)
{
char Filename[256];
char Pattern[32];

/* Validate number of parameters and ensure valid content*/
...

/* copy filename parameter to variable, may causeoff-by-one overflow */
strncpy(Filename, argv[1], sizeof(Filename));

/* copy pattern parameter to variable, may cause off-by-oneoverflow */
strncpy(Pattern, argv[2], sizeof(Pattern));

printf("Searching file: %s for the pattern: %s\n", Filename,Pattern);
Scan_File(Filename, Pattern);
}

However, the calls to strncpy use the sizeof method call for the sizeparameter that does not take into account that the strncpy will add anull terminator to each character array. Therefore if a user enters afilename or pattern that are the same size as (or larger than) theirrespective character arrays a null terminator will be added beyond theend of the buffer for the character arrays creating an off-by-one bufferoverflow. In addition to creating a buffer overflow that may cause amemory address to be overwritten, if the character arrays are output tothe user through the printf method the memory addresses at the overflowlocation may be output to the user.

To fix this problem, be sure to subtract 1 from the sizeof() call toallow room for the null byte to be added.

(Good Code)
ExampleLanguage:
/* copy filename parameter to variable, no off-by-one overflow*/
strncpy(Filename, argv[2], sizeof(Filename)-1);

/* copy pattern parameter to variable, no off-by-one overflow*/
strncpy(Pattern, argv[3], sizeof(Pattern)-1);

Example 3

Similarly, this example uses the strncat and snprintf functionsincorrectly. The code does not account for the null character that is addedby the second strncat function call, one byte beyond the end of the namebuffer.

(Bad Code)
ExampleLanguage:
char lastname[20];
char firstname[20];
char name[40];
char fullname[40];

strncat(name, firstname, sizeof(name));
strncat(name, lastname, sizeof(name));
snprintf(fullname, sizeof(fullname), "%s", name);

By leaving a free byte at the end of the buffers for a null characterto be added, the off-by-one weakness is avoided.

(Good Code)
ExampleLanguage:
char lastname[20];
char firstname[20];
char name[40];
char fullname[40];

strncat(name, firstname, sizeof(name)-1);
strncat(name, lastname, sizeof(name)-1);
snprintf(fullname, sizeof(fullname)-1), "%s", name);

Example 4

The Off-by-one error can also be manifested when reading charactersfrom a character array within a for loop that has an incorrect continuationcondition.

(Bad Code)
ExampleLanguage:
#define PATH_SIZE 60

char filename[PATH_SIZE];

for(i=0; i<=PATH_SIZE; i++) {

char c = getc();
if (c == 'EOF') {
filename[i] = '\0';
}

filename[i] = getc();
}

In this case, the correct continuation condition is shownbelow.

(Good Code)
ExampleLanguage:
for(i=0; i<PATH_SIZE; i++) {
...

Example 5

As another example the Off-by-one error can occur when using thesprintf library function to copy a string variable to a formatted stringvariable and the original string variable comes from an untrusted source. Asin the following example where a local function, setFilename is used tostore the value of a filename to a database but first uses sprintf to formatthe filename. The setFilename function includes an input parameter with thename of the file that is used as the copy source in the sprintf function.The sprintf function will copy the file name to a char array of size 20 andspecifies the format of the new variable as 16 characters followed by thefile extension .dat.

(Bad Code)
ExampleLanguage:
int setFilename(char *filename) {
char name[20];
sprintf(name, "%16s.dat", filename);
int success = saveFormattedFilenameToDB(name);
return success;
}

However this will cause an Off-by-one error if the original filenameis exactly 16 characters or larger because the format of 16 characterswith the file extension is exactly 20 characters and does not take intoaccount the required null terminator that will be placed at the end ofthe string.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值