1

This function is in a socket server. When the client sends a query, the server takes the query and finds matches from a linked list. The function works fine for the first few queries, and then a segmentation fault occurs. The problem occurs at the sprintf call(the one after "Before sprintf.\n"). I really don't understand why it works just for a few times. What have I done wrong?

char* searchNode(char* query) {
    int i, isFound, count = 0;
    node* temp = head;
    char* searchResult = calloc(1, sizeof(* searchResult));
    char* finalResult = calloc(1, sizeof(* finalResult));;

    printf("Before search node.\n");

    while(temp->next) {
        isFound = TRUE;
        temp = temp->next;
        for(i = 0; i < strlen(query); i++) { /* compare each char in both strings */
            if(tolower(query[i]) != tolower(temp->foodName[i])) {
                isFound = FALSE;
                break;
            }
        }
        if(isFound == TRUE) { /* if a match is found, write it into the temp string */
        printf("Match found.\n");
            searchResult = realloc(searchResult, strlen(searchResult) + 1 + strlen(nodeToString(temp)) + 1);
        printf("Before sprintf.\n");
            sprintf(searchResult, "%s%s", searchResult, nodeToString(temp));
            count++; /* count the number of results found */
        }
    }

    printf("Before finalise string.\n");

    if(count > 0) { /* if at least a result is found, add combine all results with a head line*/
        sprintf(finalResult, "%d food item(s) found.\n\n", count);
        strcat(finalResult, searchResult);
        free(searchResult);
        return finalResult;
    }

    /* if no match is found, return this message */
    return "No food item found.\nPlease check your spelling and try again.\n";
}
Kolya H.
  • 19
  • 1
  • 2
  • 3
    You are trying to print the string into itself. If you want to append one string to another, consider using `strcat` or `strncat`. – M Oehm Oct 27 '14 at 17:21
  • this line: node* temp = head; seems to be referencing a variable 'head' that is not defined. – user3629249 Oct 27 '14 at 21:49
  • this kind of line: char* searchResult = calloc(1, sizeof(* searchResult)); is unlikely to work because (in the beginning) searchResult is not pointed to anything in particular so asking for a dereferenced size is most likely to return 4, if anything useful – user3629249 Oct 27 '14 at 21:52
  • the (currently) int variable isFound is being set with either TRUE or FALSE. However, it is not being initialized to either value and should be defined on a separate line as bool isFound = FALSE; – user3629249 Oct 27 '14 at 21:55
  • this line: sprintf(finalResult, "%d food item(s) found.\n\n", count); will result in undefined behaviour because it does not point to an area long enough to contain the text being placed into it. – user3629249 Oct 27 '14 at 21:58
  • regarding this line: if(isFound == TRUE) your code should NEVER compare against TRUE because TRUE can be anything. much better to either: 1) if(isFound) or 2) if(isFound != FALSE) – user3629249 Oct 27 '14 at 22:02

4 Answers4

1

You forgot to test the success of  calloc. And you are using it incorrectly: you need to allocate enough bytes for the 0-terminated string in it.

Notice that char* searchResult = calloc(1, sizeof(* searchResult)); is deeply wrong: it is equivalent to /* wrong code*/ char* searchResult= calloc(1,1); and you cannot do that (you need to allocate a wide enough string); you've got some undefined behavior and you have the bad luck that it does not crash (SO contains a lot of answers about UB, see e.g. this one).

You should use snprintf(3) (perhaps with strdup(3)) and you should take into account the result of snprintf + 1 for the terminating zero byte. You may want to use asprintf(3) if your system provides it.

Please compile with all warnings and debug info gcc -Wall -Wextra -g. Use valgrind and the gdb debugger.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • 2
    it's amazing (in the bad sense) how people have no idea about the safe versions of stdlib functions and massively prefer the dangerous variations thereof... – The Paramagnetic Croissant Oct 27 '14 at 17:21
  • I'd say the OP simply forgot(?) to realloc what `finalResult` shall finally be pointing to. – alk Oct 27 '14 at 17:23
  • Thanks for answering. But why would it work for the first few calls and eventually crash? That really bothers me a lot. If it's not right, it shouldn't work at all. But why? I've been stuck with this for days. I still don't get it. – Kolya H. Oct 27 '14 at 17:35
  • @KolyaH: you are wrong: UB is not right, but unfortunately, it may give the appearance to perhaps work sometimes. And UB sadly *does **not always** crash* ! (Cf some of my answers mentioning UB) – Basile Starynkevitch Oct 27 '14 at 19:08
1

I don't know what that sprintf is going to do when passed searchResult as an argument. The man page on my system suggests that it is undefined:

C99 and POSIX.1-2001 specify that the results are undefined if a call to sprintf(), snprintf(), vsprintf(), or vsnprintf() would cause copying to take place between objects that overlap (e.g., if the target string array and one of the supplied input arguments refer to the same buffer).

You should probably be using strcat there instead.

hcs
  • 1,514
  • 9
  • 14
1

just read the failing line

 sprintf(searchResult, "%s%s", searchResult, nodeToString(temp));

It says print searchResult and other stuff into searchResult. It cant possibly work

searchResult is not the Tardis

pm100
  • 48,078
  • 23
  • 82
  • 145
0

You are adjusting what searchResult points to, but finalResult stays pointing to 1 char only, although it finally gets copied all the search result into, which most propably is more the 1 char, with this writing to invalid memory and provoking undefined behaviour.

alk
  • 69,737
  • 10
  • 105
  • 255