What does the getcontext system call (ucontext.h) really do? - c

I took operating systems last year, during which I used user contexts (defined in the header ucontext.h) to implement a thread scheduler (in which each thread simulated a process) for a project. I'm taking part in a lecture and will talk about user contexts, and it just occurred to me that, despite having done this project last year, I don't really understand what exactly the getcontext system call actually does.
The man pages for getcontext states that it
initializes the structure pointed at by ucp to the currently active context."
It also states, for the argument to setcontext, that if the ucp argument
was obtained by a call of getcontext(), program execution continues as if this call just returned.
Okay, so I understand that.
So here's what I'm confused about. Typically, for the way I learned it, to perform a context switch, one would initialize the ucontext_t struct and swap/set it as such:
ucontext_t ucp;
ucontext_t oucp;
getcontext(&ucp);
// Initialize the stack_t struct in the ucontext_t struct
ucp.uc_stack.ss_sp = malloc(STACK_SIZE);
ucp.uc_stack.ss_size = STACK_SIZE;
ucp.uc_stack.ss_flags = 0;
ucp.uc_link = /* some other context, or just NULL */;
// Don't block any signals in this context
sigemptyset(&ucp.uc_sigmask);
// Assume that fn is a function that takes 0 arguments and returns void
makecontext(&ucp, fn, 0);
// Perform the context switch. Function 'fn' will be active now
swapcontext(&oucp, &ucp);
// alternatively: setcontext(&ucp);
If I omit getcontext in smaller programs, nothing interesting happens. In somewhat larger programs in which there is more context switching via user contexts, I get a segmentation fault that is only resolved by adding getcontext back in.
What exactly does getcontext do? Why can't I just allocate a ucontext_t struct, initialize it by initializing the uc_stack and uc_sigmask fields, and calling makecontext without the getcontext? Is there some necessary initialization that getcontext performs that makecontext does not perform?

I looked at the GNU libc implementation for ucontext on x86/linux architectures, so, there might be different implementations for which the following does not hold.
The GNU libc manual states that:
The ucp parameter passed to the makecontext shall be initialized by a call to getcontext.
If you look at mcontext_t in glibc/sysdeps/unix/linux/x86/sys/ucontext.h there is a pointer to the floating point state (fpregset_t fpregs) that is initialized in getcontext() and dereferenced again in setcontext(). However, it is not initialized using makecontext(). I did a quick test with GDB and I got a segfault in setcontext() when trying to dereference the pointer to the floating point context in a ucontext_t struct not initialized by getcontext():
=> 0x00007ffff784308c <+44>: fldenv (%rcx)

Related

scope of pthread function arguments

