3

I have this linked list node struct that's using a zero-length array for storing memory:

typedef struct s_list
{
    size_t          *list_size;
    struct s_list   *prev;
    struct s_list   *next;
    size_t          size;
    char            data[0];
}   t_list;

(list_size is a pointer containing the size of the total list)

And I'm using this function to allocate a new node:

static t_list   *lst_new_element(void *data, size_t size)
{
    t_list  *new_element;

    new_element = malloc(sizeof(t_list) + size);
    if (!new_element)
        return (NULL);
    new_element->size = size;
    memcpy(new_element->data, data, size); // <--- Segfault occurs here
    return (new_element);
}

So the segmentation fault occurs in the memcpy, but I don't understand why because I allocate sizeof(t_list) + size bytes so this should be enough to do a memcpy(size) on data. The segfault occured with this call: lst_new_element((void *)atoll(argv[1]), sizeof(long long)) (argv[1] is 5)

Thanks for the help.

15
  • Does 5 (argv[1]) represents a valid address to read in your environment? (For example, user binaries in xv6 will allow that) Commented May 20, 2021 at 12:41
  • argv[1] is "5" (it's written in the post) Commented May 20, 2021 at 12:42
  • @PaulOgilvie I'm using zero-length arrays Commented May 20, 2021 at 12:44
  • 1
    Not sure but... for flexible array members, I write [] - not [0] Commented May 20, 2021 at 12:46
  • 1
    @BitTickler Rather, back in the days, people were using something like char arr[1]; as last member, then wildly converting that into some pointer or accessing it out of bounds. This was known as "the struct hack" and was a common source of strange bugs and broken code. Afaik gcc invented zero-length arrays as a cure against "the struct hack", somewhere in the mid-90s. Commented May 20, 2021 at 12:58

4 Answers 4

3

You're passing a long long value to your function as if it's a valid void *. Your function then attempts to dereference that pointer (which in invalid) in an attempt to copy what it points to. This triggers undefined behavior leading to a crash.

You need to assign the return value of atoll to a local variable, then pass the address of that variable to the function.

long long val = atoll(argv[1]);
t_list *l = lst_new_element((&val, sizeof(long long));

Also, using a 0 length array as the last member of a struct is an extension many compilers use to implement a flexible array member. The standard-compliant way of doing this is to leave the size blank.

typedef struct s_list
{
    size_t          *list_size;
    struct s_list   *prev;
    struct s_list   *next;
    size_t          size;
    char            data[];
}   t_list;
Sign up to request clarification or add additional context in comments.

1 Comment

Oh yes, I don't know why but I thought it would copy the address of the pointer, I forgot how my own code worked x), thanks
2

(void *)atoll You are converting a long long value to a pointer which is of course plain wrong. Instead store the results in a temporary variable and pass that one (by value or reference).

Also please note that ato... functions are semi-obsolete and dangerous, you should be using strtoll instead, which has better error handling.

In addition (not related to the crash), zero-length arrays is an obsolete non-standard feature of gcc since well over 20 years. You should be using standard C flexible array members instead. They work exactly the same, just change the code to: char data[];.

Comments

2

For your function call you need an intemediate variable to store the converted value, for example:

long long llval = atoll(argv[1]);
lst_new_element(&llval, sizeof(long long));

Comments

1

You can use compound literals to allocate a temporally array to have the value be in the memory instead of using temporally variable as othe answers suggests.

lst_new_element((long long[]){ atoll(argv[1]) }, sizeof(long long));

2 Comments

Clever, but since the compiler will allocate an invisible variable, nothing is gained, except obfuscation (downvote not by me).
@PaulOgilvie Compound literals are simply another approach presented here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.