sprintf () сошел с ума - PullRequest
       29

sprintf () сошел с ума

1 голос
/ 13 сентября 2009

Мне нужна помощь с этим, так как это ставит меня в тупик в моей программе на C

У меня есть 2 строки (база и путь)

BASE: /home/steve/cps730
PATH: /page2.html

это то, как printf читает тогда перед тем, как я вызываю sprintf, чтобы объединить их содержимое. вот кодовый блок

        int memory_alloc = strlen(filepath)+1;
        memory_alloc += strlen(BASE_DIR)+1;
        printf("\n\nAlloc: %d",memory_alloc);
        char *input = (char*)malloc(memory_alloc+9000);
        printf("\n\nBASE: %s\nPATH: %s\n\n",BASE_DIR,filepath);
        sprintf(input, "%s%s",BASE_DIR,filepath); //   :(

        printf("\n\nPATH: %s\n\n",input);

Теперь, вы можете объяснить, как возвращает окончательный оператор printf

PATH: e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/stev

потому что он вообще этого не понимает.

** Я добавил 9000 в оператор malloc, чтобы предотвратить сбой программы (поскольку размер строки, очевидно, больше, чем 31 байт.

Полный вывод

Alloc: 31

BASE: /home/steve/cps730
PATH: /page2.html



PATH: /home/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/steve/cps730e/stev

Sending: 
HTTP/1.0 404 Not Found
Date: Sat, 12 Sep 2009 19:01:53 GMT
Connection: close

РЕДАКТИРОВАТЬ ................... Весь код, который использует эти переменные

const char *BASE_DIR = "/home/steve/cps730";
 char* handleHeader(char *header){
    //Method given by browser (will only take GET, POST, and HEAD)
    char *method;
    method = (char*)malloc(strlen(header)+1);
    strcpy(method,header);
    method = strtok(method," ");

    if(!strcmp(method,"GET")){
        char *path = strtok(NULL," ");
        if(!strcmp(path,"/")){
            path = (char*)malloc(strlen(BASE_DIR)+1+12);
            strcpy(path,"/index.html");
        }
        free(method);
        return readPage(path);
    }
    else if(!strcmp(method,"POST")){

    }
    else if(!strcmp(method,"HEAD")){

    }
    else{
        strcat(contents,"HTTP/1.1 501 Not Implemented\n");
                strcat(contents, "Date: Sat, 12 Sep 2009 19:01:53 GMT\n");
                strcat(contents, "Connection: close\n\n");
    }
    free(method);

}

//Return the contents of an HTML file
char* readPage(char* filepath){
    int memory_alloc = strlen(filepath)+1;
    memory_alloc += strlen(BASE_DIR)+1;
    printf("\n\nAlloc: %d",memory_alloc);
    char *input = (char*)malloc(memory_alloc+9000); 
    printf("\n\nBASE: %s\nPATH: %s\n\n",BASE_DIR,filepath);
    sprintf(input, "%s%s\0",BASE_DIR,filepath);

    printf("\n\nPATH: %s\n\n",input);

    FILE *file;
    file = fopen(input, "r");
    char temp[255];
    strcat(contents,"");

    if(file){
        strcat(contents, "HTTP/1.1 200 OK\n");
                strcat(contents, "Date: Sat, 12 Sep 2009 19:01:53 GMT\n");
                strcat(contents, "Content-Type: text/html; charset=utf-8\n");
                strcat(contents, "Connection: close\n\n");

        //Read the requested file line by line
        while(fgets(temp, 255, file)!=NULL) { 
            strcat(contents, temp);         
        }
    }
    else{
        strcat(contents, "HTTP/1.0 404 Not Found\n");
                strcat(contents, "Date: Sat, 12 Sep 2009 19:01:53 GMT\n");
                strcat(contents, "Connection: close\n\n");
    }

    return contents;
}

Ответы [ 8 ]

6 голосов
/ 13 сентября 2009

Вы вызываете readPage с недействительным указателем path - он указывает на память, ранее выделенную указателем method, которая освобождается непосредственно перед вызовом readPage. Следующее malloc может повторно использовать эту память, и тогда может случиться что угодно ...

4 голосов
/ 13 сентября 2009

Ну, ясно, что этого не может быть: -)

Я предполагаю, что ваша куча уже ужасно испорчена.

Я бы посмотрел на фактические значения указателя, используемые filepath, input и base. Интересно, вы обнаружите, что ввод очень близок к filepath?

Я бы также посмотрел, как изначально были созданы filepath, base и т.д., не могли бы вы переполнить буфер там?

1 голос
/ 13 сентября 2009

Чтобы сделать это правильно, я бы изменил код на:

/* CHANGED: allocate additional space for "index.html" */
method = (char*)malloc(strlen(header)+1+10);
strcpy(method,header);
method = strtok(method," ");

if(!strcmp(method,"GET")){
    char *path = strtok(NULL," ");
    if(!strcmp(path,"/")){
             /* CHANGED: don't allocate new memory, use previously allocated */
             strcpy(path,"/index.html");
    }
    /* CHANGED: call function first and free memory _after_ the call */
    char *result = readPage(path);
    free(method);
    return result;
}
1 голос
/ 13 сентября 2009

Самый простой способ выяснить, что происходит, - это проследить выполнение в отладчике (возможно, перейдя к отслеживанию кода сборки).