Here's the signature of pthread_setschedparam:
#include <pthread.h>
int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param);
Will this piece of code result in unexpected behavior:
void schedule(const thread &t, int policy, int priority) {
sched_param params;
params.sched_priority = priority;
pthread_setschedparam(t.native_handle(), policy, &params);
}
It is completely unclear if the scope of params needs to be broader than the function call alone. When I see a function that takes in a pointer, it suggests (to me at least) that it's asking for ownership of it. Is this signature just badly designed? Should "sched_params params" live on the heap? Does it need to outlive the thread to stay valid? Can it be deleted?
I have no idea.
Thanks!
pthread_setschedparam sets the scheduling policy for the given thread. The parameters need not be alive after the call.
If the lifetime of the last argument mattered (as you put, if pthread_setschedparam takes ownership of it), it would have been explicitly documented so. But it's not in POSIX documentation pthread_setschedparam .
The probable reason why it takes a pointer (instead of value) is that it's less expensive to pass a pointer than a struct.
When I see a function that takes in a pointer, it suggests (to me at least) that it's asking for ownership of it.
I don't jump straight there when I see a function that accepts a pointer parameter, and I don't think you should, either. Although it is important to be aware of the possibility, and you do well to look for documentation, there is a variety of reasons for a function to take a pointer parameter, among them:
the function accepts arrays via the parameter. This is surely the most common reason.
the function wants to modify an object specified to it by the caller (via the pointer). This is probably the second most common reason.
the function accepts a pointer to a structure or union of large or potentially-large size to lighten the function-call overhead
the function accepts a pointer to a structure or union because it conforms to interface conventions that accommodate ancient C compilers that did not accept structures and unions as arguments. This was normal for early C compilers, as it's the way the language was originally specified:
[T]he only operations you can perform on a structure are take its address with & and access one of its members. [... Structures] can not be passed to or returned from functions. [...] Pointers to structures do not suffer these limitations[.]
(Kernighan & Ritchie, The C Programming Language, 1st ed., section 6.2)
Standard C does not have those restrictions, but their effect can still be felt in some places.
That the function expects to take (and typically reassign) responsibility for freeing dynamically-allocated space to which the pointer points, or that it otherwise intends to make a copy of the pointer that survives the function's return, are way down the list. If a function intends to do one of those things, then I fully expect its documentation to indicate so in some manner.
Is this signature just badly designed?
No, I think its design is prompted by one or both of the latter two points from my list.
Should "sched_params params" live on the heap?
I would not expect that to be a requirement.
Does it need to outlive the thread to stay valid? Can it be deleted?
I do not think it needs to outlive the thread whose properties are set. In addition to my general interpretation of the interface, I read (weak) support for that position in the wording of the function's POSIX specification:
The pthread_setschedparam() function shall set the scheduling policy
and associated scheduling parameters for the thread whose thread ID is
given by thread to the policy and associated parameters provided in
policy and param, respectively.
(POSIX specification for pthread_setscheduleparam(); emphasis added)
The "provided in" language indicates to me (again, weakly) that the function uses the contents of the pointed-to structure, not the structure itself.

C callbacks - optional argument?

Hey I have implemented some callbacks in my C program.
typedef void (*server_end_callback_t)(void *callbackArg);
then I have variable inside structure to store this callback
server->server_end_callback = on_sever_end;
What I have noticed it that I can pass in on_server_end callback function implementation that skips void *callbackArg and the code works correctly (no errors).
Is it correct to skip some arguments like void * implementing callback functions which prototypes takes such arguments?
void on_server_end(void) {
// some code goes here
}
I believe it is an undefined behavior from the C point of view, but it works because of the calling convention you are using.
For example, AMD64 ABI states that the first six arguments get passed to the calling function using CPU registers, not stack. So neither caller nor callee need no clean-up for the first six arguments and it works fine.
For more info please refer the Wikipedia.
The code works correctly because of the convention of passing arguments. Caller knows that callee expects some arguments - exactly one. So, it prepares the argument(s) (either in register or on stack - depending on ABI on your platform). Then callee uses those parameters or not. After return from callee, caller cleans up the stack if necessary. That's the mistery.
However, you shall not abuse this specific behaviour by passing incompatible function. It is a good practice to always compile your code with options -W -Wall -Werror (clang/gcc and compatible). Enabling such option would provide you a compilation error.
C allows a certain amount of playing fast and loose with function arguments. So
void (*fptr) ();
means "a pointer to a function which takes zero or more arguments". However this is for backwards compatibility, it's not wise to use it in new C code. The other way round
void (*fptr)(void *ptr)
{
/* don't use the void */
}
/* in another scope */
(*fptr)(); /* call with no arguments */
also works, as long as you don't use the void *, and I believe it is guaranteed to work though I'm not completely sure about that (on a modern machine the calling convention is to pass the first arguments in registers, so you just get a garbage register, and it will work). Again, it is a very bad idea to rely on it.
You can pass a void *, which you then cast to a structure of appropriate type containing as many arguments as you wish. That is a good idea and a sensible use of C's flexibility.
Is it correct to skip some arguments like void * implementing callback functions which prototypes takes such arguments?
No it is not. Any function with a given function declaration is not compatile with a function of a different function declaration. This rule applies for pointers to functions too.
So if you have a function such as pthread_create(..., my_callback, ...); and it expects you to pass a function pointer of type void* (*) (void*), then you cannot pass a function pointer of a different format. This invokes undefined behavior and compilers may generate incorrect code.
That being said, function pointer compatibility is a common non-standard extension on many systems. If the calling convention of the system is specified in a way that the function format doesn't matter, and the specific compiler port supports it, then such code might work just fine.
Such code is however not portable and not standard. It is best to avoid it whenever possible.

