如何使用scanf避免缓冲区溢出

问题描述

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char **argv) {
    char first_name[20];
    char last_name[20];
    int student_num;
    char debts[1];

    printf("Enter name:");
    scanf("%s",first_name);
    printf("Enter lastname:");
    scanf("%s",last_name);
    printf("Enter six bits length student ID:");
    scanf("%d",&student_num);
    printf("Do you have debts for university [Y/N]?");
    scanf("%s",debts);

    printf("\nYour name is %s %s.\n",first_name,last_name);
    printf("Your Student ID is %d.\n",student_num);
    printf("Debts: %s.\n",debts);
    return (EXIT_SUCCESS);
}

如何避免此代码中的缓冲区溢出?我希望我的代码产生这样的输出

Enter name:Enescekilokoneyasharrontannamyantoniaaquin
Enter lastname:Basenau
Enter six bits length student ID:456789
Do you have debts for university [Y/N]?YES

Your name is **Enescekilokoneyashar** (only 20 bits from name input) *Basenau*.
Your Student ID is **393299**.
Debts: **Y**.

Process returned -1073741819 (0xC0000005)   execution time : 36.336 s
Press any key to continue.

我尝试使用:

scanf("%19s",first_name);

但是,它不符合我的预期。我需要找到另一种方法来验证输入参数,以防止缓冲区溢出攻击并将输入限制为缓冲区大小。

解决方法

您可以继续使用scanf,但需要将其占用的大小限制为目标缓冲区的大小,例如:

scanf(" %19[^\n]",first_name); //last destination buffer element is for null byte

请注意,该说明符可以解析空格,因此您可以接受一个以上单词的输入。

但是,它将在\n缓冲区中保留一个stdin字符,或者在您输入较大的字符串的情况下未解析的其余字符,因此您每次都需要清除它要解析新的输入,可以制作一个方便的函数,在需要这种清除时可以调用它,从而使其成为一种模式:

void clear_buffer(){
    int c;
    while ((c = getchar()) != '\n' && c != EOF){
        continue;
    }
}

您的char debts[1]缓冲区也是有问题的,它太小了,无法存储字符串,它必须至少有2个字符才能存储空终止符:

char debts[2];

呼叫将是:

clear_buffer();
scanf(" %1[^\n]",debts);

最后,您应该验证scanf的返回值,例如,在scanf("%d",&student_num)中,如果输入字母字符而不是数字,则该函数将无法解析并返回0,您也可以使其返回最多解析6位数字,类似于:

clear_buffer();
if(scanf("%6d",&student_num) == 0){ // %6d limit the input to 6 digits
    //handle error
}

Live demo

,

如果您在Windows上,则可以按照Microsoft文档here中的说明使用scanf_s()。 还有一个示例:

char s[10];
scanf_s("%9s",s,(unsigned)_countof(s));