Search code examples
csegmentation-faultgetoptgetopt-long

segfault with getopt_long()


for a school project I have to use getopt_long() or getopt_long_only(). Here is my loop:

while ((get_opt_err = getopt_long(argc, argv, "p:x:y:n:c:f:h",
                    &help_opt, NULL)) != -1)

I have a segfault reported in valgrind like that:

==2971== Memcheck, a memory error detector
==2971== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2971== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==2971== Command: ./zappy_server --h
==2971== 
==2971== Conditional jump or move depends on uninitialised value(s)
==2971==    at 0x4F0B23E: _getopt_internal_r (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C240: _getopt_internal (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C2BD: getopt_long (in /usr/lib/libc-2.25.so)
==2971==    by 0x400C3D: getopt_server_parsing (getopt_server_parsing.c:42)
==2971==    by 0x4008AC: main (main.c:46)
==2971== 
==2971== Use of uninitialised value of size 8
==2971==    at 0x4C2E8C9: __GI_strncmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2971==    by 0x4F0B252: _getopt_internal_r (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C240: _getopt_internal (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C2BD: getopt_long (in /usr/lib/libc-2.25.so)
==2971==    by 0x400C3D: getopt_server_parsing (getopt_server_parsing.c:42)
==2971==    by 0x4008AC: main (main.c:46)
==2971== 
==2971== Invalid read of size 1
==2971==    at 0x4C2E8C9: __GI_strncmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2971==    by 0x4F0B252: _getopt_internal_r (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C240: _getopt_internal (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C2BD: getopt_long (in /usr/lib/libc-2.25.so)
==2971==    by 0x400C3D: getopt_server_parsing (getopt_server_parsing.c:42)
==2971==    by 0x4008AC: main (main.c:46)
==2971==  Address 0x1170 is not stack'd, malloc'd or (recently) free'd
==2971== 
==2971== 
==2971== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==2971==  Access not within mapped region at address 0x1170
==2971==    at 0x4C2E8C9: __GI_strncmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2971==    by 0x4F0B252: _getopt_internal_r (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C240: _getopt_internal (in /usr/lib/libc-2.25.so)
==2971==    by 0x4F0C2BD: getopt_long (in /usr/lib/libc-2.25.so)
==2971==    by 0x400C3D: getopt_server_parsing (getopt_server_parsing.c:42)
==2971==    by 0x4008AC: main (main.c:46)
==2971==  If you believe this happened as a result of a stack
==2971==  overflow in your program's main thread (unlikely but
==2971==  possible), you can try to increase the size of the
==2971==  main thread stack using the --main-stacksize= flag.
==2971==  The main thread stack size used in this run was 8388608.
==2971== 
==2971== HEAP SUMMARY:
==2971==     in use at exit: 29 bytes in 2 blocks
==2971==   total heap usage: 2 allocs, 0 frees, 29 bytes allocated
==2971== 
==2971== LEAK SUMMARY:
==2971==    definitely lost: 0 bytes in 0 blocks
==2971==    indirectly lost: 0 bytes in 0 blocks
==2971==      possibly lost: 0 bytes in 0 blocks
==2971==    still reachable: 29 bytes in 2 blocks
==2971==         suppressed: 0 bytes in 0 blocks
==2971== Rerun with --leak-check=full to see details of leaked memory
==2971== 
==2971== For counts of detected and suppressed errors, rerun with: -v
==2971== Use --track-origins=yes to see where uninitialised values come from
==2971== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
[1]    2971 segmentation fault (core dumped)  valgrind ./zappy_server --h

Have you an idea of what happening ? It's my first use of the getopt api, and I am totally lost. It seems happening from the fact that my opt is prefixed with -- but I don't know why...

here is my full code:

static void fill_help_opt(struct option *help_opt)
{
  help_opt->name = "help";
  help_opt->has_arg = 0;
  help_opt->flag = NULL;
  help_opt->val = 'h';
}

int         getopt_server_parsing(int argc, char **argv,
                          t_server_params *params)
{
    int         get_opt_err;
    t_opts_checkers opts_fcts[7];
    int         idx;
    struct option       help_opt;

    fill_opts_fcts(opts_fcts);
    fill_help_opt(&help_opt);
    get_opt_err = 0;
    while ((get_opt_err = getopt_long(argc, argv, "p:x:y:n:c:f:h",
                    &help_opt, NULL)) != -1)
    {
        idx = 0;
        while (idx < 7)
        {
            if (opts_fcts[idx].key == get_opt_err
                    && opts_fcts[idx].value(optarg, params, argv) != 0)
                return (1);
            ++idx;
        }
        if (get_opt_err == '?')
            return (1);
    }
    return (0);
}

I don't wan't to paste everything because my school can find this code and blame me for cheating... (because of copying/pasting code, even though it's mine).


Solution

  • The third argument to getopt_long should not be the address of a single struct option, but a pointer to the first element of an array of struct option, the last of which should be filled with zeros.

    Because you don't pass in an array, getopt_long attempts to read past help_opt as if it were an array. This invokes undefined behavior.

    Change help_opt to an array with the last element zeroed out.

    struct option help_opt[] = {
        { "help", 0, NULL, 'h' },
        { NULL, 0, NULL, 0 }
    };
    
    ...
    
    while ((get_opt_err = getopt_long(argc, argv, "p:x:y:n:c:f:h",
                    help_opt, NULL)) != -1)