Is Wikipedia wrong about reentrancy?

https://en.wikipedia.org/wiki/Reentrancy_(computing) (as of Dec 9, 2016) states that the following code is reentrant, despite modifying a global variable, because swap leaves it unchanged:
int t;
void swap(int *x, int *y)
{
int s;
s = t; // save global variable
t = *x;
*x = *y;
// hardware interrupt might invoke isr() here!
*y = t;
t = s; // restore global variable
}
However, what if swap is interrupted in any other number of places? Is this function reentrant, according to the definition of the term?
By the way, this article is cited as a source elsewhere on SO -- perhaps someone should improve it.
To answer the question it's necessary to observe that "reentrant" is an overloaded term with several accepted meanings, some of them ranging from "stretch" to "blatantly incorrect".
I would say the function is synchronously (but not asynchronously) reentrant, and certainly not thread-safe. That's rather vacuous here since it doesn't call any other functions, but it would be meaningful if it called a callback function which could then call back into this function.
If there were proper use of volatile types, the function could plausibly be asynchronously reentrant (but still not thread-safe).
You are aware that this swap() function is written in a deliberately bogus way. Even for the purpose of illustrating the concept, it would be better to make t a local variable with static storage class. This way, we could assume that t is not modified elsewhere.
At first sight, the function could be called from an interrupt service routine even if the interruption occurred during the execution because, as they mention, swap restores the value of its private global (useless) state upon exit. To this regard, it can be considered reentrant (asynchronous reentrancy).
For more complicated functions, that call back user supplied code, whether such callbacks can in turn call the function is also a criteria for reentrancy (synchronous reentrancy), but it does not apply here as swap() does not call anything.
The problem is hardware has become quite tricky nowadays, and unless you are programming to a simple embedded processor, fiddling with global variables from interrupt routines can have surprising side effects. t should be made volatile to prevent some of these, but not all.
On a side note, if swap() were to be called by different preemptive threads, the calls and return sequences could be interleaved, a situation that is not handled by the function, not to mention that accesses to the global variable t are not guaranteed to be atomic... swap is definitely not thread safe.

C language calling nested function

I know that C does not support nested functions and it's only a gcc extension. But even so, this behavior is strange.
It seems like the nested functions can be called only once; the second calling causes SIGSEV, and sometimes SIGILL. I want nested functions for structures like stack. On stack, I would be able to define functions like pop, push, etc., which I will assign with nested functions, which will call normal functions with reference, which I will get. This function is like a constructor, or initializer. But this code is enough to simulate my problem.
When generate function assigns innerFunction into struct, second calling causes an error. If the assignment is to test function, second calling is ok.
Where is the problem, please? In gcc documentation, it says that as long as you have inner function address, you can access this function and nested function has access into all variables defined above.
typedef struct A A;
struct A {
void (*foo)();
};
void test() {
printf("test\n");
}
void generate(A* a) {
void innerTest(){
test();
}
a->foo = &innerTest;
}
int main() {
A a;
generate(&a);
a.foo();
a.foo();
};
Do not allow pointers to inner functions to escape the stack frame they were created on. This yields undefined behavior.
The code generated places a trampoline on the stack. With the particular construct you have created, the trampoline got overwritten with your first call to the function so it failed at the second call to the function.
I personally am annoyed that it invokes that behavior without a closure to care about, but that's what it does. Oh wait I get it. your example works because in pairing it down this far you accidentally reduced it to working code (see acorngal's comment). Your real code accesses the pointer to structure labelled a and so does force a closure and therefore a trampoline. You cannot do this stunt in C; when doing OO programming you must pass the equivalent to the this pointer yourself.

C function call with too few arguments

