Liam Wilson
Liam Wilson

Reputation: 188

Using getline consecutively causes segmentation fault

I'm trying to read two consecutive lines of input from stdin using getline. This minimal code causes a segmentation fault and I'm not sure why:

int main() {
    char *ln1 = NULL;
    char *ln2 = NULL;
    size_t lnsz1;
    size_t lnsz2;

    getline(&ln1,&lnsz1,stdin);
    getline(&ln2,&lnsz2,stdin);


}

Can someone explain what I'm doing wrong?

Upvotes: 1

Views: 674

Answers (1)

Jonathan Leffler
Jonathan Leffler

Reputation: 753695

I said in a comment that setting the size to zero (as well as the pointer to a null pointer) should fix the observed crash.

Lurking in the background is a hypothesis that the specific implementation of getline() causing the crash is somewhat faulty. One possible way in which it could be faulty is if it uses the size argument when allocating space because the pointer is null. That is somewhat improbable, but the question gives no information on which platform yields the crash.

The POSIX specification of getline() says:

ssize_t getline(char **restrict lineptr, size_t *restrict n, FILE *restrict stream);

The application shall ensure that *lineptr is a valid argument that could be passed to the free() function. If *n is non-zero, the application shall ensure that *lineptr either points to an object of size at least *n bytes, or is a null pointer.

If *lineptr is a null pointer or if the object pointed to by *lineptr is of insufficient size, an object shall be allocated as if by malloc() or the object shall be reallocated as if by realloc(), respectively, such that the object is large enough to hold the characters to be written to it, including the terminating NUL, and *n shall be set to the new size. If the object was allocated, or if the reallocation operation moved the object, *lineptr shall be updated to point to the new object or new location.

Upon successful completion, the getline() and getdelim() functions shall return the number of bytes written into the buffer, including the delimiter character if one was encountered before EOF, but excluding the terminating NUL character. If the end-of-file indicator for the stream is set, or if no characters were read and the stream is at end-of-file, the end-of-file indicator for the stream shall be set and the function shall return -1. If an error occurs, the error indicator for the stream shall be set, and the function shall return -1 and set errno to indicate the error.

The code in the question is not self-evidently abusing those rules. The application ensures that *lineptr is a null pointer — which is a value that can be passed to free(). It doesn't much matter what the value of *n is because *lineptr is a null pointer.

One way to experiment is to try printing the value in the uninitialized variables (lnsz1 and lnsz2), but such experimentation is arguably invoking undefined behaviour. Another is to initialize the variables to some large values — such as SIZE_MAX or SIZE_MAX / 2 (where SIZE_MAX defined in C11 <stdint.h> but not in earlier versions of the C standard) or other similar inflated values. You should also test with a value of 0, of course, and some testing could be done with other small values such as 1, 8, 16 etc. All of these tests would be conducted with the pointers set to a null pointer. If you can use a memory allocation package such that a debugging version of malloc() (or realloc()) is called, you may be able to log the requested size of the memory. You may get some information from printing the value of lnsz1 after the first call to getline() if the crash occurs on the second call.

Such an investigation is barely worth the effort, though. Setting both the pointers and the sizes to zero should resolve the problem. If it doesn't, there is more to investigate. That is, however, unlikely to be the problem.

//#define _XOPEN_SOURCE 700 // Explicitly request POSIX support
#include <stdio.h>

int main(void)
{
    char *ln1 = NULL;
    char *ln2 = NULL;
    size_t lnsz1 = 0;
    size_t lnsz2 = 0;
    ssize_t len1 = -1;
    ssize_t len2 = -1;

    if ((len1 = getline(&ln1, &lnsz1, stdin)) != -1)
    {
        printf("%zd (%zu: %p) [%s]\n", len1, lnsz1, (void *)ln1, ln1);
        if ((len2 = getline(&ln2, &lnsz2, stdin)) != -1)
            printf("%zd (%zu: %p) [%s]\n", len2, lnsz2, (void *)ln2, ln2);
    }
    free(ln1);
    free(ln2);

    return 0;
}

Note that getline() specifically returns -1 and not EOF when it detects an error or end-of-file. On most systems, the two values are the same, but the C standard only requires that EOF is negative — it does not require that it is -1. I cannot determine any circumstance under which a return value of 0 is valid for getline().

Upvotes: 1

Related Questions