Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240805182646.GC10433@brightrain.aerifal.cx>
Date: Mon, 5 Aug 2024 14:26:46 -0400
From: Rich Felker <dalias@...c.org>
To: contact@...ktivis.me
Cc: musl@...ts.openwall.com
Subject: Re: [PATCH v2 3/3] signal: add str2sig(3) from POSIX.1-2024

On Mon, Aug 05, 2024 at 08:56:07AM +0200, contact@...ktivis.me wrote:
> From: "Haelwenn (lanodan) Monnier" <contact@...ktivis.me>
> 
> ---
>  include/signal.h     |  1 +
>  src/signal/str2sig.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 46 insertions(+)
>  create mode 100644 src/signal/str2sig.c
> 
> diff --git a/include/signal.h b/include/signal.h
> index 217cfa08..fd486cd0 100644
> --- a/include/signal.h
> +++ b/include/signal.h
> @@ -235,6 +235,7 @@ void psignal(int, const char *);
>  
>  #define SIG2STR_MAX sizeof("RTMIN+32")
>  int sig2str(int signum, char *str);
> +int str2sig(const char *restrict str, int *restrict pnum);
>  
>  #endif
>  
> diff --git a/src/signal/str2sig.c b/src/signal/str2sig.c
> new file mode 100644
> index 00000000..b6a4fc23
> --- /dev/null
> +++ b/src/signal/str2sig.c
> @@ -0,0 +1,45 @@
> +#include <signal.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <stdlib.h>
> +
> +int str2sig(const char *restrict str, int *restrict pnum)
> +{
> +	if (str[0] == '\0') return -1;
> +
> +	errno = 0;
> +	long signum = strtol(str, NULL, 10);
> +	if (errno == 0 && signum < _NSIG) return (*pnum = signum, 0);
> +
> +	if (strnlen(str, sizeof *__sys_signame) <= sizeof *__sys_signame)
> +		for (int i = 0; i < sizeof __sys_signame/sizeof *__sys_signame; i++)
> +			if (strncmp(str, __sys_signame[i], sizeof *__sys_signame) == 0)
> +				return (*pnum = i, 0);
> +
> +	// signal aliases
> +	if (strcmp(str, "IOT") == 0)
> +		return (*pnum = SIGIOT, 0);
> +	if (strcmp(str, "UNUSED") == 0)
> +		return (*pnum = SIGUNUSED, 0);

If there were a lot of these I'd lean towards some table to avoid the
above, but there seem to be only two that fit this pattern, so what
you've done is probably best.

> +#if SIGPOLL != SIGIO
> +	if (strcmp(str, "POLL") == 0)
> +		return (*pnum = SIGPOLL, 0);
> +#endif

I think it's when they're equal, not when they're not-equal, that you
need to special-case this. Otherwise "POLL" won't be accepted on archs
where the values are equal. Of course you'd need to special-case both
if it weren't in the table, but see my comment on sig2str about
putting POLL in the table when not-equal.

> +	if (strcmp(str, "RTMIN") == 0)
> +		return (*pnum = SIGRTMIN, 0);
> +	if (strcmp(str, "RTMAX") == 0)
> +		return (*pnum = SIGRTMAX, 0);

These don't count in the "only two" I mentioned above because the
results are not a constant translation but runtime-variable.

This does raise a point that we could have SIGRTMIN and SIGRTMAX
expand to constants at musl build time, since the only way they could
change is by changing musl. My leaning is kinda not to do that,
though, because I've toyed with the idea of picking the
implementation-internal signals dynamically so that nesting in
qemu-user with some signals already stolen by the outer implementation
can work. I'm not sure if that's actually practical (it's more of a
pain than it sounds like, at least) but it's probably best to leave it
the way you've done it so as not to lock that possibility out.

> +	if (strncmp(str, "RTMIN+", 6) == 0 || strncmp(str, "RTMAX-", 6) == 0)
> +	{
> +		errno = 0;
> +		long sigrt = strtol(str+6, NULL, 10);
> +		if(errno != 0 || sigrt < 1 || sigrt > SIGRTMAX - SIGRTMIN) return -1;
> +
> +		*pnum = str[5] == '+' ? SIGRTMIN + sigrt : SIGRTMAX - sigrt;
> +		return 0;
> +	}
> +
> +	return -1;
> +}

The call to strtol isn't validating that there's not additional junk
after the digits, or that there's not a second sign character or
whitespace before the digits.

The canonical way to solve this is checking isdigit on the first char
and using the pend argument to see where parsing stopped, but in this
case, since the + or - is mandatory and already checked, you could
pass str+5 instead of str+6 to have it parse the + or - too. This
would require changing the arithmetic that follows, It also
complicates accepting 0 if you want to, but the current code rejects
0, so maybe that's okay?

It's also not clear if you want to accept over-long inputs like
RTMIN+000001. If not, checking that first char after + or - is in
range '1' to '9' would probably be the easiest.

Another possibility to consider is that interfacing with strtol is
sufficiently complicated that it's not worth bothering, and that
open-coding a-'0' or 10*(a-'0')+(b-'0') is easier (if you don't
actually intend to accept over-length representations).

Rich

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.