cbsearch

bsearch() in C giving segmentation fault in new gcc version 7.4.0


I have an old program in C which uses bsearch() function with strcmp() from the C library. In old gcc version4.4.7, it is running properly. But in latest Ubuntu 18.04 with gcc version7.4.0, it is giving segmentation fault. Code is given below:

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <search.h>

#define MAX_CHR_IN_STR 50 
#define MAX_CHR_IN_DEMO_STR 86
#define GENDER 4
#define NUMBER 4
#define PERSON 4
#define TOTAL_ENTRY 2
#define MEDIUM 50

struct mytam_gnpstr {
    char mytam[MAX_CHR_IN_STR];
    char mytam_lbl[MAX_CHR_IN_STR];
    char gnp_str[MAX_CHR_IN_DEMO_STR];
    char gen_pos[GENDER]; 
    char num_pos[NUMBER]; 
    char per_pos[PERSON]; 
};

struct mytam_gnpstr mytam_gnpstr_array[TOTAL_ENTRY] = {
    "0", "0", "0[-,s,m]", "0", "s", "m",
    "0_0_kara", "0_0_kara", "02[-,-,-]kara_0[-,-,-]", "0", "0", "0",
};

int main(void) {
    char *rtamexample;
    char TAM[MEDIUM] = "wA";
    fprintf(stderr, "TAM :::::::: %s\n", TAM);
    fprintf(stderr, "mytam_gnpstr_array[0].mytam :::::::: %s\n",
            mytam_gnpstr_array[0].mytam);
    fprintf(stderr, "TOTAL_ENTRY :::::::: %d\n", TOTAL_ENTRY);
    fprintf(stderr, "sizeof(mytam_gnpstr_array[0]) :::::::: %zu\n",
            sizeof(mytam_gnpstr_array[0]));

    rtamexample = (char *)bsearch(TAM, mytam_gnpstr_array[0].mytam, TOTAL_ENTRY,
                                  (sizeof(mytam_gnpstr_array[0])), strcmp);
    fprintf(stderr, "bsearch :::::::: %s\n", rtamexample);
}

It is giving bsearch() output "wA" in old gcc version-4.7.7 but It is giving segmentation fault in gcc7.4.0. Any help solving this is appreciated.


Solution

  • The old code is broken. It is apparently producing an answer out of thin air; the data value wA does not appear anywhere in the array being searched, so any answer other than a NULL pointer is bogus. If the code shown, when compiled on the old system, produces wA as an answer, the code is IMNSHO self-evidently broken.

    Here is code which conforms to what's needed. It includes <stdlib.h> since that is where bsearch() is declared. It does not include <search.h> since that does not declare anything the code uses. Ditto for <ctype.h>. It tells bsearch() about the array of structures it is searching, rather than passing a pointer to the start of the first member of the first element of the array. The comparator function passed in the code in the question is strcmp(); its prototype does not match the function pointer type expected by bsearch(), so you officially get undefined behaviour there. The comparator function in this code works correctly, and expects to be given a pair of pointers to the structure type, cast to const void * by bsearch(). The first pointer will be the key being searched for; the second will be a row from the array being searched.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAX_CHR_IN_STR 50
    #define MAX_CHR_IN_DEMO_STR 86
    #define GENDER 4
    #define NUMBER 4
    #define PERSON 4
    #define MEDIUM 50
    
    struct mytam_gnpstr
    {
        char mytam[MAX_CHR_IN_STR];
        char mytam_lbl[MAX_CHR_IN_STR];
        char gnp_str[MAX_CHR_IN_DEMO_STR];
        char gen_pos[GENDER];
        char num_pos[NUMBER];
        char per_pos[PERSON];
    };
    
    struct mytam_gnpstr mytam_gnpstr_array[] =
    {
        { "0", "0", "0[-,s,m]", "0", "s", "m", },
        { "0_0_kara", "0_0_kara", "02[-,-,-]kara_0[-,-,-]", "0", "0", "0", },
        { "wA", "Match", "Match", "0", "123", "ZZZ" },
        { "zB", "Unmatch", "Unmatch", "0", "123", "ZZZ" },
    };
    
    enum { TOTAL_ENTRY = sizeof(mytam_gnpstr_array) / sizeof(mytam_gnpstr_array[0]) };
    
    static int comparator(const void *v1, const void *v2)
    {
        const struct mytam_gnpstr *p1 = v1;
        const struct mytam_gnpstr *p2 = v2;
        return strcmp(p1->mytam, p2->mytam);
    }
    
    int main(void)
    {
        struct mytam_gnpstr key = { .mytam = "wA" };
    
        fprintf(stderr, "TAM :::::::: %s\n", key.mytam);
        fprintf(stderr, "mytam_gnpstr_array[0].mytam :::::::: %s\n", mytam_gnpstr_array[0].mytam);
        fprintf(stderr, "TOTAL_ENTRY :::::::: %d\n", TOTAL_ENTRY);
        fprintf(stderr, "sizeof(mytam_gnpstr_array[0]) :::::::: %zu\n", sizeof(mytam_gnpstr_array[0]));
    
        struct mytam_gnpstr *res = bsearch(&key, mytam_gnpstr_array, TOTAL_ENTRY,
                                        sizeof(mytam_gnpstr_array[0]), comparator);
    
        if (res == 0)
            fprintf(stderr, "Did not find entry matching\n");
        else
            fprintf(stderr, "bsearch :::::::: %s ('%s', '%s')\n",
                    res->mytam, res->mytam_lbl, res->gnp_str);
    
        return 0;
    }
    

    When compiled as shown, it produces the output:

    TAM :::::::: wA
    mytam_gnpstr_array[0].mytam :::::::: 0
    TOTAL_ENTRY :::::::: 4
    sizeof(mytam_gnpstr_array[0]) :::::::: 198
    bsearch :::::::: wA ('Match', 'Match')
    

    When the entry with wA is commented out, it produces the output:

    TAM :::::::: wA
    mytam_gnpstr_array[0].mytam :::::::: 0
    TOTAL_ENTRY :::::::: 3
    sizeof(mytam_gnpstr_array[0]) :::::::: 198
    Did not find entry matching
    

    This behaviour is correct.

    With the data in the question (two lines, nary a wA anywhere in the array), you will never get anything other than NULL back from a valid invocation of bsearch(). Expecting anything else is an exercise in futility.

    JFTR: compiled on macOS Mojave 10.14.6 (don't ask why it isn't Catalina) with GCC 9.2.0 and Xcode 11.3.1. I'd expect the same results on Ubuntu 18.04 LTS, or indeed any system where C99 is available. Indeed, it should work the same with C90, too.

    $ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
    >     bs47.c -o bs47
    $