cstackstack-smash

Stack Smashing with strtol


I'm trying to learn about C and how to use its functions, etc. The man pages suggested to use strtol instead of atoi, so I came up with this code which works without issues:

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

int main()
{
    const char str[] = "12aaa";
    long size = sizeof(str) / sizeof(char);
    char **ptr = malloc(size);
    if (!ptr) {
        perror("ERROR! Could not reserve memory for ptr!");
        return EXIT_FAILURE;
    }
        
    memset(ptr, 0, size);
    
    printf("str: %s\n", str);
    printf("strtol: %ld\n", strtol(str, ptr, 10));
    printf("ptr container: %s\n", *ptr);

    free(ptr);
    
    return EXIT_SUCCESS;
}

This successfully output:

str: 12aaa
strtol: 12
ptr container: aaa

After, I came up with a question: since strtol asks for a double ptr to char, can I just use the address of a string variable (container)? If I make sure that the container string is the same length, in theory there should be no overflow (if main string is longer etc). So I edited the code to use a local string variable and pass the address as such:

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

int main()
{
    const char str[] = "12aaa";
    
    // using char instead of const char because it should be changeable?
    char container[] = "aaaaa"; //same length of str

    long size = sizeof(str) / sizeof(char);

    printf("strtol: %ld\n", strtol(str, &container, 10));
    printf("container: %s\n", container);
    
    return EXIT_SUCCESS;
}

Here I get this message:

strtol: 12
container: $g��
*** stack smashing detected ***: terminated

I thought that the container variable should remain until main is terminated? Why does this not work as intended? From what I understand, which is only beginner level, container is a stack variable, so it only exists until a function/main executes and then goes away. Since it is in main, why is it not working properly? Is the stack protected/unable to be used by pointers? Why does this only work for heap allocated memory locations? Is there something wrong in my reasoning?

Thanks for any clarifications in advanced!

UPDATE: I'm running macos and when I run man strtol I don't get any examples and this paragraph is near the end of the Description: If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, however, strtol() stores the original value of str in *endptr. The last sentence's wording make me thing that strtol does copy the original str, which is why I thought maybe allocating memory would work. I obviously did not understand how to use endptr or what its function was properly, but thanks for your answers/comments! It seems that I need more work to understand the differences between C strings, arrays, and pointers!


Solution

  • It seems you're misunderstanding what the second parameter to strtol is for.

    It is not a pointer to memory location that holds a copy of the string after the end of the match. It is simply a pointer to a pointer which points to a location in the original string where the match ended.

    In both cases, you're passing a pointer which points to an object 6 bytes in length. Assuming you're using a modern system that uses 64-bit addressing, that's two bytes less than what is required to hold a char *. This triggers undefined behavior in your code. In the first case it appears to work normally, while in the second it causes a crash.

    What you really need here is simply a char * variable whose address is passed to the function.

    char *ptr;
    printf("strtol: %ld\n", strtol(str, &ptr, 10));
    printf("ptr points to: %s\n", ptr);