I am working on some legacy C code. The original code was written in the mid-90s, targeting Solaris and Sun's C compiler of that era. The current version compiles under GCC 4 (albeit with many warnings), and it seems to work, but I'm trying to tidy it up -- I want to squeeze out as many latent bugs as possible as I determine what may be necessary to adapt it to 64-bit platforms, and to compilers other than the one it was built for.
One of my main activities in this regard has been to ensure that all functions have full prototypes (which many did not have), and in that context I discovered some code that calls a function (previously un-prototyped) with fewer arguments than the function definition declares. The function implementation does use the value of the missing argument.
Example:
impl.c:
int foo(int one, int two) {
if (two) {
return one;
} else {
return one + 1;
}
}
client1.c:
extern foo();
int bar() {
/* only one argument(!): */
return foo(42);
}
client2.c:
extern int foo();
int (*foop)() = foo;
int baz() {
/* calls the same function as does bar(), but with two arguments: */
return (*foop)(17, 23);
}
Questions: is the result of a function call with missing arguments defined? If so, what value will the function receive for the unspecified argument? Otherwise, would the Sun C compiler of ca. 1996 (for Solaris, not VMS) have exhibited a predictable implementation-specific behavior that I can emulate by adding a particular argument value to the affected calls?
EDIT: I found a stack thread C function with no parameters behavior which gives a very succinct and specific, accurate answer. PMG's comment at the end of the answer taks about UB. Below were my original thoughts, which I think are along the same lines and explain why the behaviour is UB..
Questions: is the result of a function call with missing arguments defined?
I would say no... The reason being is that I think the function will operate as-if it had the second parameter, but as explained below, that second parameter could just be junk.
If so, what value will the function receive for the unspecified argument?
I think the values received are undefined. This is why you could have UB.
There are two general ways of parameter passing that I'm aware of... (Wikipedia has a good page on calling conventions)
Pass by register. I.e., the ABI (Application Binary Interface) for the plat form will say that registers x & y for example are for passing in parameters, and any more above that get passed via stack...
Everything gets passed via stack...
Thus when you give one module a definition of the function with "...unspecified (but not variable) number of parameters..." (the extern def), it will not place as many parameters as you give it (in this case 1) in either the registers or stack location that the real function will look in to get the parameter values. Therefore the second area for the second parameter, which is missed out, essentially contains random junk.
EDIT: Based on the other stack thread I found, I would ammended the above to say that the extern declared a function with no parameters to a declared a function with "unspecified (but not variable) number of parameters".
When the program jumps to the function, that function assumes the parameter passing mechanism has been correctly obeyed, so either looks in registers or the stack and uses whatever values it finds... asumming them to be correct.
Otherwise, would the Sun C compiler of ca. 1996 (for Solaris, not VMS) have exhibited a >> predictable implementation-specific behavior
You'd have to check your compiler documentation. I doubt it... the extern definition would be trusted completely so I doubt the registers or stack, depending on parameter passing mechanism, would get correctly initialised...
If the number or the types of arguments (after default argument promotions) do not match the ones used in the actual function definition, the behavior is undefined.
What will happen in practice depends on the implementation. The values of missing parameters will not be meaningfully defined (assuming the attempt to access missing arguments will not segfault), i.e. they will hold unpredictable and possibly unstable values.
Whether the program will survive such incorrect calls will also depend on the calling convention. A "classic" C calling convention, in which the caller is responsible for placing the parameters into the stack and removing them from there, will be less crash-prone in presence of such errors. The same can be said about calls that use CPU registers to pass arguments. Meanwhile, a calling convention in which the function itself is responsible for cleaning the stack will crash almost immediately.
It is very unlikely the bar function ever in the past would give consistent results. The only thing I can imagine is that it is always called on fresh stack space and the stack space was cleared upon startup of the process, in which case the second parameter would be 0. Or the difference between between returning one and one+1 didn't make a big difference in the bigger scope of the application.
If it really is like you depict in your example, then you are looking at a big fat bug. In the distant past there was a coding style where vararg functions were implemented by specifying more parameters than passed, but just as with modern varargs you should not access any parameters not actually passed.
I assume that this code was compiled and run on the Sun SPARC architecture. According to this ancient SPARC web page: "registers %o0-%o5 are used for the first six parameters passed to a procedure."
In your example with a function expecting two parameters, with the second parameter not specified at the call site, it is likely that register %01 always happened to have a sensible value when the call was made.
If you have access to the original executable and can disassemble the code around the incorrect call site, you might be able to deduce what value %o1 had when the call was made. Or you might try running the original executable on a SPARC emulator, like QEMU. In any case this won't be a trivial task!

Resources