Yuri J
Yuri J

Reputation: 129

Why does printing to stderr cause segmentation fault when dealing with ucontext?

I was working on a project for a course on Operating Systems. The task was to implement a library for dealing with threads, similar to pthreads, but much more simpler. The purpose of it is to practice scheduling algorithms. The final product is a .a file. The course is over and everything worked just fine (in terms of functionality).

Though, I got curious about an issue I faced. On three different functions of my source file, if I add the following line, for instance:

fprintf(stderr, "My lucky number is %d\n", 4);

I get a segmentation fault. The same doesn't happen if stdout is used instead, or if the formatting doesn't contain any variables.

That leaves me with two main questions:

  1. Why does it only happen in three functions of my code, and not the others?

  2. Could the creation of contexts using getcontext() and makecontext(), or the changing of contexts using setcontext() or swapcontext() mess up with the standard file descriptors?

My intuition says those functions could be responsible for that. Even more when given the fact that the three functions of my code in which this happens are functions that have contexts which other parts of the code switch to. Usually by setcontext(), though swapcontext() is used to go to the scheduler, for choosing another thread to execute.

Additionally, if that is the case, then:

  1. What is the proper way to create threads using those functions?

I'm currently doing the following:

/*------------------------------------------------------------------------------
Funct:  Creates an execution context for the function and arguments passed.
Input:  uc      -> Pointer where the context will be created.
        funct   -> Function to be executed in the context.
        arg     -> Argument to the function.
Return: If the function succeeds, 0 will be returned. Otherwise -1.
------------------------------------------------------------------------------*/
static int create_context(ucontext_t *uc, void *funct, void *arg)
{
    if(getcontext(uc) != 0) // Gets a context "model"
    {
        return -1;
    }
    stack_t *sp = (stack_t*)malloc(STACK_SIZE); // Stack area for the execution context
    if(!sp) // A stack area is mandatory
    {
        return -1;
    }
    uc->uc_stack.ss_sp = sp; // Sets stack pointer
    uc->uc_stack.ss_size = STACK_SIZE; // Sets stack size
    uc->uc_link = &context_end; // Sets the context to go after execution

    makecontext(uc, funct, 1, arg); // "Makes everything work" (can't fail)
    return 0;
}

This code is probably a little modified, but it is originally an online example on how to use u_context.

Upvotes: 1

Views: 1089

Answers (1)

Florian Weimer
Florian Weimer

Reputation: 33717

Assuming glibc, the explanation is that fprintf with an unbuffered stream (such as stderr by default) internally creates an on-stack buffer which as a size of BUFSIZE bytes. See the function buffered_vfprintf in stdio-common/vfprintf.c. BUFSIZ is 8192, so you end up with a stack overflow because the stack you create is too small.

Upvotes: 4

Related Questions