flak rss random

signal safe strcpy

According to The Open Group Base Specifications Issue 7 Chapter 2 (POSIX), there is a very narrow list of functions which are safe to call in a signal handler. This list appears in section 2.4.3 and notably omits strcpy and all the other str functions. But there’s a workaround.

sigsafe_strlcpy

I have chosen to implement strlcpy instead of the unsafe strcpy function but the principle of operation is the same. Careful examination of the signal safe function list reveals the presence of two building blocks which we will use, symlink and readlink.

int
sigsafe_strlcpy(char *dst, const char *src, size_t dstsize)
{
        const char *linkname = "sigsafe_strlcpy_link";
        int rv;

        if (symlink(src, linkname) == -1)
                return -1;
        rv = readlink(linkname, dst, dstsize);
        unlink(linkname);
        if (rv == -1 || rv == dstsize)
                return -1;
        dst[rv] = 0;
        return rv;
}

Yup. This is technically signal safe, whereas strcpy is not. There are a few more failure modes, however, so you’ll need to be careful and remember to check the return value. For example, it won’t work if you can’t create a new symlink in the current directory. Also it’s not safe to use outside of a signal handler, because if you get interrupted between the symlink and unlink calls, then it won’t work in the signal handler. It’s not thread safe either. Writing signal safe code is tough.

If you need to copy longer strings or memory containing zero bytes, I recommend pipe, read, and write. I suppose you could copy the implementation of memcpy and give it a different name, but then you’re likely to also copy whatever implementation flaw has led POSIX to conclude these functions are not signal safe. Is that a risk you’re willing to take?

signal safe server

What other functions can we call? How about socket, bind, listen, accept, and recvfrom? Yes. All of them. That’s right, you can create a new socket, bind it to an address, listen and accept new connections and receive data from clients, all in your signal handler. Just don’t copy any strings.

Of course, you’ll be somewhat limited in configuring your server or passing data back to the rest of the program because of the no globals rule: “the behavior is undefined if the signal handler refers to any object other than errno with static storage duration other than by assigning a value to an object declared as volatile sig_atomic_t”. In English, that means you can read and write errno, you can write to a sig_atomic_t, and that’s it. You may not read or write any other global variable. (This restriction is for serious. Innumerable race conditions result from crappy signal handlers.) There’s always the filesystem, of course.

sem_post

This limitation reveals one of the truly and completely useless (as opposed to absurd) signal safe functions, sem_post. Alas, it’s not possible to refer to an existing semaphore due to the no globals rule. Nor is it possible to create a new semaphore, as silly as that would be, because neither sem_init nor sem_open are on the list. It is physically impossible to safely call sem_post in a signal handler, but it would be safe if you could. Good to know.

footnote

I’m not entirely certain that referring to a string literal such as "sigsafe_strlcpy_link" is defined behavior in a signal handler. It’s an object, it’s not errno, and it has static storage duration. Consider building such strings one character at a time to be on the safe side:
s[0] = 's'; s[1] = 'i'; s[2] = 'g'; ...

Posted 08 May 2013 14:57 by tedu Updated: 21 Apr 2022 08:30
Tagged: c programming rants