Cannot access memory at address using gdb in CLION IDE. How do I setup gdb?

284 views Asked by At

I get the error "Cannot access memory at address 0x100403055" when I try and set a memory value to 0x00 when stopped in the debugger.

Is there a special switch I need to set to enable the set operation?

Here is my complete C code file "main.c"

#include <stdio.h>
#include <string.h>
/*
  separator - consume all non-token characters until next token.  This includes:
    comments:   '#' 
    nesting:    '{'
    unnesting:  '}'
    whitespace: ' ','\t','\n'

    *nest is changed according to nesting/unnesting processed
 */
static void separator(int *nest, char **tokens) {
    char c, *s;

    s = *tokens;
    while ((c = *s)) {
        /* #->eol = comment */
        if (c == '#') {
            s++;
            while ((c = *s)) {
                s++;
                if (c == '\n')
                    break;
            }
            continue;
        }
        if (c == '{') {
            (*nest)++;
            s++;
            continue;
        }
        if (c == '}') {
            (*nest)--;
            s++;
            continue;
        }
        if (c == ' ' || c == '\n' || c == '\t') {
            s++;
            continue;
        }
        break;
    }
    *tokens = s;
}

/*
  token - capture all characters until next separator, then consume separator,
    return captured token, leave **tokens pointing to next token.
 */
static char *token(int *nest, char **tokens) {
    char c, *s, *t;
    char terminator = '\0';
    s = t = *tokens;
    while ((c = *s)) {
        if (c == '#'
            || c == ' ' || c == '\t' || c == '\n' || c == '{' || c == '}')
            break;
        s++;
    }
    *tokens = s;
    separator(nest, tokens);
    /* Breakpoint here to examine and manipulate memory */
    *s = '\0';
    return t;
}


struct test_case {
    char *input;
    int nest;
    char *expected_output;
};


int main() {
    int nest = 0;
    int TESTSEP = 0;
    if (TESTSEP>0) {
        char *tokens = "# this is a comment\n{nesting {example} unnesting}\n  \t end";

        separator(&nest, &tokens);

        printf("nest: %d\n", nest);
        printf("tokens: %s\n", tokens);

        return 0;
    } else {
        struct test_case test_cases[] = {
                {"hello world",  0, "hello"},
                {"hello#world",  0, "hello"},
                {"hello{world}", 0, "hello"},
                {"hello  world", 0, "hello"},
                {"hello\tworld", 0, "hello"},
                {"hello\nworld", 0, "hello"},
        };

        for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
            struct test_case test_case = test_cases[i];
            char *tokens = test_case.input;
            char *output = token(&test_case.nest, &tokens);
            if (strcmp(output, test_case.expected_output) != 0) {
                printf("Test case %d failed: expected %s, got %s\n", i, test_case.expected_output, output);
            }
        }

        return 0;
    }
}

In the token function there is a comment line where I place a breakpoint and drop into the gdb debugger. The code is supposed to write a '\0' at the address of the pointer *s to truncate the string.

When I'm in the debugger and I examine the 's' variable I get the following:

  (gdb) x s
  0x100403055:  0x726f7720

When I try and set the variable I get:

(gdb) [![set *0x0000000100403055 = 0x726f7700][1]][1]
Cannot access memory at address 0x100403055

I'm using the CLION IDE and am a novice. I'm not sure if its an IDE problem, a user problem or some external memory protection mechanism that is preventing this. Does anyone know how to make this work?

Here is a screenshot of the IDE: Screenshot of IDE stopped at the breakpoint

When I run the code (without the debugger) I get this output:

./explore.exe 

Test case 0 failed: expected hello, got hello world 
Test case 1 failed: expected hello, got hello#world 
Test case 2 failed: expected hello, got hello{world} 
Test case 3 failed: expected hello, got hello world 
Test case 4 failed: expected hello, got hello world 
Test case 5 failed: expected hello, got hello world 
Process finished with exit code 0
1

There are 1 answers

0
P Moran On

I this case I believe I was passing in a pointer to memory in the read only space. The struct test_case is built into the code and is read only. So that when I pass that into the token function it was trying to write to read only. Here is the code that seems to work.

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