Несколько предположений относительно того, что может происходить:

  • повреждение памяти другим потоком (кажется маловероятным, если это легко повторить)
  • поврежденная куча (также маловероятно, так как вы выкидываете 2 строки компонента после вызова malloc())
  • , как упоминал Джонатан Леффлер в комментарии, вы можете пропустить заголовок (возможно, stdio.h), и компилятор генерирует неправильную последовательность вызовов / очистки стека для вызовов printf / sprintf. Я ожидаю, что вы увидите некоторые предупреждения времени компиляции, если бы это было так - те, которые вы должны принять к сведению.

Какой компилятор / цель вы используете?

1 голос
/ 13 сентября 2009

Поскольку значение BASE_DIR повторяется, либо BASE_DIR, либо filepath, вероятно, перекрывают входящую в input память.

Убедитесь, что и BASE_DIR, и filepath действительно выделяют память.

Первая попытка - просто сделать локальную копию BASE_DIR и filepath перед вызовом sprintf.

1 голос
/ 13 сентября 2009

Попробуйте этот код:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    const char* BASE_DIR = "/home/steve/cps730";
    const char* filepath = "/page2.html";
    int memory_alloc = strlen(filepath);
    memory_alloc += strlen(BASE_DIR)+1;
    printf("\n\nAlloc: %d",memory_alloc);
    char *input = (char*)malloc(memory_alloc);
    printf("\n\nBASE: %s\nPATH: %s\n\n",BASE_DIR,filepath);
    sprintf(input, "%s%s",BASE_DIR,filepath); //   :(

    printf("\n\nPATH: %s\n\n",input);

    return 0;
}

Если с этим нет проблем, то в другом месте кода должно быть что-то не так. Вот как иногда может проявляться неопределенное поведение (портить работу несвязанного кода).

(Кстати, я не добавил +1 к обоим вызовам strlen, поскольку в объединенной строке все еще будет только один нулевой терминатор.)

1 голос
/ 13 сентября 2009

Предложения


Нет ничего плохого в программе. ( Обновление: хорошо, теперь есть что-то очевидное. За первый час было опубликовано всего несколько строк, и у них не было серьезных ошибок. придется опубликовать больше об этом. Вот несколько идей:

  1. malloc(3) возвращает void *, поэтому нет необходимости его разыгрывать. Если вы получаете предупреждение, скорее всего, это означает, что вы не включили <stdlib.h>. Если нет, вам следует. (Например, в 64-битной системе не прототипирование malloc(3) может быть достаточно серьезным. Некоторые из 64-битных сред на самом деле не поддерживают K & R C.: -)
  2. Говоря о предупреждениях, пожалуйста, убедитесь, что вы их все включаете. С помощью gcc вы можете включить большинство из них с помощью -Wall.
  3. Вы не проверяете возвращаемое значение malloc(3) на наличие ошибки.
  4. Используйте отладчик памяти как Electric Fence . Есть много вариантов, см. Мою ссылку.
1 голос
/ 13 сентября 2009

А-а-а - ощущения от погони, когда вопрос трансформируется, пока мы пытаемся решить проблему!

Текущий код выглядит так:

const char *BASE_DIR = "/home/steve/cps730";

//Handles the header sent by the browser
char* handleHeader(char *header){
    //Method given by browser (will only take GET, POST, and HEAD)
    char *method;
    method = (char*)malloc(strlen(header)+1);
    strcpy(method,header);
    method = strtok(method," ");

    if(!strcmp(method,"GET")){
        char *path = strtok(NULL," ");
        if(!strcmp(path,"/")){
                path = (char*)malloc(strlen(BASE_DIR)+1+12);
                strcpy(path,"/index.html");
        }
        free(method);
        return readPage(path);
    }
    ...

Вопрос: если это выполняется на веб-сервере, безопасно ли использовать функцию небезопасного потока strtok()? Я собираюсь предположить «Да, это безопасно», хотя я не совсем убежден. Вы напечатали строку header? Вы напечатали значение path? Вы действительно намеревались утечь выделенный path? Вы поняли, что последовательность malloc() + strcpy() не копирует BASE_DIR в path?


Исходная версия кода закончилась на:

 printf("\n\nPATH: %s\n\n", filepath);

Отсюда и первоначальный частичный ответ:

Вы форматируете в input; вы печатаете с filepath?


Какова вероятность того, что filepath указывает на уже освобожденную память? Когда вы распределяете память, вы можете получить что-нибудь, происходящее с квазислучайной областью, на которую filepath указывает. Другая возможность может заключаться в том, что filepath является указателем на локальную переменную в функции, которая возвратилась, поэтому он указывает на случайное место в стеке, которое используется другим кодом, таким как sprintf().

Я также упомянул в комментарии, что вам, возможно, понадобится убедиться, что объявлено malloc(), и проверить возвращаемое значение из него. Приведение «(char *)» не является обязательным в C (оно в C ++), и многие предпочитают не включать приведение, если код строго C и не двуязычный в C и C ++.


У меня работает этот код:

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

int main(void)
{
    const char *BASE_DIR = "/home/steve/cps730";
    const char *filepath = "/page2.html";

    int memory_alloc = strlen(filepath) + 1;
    memory_alloc += strlen(BASE_DIR) + 1;
    printf("\n\nAlloc: %d", memory_alloc);
    char *input = (char*)malloc(memory_alloc + 9000);
    printf("\n\nBASE: %s\nPATH: %s\n\n", BASE_DIR, filepath);
    sprintf(input, "%s%s", BASE_DIR, filepath);

    printf("\n\nPATH: %s\n\n", filepath);
    printf("\n\nPATH: %s\n\n", input);

    return(0);
}

Создает посторонние пустые строки плюс:

Alloc: 31
BASE: /home/steve/cps730
PATH: /page2.html
PATH: /page2.html
PATH: /home/steve/cps730/page2.html
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...