Problem: presumably, a segfault (windows won't tell me)

Jan 22, 2010 at 10:30pm
So I read this thread ( http://cplusplus.com/forum/beginner/18518/ ), which reminded me of a get_input function I wrote a while ago. It seems I've lost it (my computer is in for "repairs" (I can't fix it myself, or warranty = void)).

Anyway, I decided to rewrite the function. So I've pretty much finished it, although one function needs a little fixing; but I'm having trouble allocting some memory. Now, you'll probably just think "the heap must be full, or the OS is unable to give you the memory..." but, well, it's 17 bytes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
static
char* gi_get_opts(const struct gi_options opts[], char* optstring, size_t n) {
    int  i = 0;
    long longest_option; /* We determine the length of the longest option in
                          * opts, so we can malloc optstring */
    /* Find the length of the largest option */
    for (; i < n; i++)
        longest_option = (strlen(opts[i].option) > longest_option) ?
                          strlen(opts[i].option) : longest_option;

    /* Allocate optstring enough memory to store all the options,
     * the enclosing brackets, and a forward-slash between each option */
    const size_t optstring_sz = (n * (longest_option + 1) + 2);

    assert(optstring_sz == 17);

    optstring = (char*)malloc(optstring_sz);

    if (!optstring)
        fprintf(stderr, "Couldn't allocate %ld bytes of memory\n", optstring_sz);
    
    memset(optstring, 0, optstring_sz);
    
    optstring[0] = '['; /* For the enclosing bracket */

    /* Concatenate the options into optstring */
    for (i = 0; i < n; i++) {
        strcat(optstring, opts[i].option);
        if ((i + 1) < n)
            strcat(optstring, "/");
    }

    optstring[optstring_sz - 3] = ']';

    return optstring;
}


My assertion on line 30 does not fail, which means that optstring_sz certainy does == 17. By my calculations, longest_option = 4 (all the options in opts are 4 chars long). If we add 1 (for a "/" after each option) and multiply it by n (which is passed as 3) we get 15. If we then add two (one is space for the NULL, and one is space for the opening bracket ('[') (the last option doesn't have a "/" after it, so that leaves space for the closing bracket) we get 17.

Now, I have a hard time believing that the OS cannot allocate 17 bytes, and in fact, even if I change the malloc to malloc(17), it still fails.

Can somoene help me to figure out what is the problem? Any help would be apprecieted. If more information is required, ask.

Thanks.
Jan 22, 2010 at 10:40pm
I don't understand how that could work. longest_option is unset at the beginning, but the for loop is required to read it before assigning to it.
Why don't you just do 1 /*'['*/ + (sum of all the string sizes) + n /*'/'*/ + 1 /*']'*/ + 1 /*'\0'*/?
Jan 22, 2010 at 10:49pm
I don't understand how that could work. longest_option is unset at the beginning, but the for loop is required to read it before assigning to it.

Uhhh... bug in MinGW's gcc? Well, gcc is able to optimize code. Perhaps the program decided that longest_option would be better if it was initialized, and rather than telling me with an error or warning, decided to initialize it anyway?

/*'['*/ + (sum of all the string sizes) + n /*'/'*/ + 1 /*']'*/ + 1 /*'\0'*/

I think I know what that means :l


Like this?
1
2
3
4
5
6
7
8
9
10
    size_t string_sz_sum = 0;
	
    /* Sum the lengths of all the options in opts */
    for (; i < n; i++)
        string_sz_sum += strlen(opts[i].option);

    /* Allocate optstring enough memory to store all the options,
     * the enclosing brackets, and a forward-slash between each option */
    const size_t optstring_sz = 1 + string_sz_sum + n + 1 + 1;
                             /* [ +    options    + n + ] + \0 */


It still isn't working... specifically, I get that annoying "windows is trying to find a solution to the problem" dialogue (which gives you no help about what went wrong. If it said "heap error" or "unable to allocate memory" or "segmentation/paging fault" I'd understand, but no. Programmers don't need to know what their programs did wrong).

As it happens, I do have GDB (I thought I didn't), so I can find out.

(gdb) start
Breakpoint 1 at 0x4012d6
Starting program: C:\Users\admin.Chris-pc\Documents/get_input.exe
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
0x004012d6 in main ()
(gdb) next
Single stepping until exit from function main,
which has no line number information.

Program received signal SIGSEGV, Segmentation fault.
0x771eeb6a in strlen () from C:\Windows\system32\msvcrt.dll

So it is a segfault, but it's in strlen? It can't be... if I remove the strlen() call,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static
char* gi_get_opts(const struct gi_options opts[], char* optstring, size_t n) {
    int  i = 0;
    size_t string_sz_sum = 0;
#if 0
    /* Sum the lengths of all the options in opts */
    for (; i < n; i++)
        string_sz_sum += strlen(opts[i].option);
#endif
    /* Allocate optstring enough memory to store all the options,
     * the enclosing brackets, and a forward-slash between each option */
    const size_t optstring_sz = 1 + /* string_sz_sum */ 12 + n + 1 + 1;
                             /* [ +    options    + n + ] + \0 */
    optstring = (char*)malloc(optstring_sz);

it still doesn't work.

But if I do this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
static
char* gi_get_opts(const struct gi_options opts[], char* optstring, size_t n) {
    int  i = 0;
    size_t string_sz_sum = 0;

    /* Sum the lengths of all the options in opts */
    for (; i < n; i++)
        string_sz_sum += strlen(opts[i].option);

    /* Allocate optstring enough memory to store all the options,
     * the enclosing brackets, and a forward-slash between each option */
    const size_t optstring_sz = 1 + /* string_sz_sum */ 12 + n + 1 + 1;
                             /* [ +    options    + n + ] + \0 */
#if 0
    optstring = (char*)malloc(optstring_sz);

    if (!optstring)
        fprintf(stderr, "Couldn't allocate %ld bytes of memory\n", optstring_sz);
    
    memset(optstring, 0, optstring_sz);
    
    optstring[0] = '['; /* For the enclosing bracket */

    /* Concatenate the options into optstring */
    for (i = 0; i < n; i++) {
        strcat(optstring, opts[i].option);
        if ((i + 1) < n)
            strcat(optstring, "/");
    }

    optstring[optstring_sz - 3] = ']';
#endif
    return optstring;
}

it does. But it's not the memset or anything, because if I remove anything else but leave the malloc() call there, it still segfaults.

This is annoying.
Last edited on Jan 23, 2010 at 2:20am
Jan 23, 2010 at 12:05am
This looks like memory corruption. I don't know of any other reason why malloc() would fail when by all appearances it shouldn't.
Jan 23, 2010 at 2:13am
Well, I'll try it now. This machine has been rebooted.

It could be memory leakage; probably my fault, because I kept forgetting to free my buffers.

Edit: nah, still not working, and all my malloc()ated pointers are definitely being free()d.

Here's some more gdb output; I kept stepping through until windows terminated the program:
(gdb) start
Breakpoint 1 at 0x4015f6
Starting program: C:\Users\admin.Chris-pc\Documents/get_input.exe
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
0x004015f6 in main ()
(gdb) next
Single stepping until exit from function main,
which has no line number information.

Program received signal SIGSEGV, Segmentation fault.
0x75cbeb6a in strlen () from C:\Windows\system32\msvcrt.dll
(gdb) nextr
Undefined command: "nextr".  Try "help".
(gdb) next
Single stepping until exit from function strlen,
which has no line number information.
0x771d5dc9 in ntdll!LdrEnumerateLoadedModules ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!LdrEnumerateLoadedModules,
which has no line number information.
0x771a973f in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771d60cd in ntdll!RtlCheckRegistryKey () from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlCheckRegistryKey,
which has no line number information.
0x771a9774 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771d4e54 in ntdll!ZwQueryInformationProcess ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!ZwQueryInformationProcess,
which has no line number information.
0x771a9791 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771d60cd in ntdll!RtlCheckRegistryKey () from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlCheckRegistryKey,
which has no line number information.
0x771a97ac in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771ba4b8 in tan () from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function tan,
which has no line number information.
0x771a98e6 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771b1e36 in ntdll!RtlAddAccessAllowedObjectAce ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlAddAccessAllowedObjectAce,
which has no line number information.
0x771a9904 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771a9ba6 in ntdll!RtlDelete () from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlDelete,
which has no line number information.
0x771a9959 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.
0x771b1e0d in ntdll!RtlRemoveVectoredContinueHandler ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlRemoveVectoredContinueHandler

which has no line number information.
0x771a9968 in ntdll!RtlMultipleAllocateHeap ()
   from C:\Windows\system32\ntdll.dll
(gdb) next
Single stepping until exit from function ntdll!RtlMultipleAllocateHeap,
which has no line number information.

Program received signal SIGSEGV, Segmentation fault.
0x75cbeb6a in strlen () from C:\Windows\system32\msvcrt.dll
(gdb) next
Single stepping until exit from function strlen,
which has no line number information.

Program exited with code 030000000005.

The first SIGSEGV is in strlen(). Then it goes through a bunch of ntdll functions to do with heap allocation. Then eventually it gets another segfault in strlen().

Maybe it's one of my other functions interfering. Wait... I didn't malloc that pointer elsewhere, did I? *checks frantically for potential stupid mistakes*

Right, it's a NULL pointer, not a pointer to const nor a const pointer, being re-pointed to the return value of malloc as you can see above. I do nothing else with optstring until that function.
Last edited on Jan 23, 2010 at 2:24am
Jan 23, 2010 at 2:25am
Here's an otherwise sort-of working program (I need to fix another couple of bugs with string manipulation);
get_input.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _GET_INPUT_H
#define _GET_INPUT_H

/* Includes */
#include <stdio.h>
#include <string.h>

static struct gi_options {
    char*        option;
    unsigned int is_default;
}   default_option[] = {
    {"Y", 1},
    {"n", 0}
};

char* get_input(char*, const char*, struct gi_options*, size_t, size_t);

#endif /* ! _GET_INPUT_H */ 


main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "get_input.h"

int main(int argc, char** argv) {
    struct gi_options opts[] = {
        {"<=1m", 0},
        {"<=2m", 1},
        {"<=3m", 0}
    };

    char* buf = (char*)malloc(512);

    if (!get_input(buf, "How tall are you?", opts, 3, 3))
        printf("%s: failed to get input from stdin\n", argv[0]);

    printf("buf: %s\n", buf);
    
    free(buf);
    
    return 0;
}


get_input.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#include "get_input.h"
#include <assert.h>

#ifndef _GET_INPUT_C
# define _GET_INPUT_C
#endif

/* gi_get_opts: fill optstring with options based on an object of struct opts
 * opts:      a const array object of struct opts from which to get options and
 *            the default option.
 * optstring: a C-string into which the options will be placed, in the format
 *            [option1/option2/option3] (with up to n options)
 * n:         the amount of options to be found in opts and placed in optstring
 * Note: this routine should not be called directly, and is therefore static.
 */
static
char* gi_get_opts(const struct gi_options opts[], char* optstring, size_t n) {
    int  i = 0;
    size_t string_sz_sum = 0;

    /* Sum the lengths of all the options in opts */
    for (; i < n; i++)
        string_sz_sum += strlen(opts[i].option);

    /* Allocate optstring enough memory to store all the options,
     * the enclosing brackets, and a forward-slash between each option */
    const size_t optstring_sz = 1 + string_sz_sum + n + 1 + 1;
                             /* [ +    options    + n + ] + \0 */

    optstring = (char*)malloc(optstring_sz);
	
    if (!optstring)
        fprintf(stderr, "Couldn't allocate %ld bytes of memory\n", optstring_sz);
    
    memset(optstring, 0, optstring_sz);
    
    optstring[0] = '['; /* For the enclosing bracket */

    /* Concatenate the options into optstring */
    for (i = 0; i < n; i++) {
        strcat(optstring, opts[i].option);
        if ((i + 1) < n)
            strcat(optstring, "/");
    }

    optstring[optstring_sz - 3] = ']';

    return optstring;
}

/* gi_get_buf: fill buf with validated user input from stdin
 * buf:       a buffer into which user input will be placed
 * bufsz:     the given size of buf
 * prompt:    the prompt to be displayed
 * optstring: a string of options to search
 * Note: this routine should not be called directly, and is therefore static.
 */
static
char* gi_get_buf(char* buf, size_t bufsz, const char* prompt, 
                 const char* optstring, struct gi_options opts[], size_t n) {
    int i = 0;
    do {
        printf("%s%s: ", (i > 0) ? "\nInvalid input. " : "", prompt);
        fgets(buf, bufsz, stdin);
        buf[strlen(buf) - 1] = 0;
        i++;
    } while ((strstr(optstring, buf)) && strcmp(buf, "\n"));
    /* This is done to validate buf: if the option isn't in optstring,
     * the caller doesn't want it as input */

    if (strcmp(buf, "\n") == 0) { /* Default option is to be used */
        while (n--)
            if (opts[n].is_default == 1)
                break;
        strcpy(buf, opts[n].option);
    }
    
    return buf;
}

/* get_input: get input from the user with up to n options
 * buf:     buffer to store the input from the user in. It should be noted that
 *          get_input null-terminates buf.
 * prompt:  prompt to display to the user
 * options: an object of struct gi_options with n elements to be displayed
 *          as options
 * n:       the amount of options chosen to be displayed,
 *          should be equal to the amount of elements in options
 * bufsz:   the size (bytes) that buf can store
 */
char* get_input(char* buf, const char* prompt,
                struct gi_options opts[], size_t n, size_t bufsz) {
    char* optstring = NULL,
        * promptBuf = NULL;

    if (!gi_get_opts(opts, optstring, n))
        return NULL;

    /* Allocate promptBuf with enough data to store optstring and prompt */
    promptBuf = (char*)malloc(strlen(prompt) + strlen(optstring) + 3);

    strcpy(promptBuf, prompt);
    strcat(promptBuf, " ");
    strcat(promptBuf, optstring);
    
    /* Now we've built a prompt, we can take the input */
    if (!gi_get_buf(buf, bufsz, promptBuf, optstring, opts, n))
        return NULL;

    free(optstring);
    free(promptBuf);
        
    return buf;
}


It had to be in a second post, it wouldn't fit into the first one.
Last edited on Jan 23, 2010 at 2:26am
Topic archived. No new replies allowed.