How to Invoke Non-reentrant Functions with Linux Signals?

3 min readOct 17, 2022

A function is referred to as reentrant if it can be safely (and without side effects) invoked in the middle of its execution.

Some single-threaded applications can work with non-reentrant functions. Because most applications don’t handle signals or have recursive, callback functions at all. It would be challenging to debug the code if you load it to Linux Kernel as a module or in an embedded system.

Photo by Gabriel Heinzer on Unsplash

In this code, the calculate function is a non-reentrant function. Because the calculate function can be invoked immediately after the calculation is done (line number 7) and sets the value of thesum variable. It doesn't appear to be possible in this code, though. So, even with the non-reentrant function, this code is still viable.

Let’s run it:

Let’s handle a Linux Signal, SIGINT in the code:

Every time SIGINT (Ctrl + C) is sent to this code, the signal_handler function will be invoked. Notice that the signal_handler function creates a variable called sum_internal

Let’s run it:

It seems to be working fine.

I will modify the signal_handler function. The signal_handler function modifies the global variable sum :

Let’s test it:

Since the sum variable is global, when the SIGINT signal is sent, the signal_handler function modifies it. We represent I/O intensive function call in the calculate function.

First, the calculate function is called by the main function (3 + 5). By the way, I sent a SIGINT signal to the program. The global sum variable has been changed. Therefore, we see the following line for a moment:

Result from main: 110

The problem is that the calculate function is a non-reentrant function. We can fix the code like this:

It seems okay:

Sometimes, it is not possible to modify the reentrant functions. Because they are a part of glibc or a part of a third-party library. You may want to block the signal while executing the reentrant function and unblock the signal when you are done with the reentrant function:

Let’s run it:

We managed to fix the problem as it was a simple code. Imagine the compiler warns you when you try to modify a global variable.

Let’s try to write the same non-reentrant code in Rust:

Rust will force you to use global variables in the unsafe block. You can understand that something is suspicious here.

Why are malloc() and printf() non-reentrant functions?

Which functions can I use safely?

P.S.: strtok used to be a non-reentrant function. Therefore, strtok_r was added to glibc. However, as of this commit, strtok is a wrapper for strtok_r