Frederik Deweerdt
Frederik Deweerdt

Reputation: 5271

Limiting which functions can be called by a thread

Let's say we have a multi-threaded Linux x86-64 executable (written in C, for example) with three threads: main, consumer and producer. Some of the functions are intended to use by some threads only. For example, the produce() function should only ever be called by the producer thread. I would like that if another thread (such as consumer) calls produce(), then we'd get a fatal error (a SIGABRT or SIGSEGV, for example).

One way to deal with this is to register the thread id, and check that the thread id calling produce() is in fact the producer thread id. If not, call abort(). That method unfortunately requires a runtime check for each function call, that maybe prohibitive if the function is in a hot path.

I'm wondering if there's another way, such as annotating and then moving all functions intended for producer only to their own section and remove executable memory accesses for all the other threads - my understanding is that this wouldn't work since mprotect() sets process-wide permissions - ?

Edit: @AlanAu asks whether this check has to be done at runtime. It's not a requirement, but my understanding is that such a check would only work at runtime for non-trivial programs using functions pointers, for example.

Edit2: I realize using processes would help address this, but as noted in the comments inter-threads communications is more efficient.

Upvotes: 1

Views: 80

Answers (1)

phyrrus9
phyrrus9

Reputation: 1467

One /rather hackey/ way of doing this is to make pointers for these and calling the pointers instead of the functions themselves. Example:

void disallowed_call(void)
{ abort(); }

void testfunc(void)
{
     printf("Hello, world!\n");
}

void childcode(void (*notrestricted)(void), void (*restricted)(void);)
{
     printf("Non restricted call:\n");
     *notrestricted();
     printf("Restricted call:\n");
     *restricted();
}

int main()
{
     fork();
     if (getpid() == 0)
     {
          childcode(&testfunc, &testfunc);
     }
     else
     {
          childcode(&testfunc, &disallowed_call);
     }
     return 0;
}

That might be a bit more complicated than you were looking for, but it should work. The runtime check is done only once.

Upvotes: 1

Related Questions