понедельник, 23 августа 2010 г.

Расширение библиотеки GNU C для безопасного ввода строк.

    Не правильное применение функции scanf приводит к проблемам с безопасностью. Ниже приведён код, в котором произойдет переполнение буфера при введении 10 и более символов:  

    char str[10];
    scanf("%s", str);

    Чтобы избежать переполнения можно указать ширину поля:

    char str[10];
    scanf("%9s", str);

    В этом случае будет прочитано не более 9 символов, но если было введено больше символов, то в буфере stdin остаются лишние символы и перед следующим чтением буфер нужно очистить. Для избежания переполнения буфера, при использовании функции scanf, библиотека GNU C поддерживает не стандартное расширение, которое автоматически выделять достаточно память под вводимые данные. Для использования этого расширения нужно указать модификатор a как модификатор длинный %as:

    char *str;
    if ( scanf("%as", &str) == 1 )
    // указатель на char* указатель для адреса буфера
    {
        printf("%s\n", str);
        free(str);
    }

    При успешном выполнении функции scanf вызывающий код должен освободить выделенную память функцией free. Модификатор a не доступен если программа компилируется с флагом -std=c99 или -D_ISOC99_SOURCE. Начиная с версии 2.7 библиотека поддерживает модификатор m, который действует как a, но доступен при -std=c99.
    Вместо функции scanf рекомендуется пользоваться другим расширением библиотека GNU C, функцией getline, которая позволяет надёжно читать строку произвольной длинны и самостоятельно выделяет необходимый объёма памяти:

    ssize_t getline(char **lineptr, size_t *n, FILE *stream);
    lineptr — указатель на char* указатель для адреса динамически выделенного буфера.
                    Если инициализирован NULL, getline сам выделит память иначе должен
                    указывать на буфер выделенный с помощью функции malloc.
    n — указатель на размер буфера. Если буфер выделен с помощью malloc, *n должно
            содержать размер буфера, иначе 0.
    stream — поток, откуда читаются данные.


    Пример использования:

    #define _GNU_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    char* pUserName = NULL;
    size_t bytes_read, nbytes = 0;

    bytes_read = getline(&pUserName, &nbytes, stdin);
    if ( bytes_read != -1 )
    {
        printf("%s", pUserName);
    }

    if ( pUserName )
        free(pUserName);

    При достижении конца файла или ошибке функция возвращает -1. В случае успешного завершения функция возвращает число прочитанных символов (включая символ перевода строки, но не, включая пустой символ завершения) и может обновлять значения *lineptr и *n, если буфер изменялся.

Комментариев нет:

Отправить комментарий