/*
  separator - consume all non-token characters until next token.  
This includes:
    comments:   '#' ... '\n'
    nesting:    '{'
    unnesting:  '}'
    whitespace: ' ','\t','\n'

    *nest is changed according to nesting/unnesting processed
 */
static void separator(int *nest, char **tokens) {
    char c, *s;

    s = *tokens;
    while ((c = *s)) {
        /* #->eol = comment */
        if (c == '#') {
            s++;
            while ((c = *s)) {
                s++;
                if (c == '\n')
                    break;
            }
            continue;
        }
        if (c == '{') {
            (*nest)++;
            s++;
            continue;
        }
        if (c == '}') {
            (*nest)--;
            s++;
            continue;
        }
        if (c == ' ' || c == '\n' || c == '\t') {
            s++;
            continue;
        }
        break;
    }
    *tokens = s;
}

/*
  token - capture all characters until next separator, then consume 
separator,
    return captured token, leave **tokens pointing to next token.
 */
static char *token(int *nest, char **tokens) {
    char c, *s, *t;
    char terminator = '\0';
    s = t = *tokens;
    while ((c = *s)) {
        if (c == '#'
            || c == ' ' || c == '\t' || c == '\n' || c == '{' || c == '}')
            break;
        s++;
    }
    *tokens = s;
    separator(nest, tokens);
    *s = '\0';
    return t;
}


struct test_case {
    char *input;
    int nest;
    char *expected_output;
};


int main() {
    int nest = 0;
    int TESTSEP = 0;
    char *temp_malloc_string;
    if (TESTSEP>0) {
        char *tokens = "# this is a comment\n{nesting {example} 
unnesting}\n  \t end";
        temp_malloc_string = malloc(strlen(tokens)*sizeof(char));
        strcpy(temp_malloc_string, tokens);
        char * t = token(&nest, &temp_malloc_string);
        printf("nest: %d\n", nest);
        printf("tokens: %s\n", t);
        separator(&nest, &temp_malloc_string);

        printf("nest: %d\n", nest);
        printf("tokens: %s\n", temp_malloc_string);
        return 0;
    } else {
        struct test_case test_cases[] = {
            {"hello world",  0, "hello"},
            {"hello#world",  0, "hello"},
            {"hello{world}", 0, "hello"},
            {"hello  world", 0, "hello"},
            {"hello\tworld", 0, "hello"},
            {"hello\nworld", 0, "hello"},
        };

        for (int i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
            struct test_case test_case = test_cases[i];
            char *tokens = test_case.input;
            printf("len of string is %d\n", strlen(tokens));
            temp_malloc_string = malloc((strlen(tokens)+1)*sizeof(char));
            char * tt = temp_malloc_string;
            if ( temp_malloc_string==NULL ) {
                printf("error!\n");
            }
            strcpy(temp_malloc_string, tokens);
            printf("tm going in: %s\n", temp_malloc_string);

            char *output = token(&test_case.nest, &temp_malloc_string);
            printf("Test case %d: expected %s, got %s\n\t\ttm is now: %s\n",
                   i, test_case.expected_output, output, temp_malloc_string);
            if (strcmp(output, test_case.expected_output) != 0) {
                printf("Test case %d failed: expected %s, got %s\n",
                       i, test_case.expected_output, output);
            }
            free(tt);
            temp_malloc_string = NULL;
        }

        return 0;
    }
}

Now when I run the code I get:

./explore.exe
len of string is 11
tm going in: hello world
Test case 0: expected hello, got hello
                tm is now: world
len of string is 11
tm going in: hello#world
Test case 1: expected hello, got hello
                tm is now:
len of string is 12
tm going in: hello{world}
Test case 2: expected hello, got hello
                tm is now: world}
len of string is 12
tm going in: hello  world
Test case 3: expected hello, got hello
                tm is now: world
len of string is 11
tm going in: hello      world
Test case 4: expected hello, got hello
                tm is now: world
len of string is 11
tm going in: hello
world

Test case 5: expected hello, got hello
                tm is now: world

Process finished with exit code 0

And when I stop at the breakpoint I can write to memory.

In this modified code I malloc a char* object and copy the string from the struct into that then pass that into the token function.

I'm guess that gdb is protecting me from writing to the .text block in code.

Like I said: I'm a newbie :(