Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting the SIGFPE handler to SIG_IGN is problematic, especially when embedding perl #12349

Open
p5pRT opened this issue Aug 24, 2012 · 12 comments

Comments

@p5pRT
Copy link

p5pRT commented Aug 24, 2012

Migrated from rt.perl.org#114574 (status was 'open')

Searchable as RT114574$

@p5pRT
Copy link
Author

p5pRT commented Aug 24, 2012

From andres@anarazel.de

Created by andres@anarazel.de

This is a bug report for perl from andres@​anarazel.de,
generated with the help of perlbug 1.39 running under perl 5.14.2.

-----------------------------------------------------------------

In PERL_SYS_INIT3 the SIGFPE handler unconditionally gets changed to
SIG_IGN.

This generally doesn't seem to be a good idea and actually hits undefined
behaviour in POSIXish systems​:
An excerpt from POSIX 2001/IEEE Std 1003.1-2004​:
"Delivery of the signal shall have no effect on the process. The behavior of a
process is undefined after it ignores a SIGFPE, SIGILL, SIGSEGV, [RTS] [Option
Start] or SIGBUS [Option End] signal that was not generated by kill(), [RTS]
[Option Start] sigqueue(), [Option End] or raise()."

In fact linux ignores setting SIGFPE to SIG_IGN and resets it to SIG_DFL
whenever its about to generate a SIGFPE. It seems to have done so for at least
8 years (I don't have anything older lying around). See
arch/x86/kernel/traps.c​:math_error for details if interested. It calls
force_sig_info(SIGFPE, &info, task); which resets SIG_IGN to SIG_DFL...

I/We hit this case in postgres' plperl module which embeds perl. Postgres uses
a SIGFPE handler to signal an error after invalid math to the user. This works
fine until plperl is used for the first time which triggers calling
PERL_SYS_INIT3 & perl_alloc.

Should you have postgres with plperl lying arround you can reproduce the issue
easy enough​:

andres=# SELECT (-(2​::numeric^31))​::int/-1;
ERROR​: 22P01​: floating-point exception
DETAIL​: An invalid floating-point operation was signaled. This probably means an out-of-range result or an invalid operation, such as division by zero.
LOCATION​: FloatExceptionHandler, postgres.c​:2708
andres=# DO LANGUAGE plperl ''; /* do nothing */
DO
Time​: 41.957 ms
andres=# SELECT (-(2​::numeric^31))​::int/-1;
The connection to the server was lost. Attempting reset​: Failed.
!>

In a situation where perl gets embedded it doesn't seem like a good idea to
implicitly muck around with signal handlers from my pov. Especially not if that
has an effect outside the scope where perl controls execution.

Do you have any suggestions how to handle this?

As far as I can see that behaviour is pretty much the same across all supported
perl version including the current git head.

Perl Info

Flags:
    category=core
    severity=medium

Site configuration information for perl 5.14.2:

Configured by Debian Project at Mon Jun 18 23:58:55 UTC 2012.

Summary of my perl5 (revision 5 version 14 subversion 2) configuration:

  Platform:
    osname=linux, osvers=2.6.32-5-amd64, archname=x86_64-linux-gnu-thread-multi
    uname='linux brahms 2.6.32-5-amd64 #1 smp sun may 6 04:00:17 utc 2012 x86_64 gnulinux '
    config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -D_FORTIFY_SOURCE=2 -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -Dldflags= -Wl,-z,relro -Dlddlflags=-shared -Wl,-z,relro -Dcccdlflags=-fPIC -Darchname=x86_64-linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.14 -Darchlib=/usr/lib/perl/5.14 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.14.2 -Dsitearch=/usr/local/lib/perl/5.14.2 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Duse64bitint -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Ud_ualarm -Uusesfio -Uusenm -Ui_libutil -DDEBUGGING=-g -Doptimize=-O2 -Duseshrplib -Dlibperl=libperl.so.5.14.2 -des'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=define, usemultiplicity=define
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fstack-protector -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64',
    optimize='-O2 -g',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -DDEBIAN -fstack-protector -fno-strict-aliasing -pipe -I/usr/local/include'
    ccversion='', gccversion='4.7.0', gccosandvers=''
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='cc', ldflags =' -fstack-protector -L/usr/local/lib'
    libpth=/usr/local/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib /usr/lib
    libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt
    perllibs=-ldl -lm -lpthread -lc -lcrypt
    libc=, so=so, useshrplib=true, libperl=libperl.so.5.14.2
    gnulibc_version='2.13'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E'
    cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib -fstack-protector'

Locally applied patches:



@INC for perl 5.14.2:
    /etc/perl
    /usr/local/lib/perl/5.14.2
    /usr/local/share/perl/5.14.2
    /usr/lib/perl5
    /usr/share/perl5
    /usr/lib/perl/5.14
    /usr/share/perl/5.14
    /usr/local/lib/site_perl
    .


Environment for perl 5.14.2:
    HOME=/home/andres
    LANG=en_US.utf8
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=/usr/lib/ccache:/home/andres/bin/bin:/home/andres/bin:/home/andres/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From @jkeenan

On Fri Aug 24 14​:37​:39 2012, andres@​anarazel.de wrote​:

-----------------------------------------------------------------

In PERL_SYS_INIT3 the SIGFPE handler unconditionally gets changed to
SIG_IGN.

Can you identify the place in the Perl 5 source code where this happens?
(I was unable to do so, myself).

Thank you very much.
Jim Keenan

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From andres@anarazel.de

For reference, this is the thread discussing the issue on the pgsql-hackers
mailing list​:
http​://archives.postgresql.org/message-
id/201208240644.15864.andres@​2ndquadrant.com

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From andres@anarazel.de

On Saturday, August 25, 2012 04​:35​:37 AM James E Keenan via RT wrote​:

On Fri Aug 24 14​:37​:39 2012, andres@​anarazel.de wrote​:

-----------------------------------------------------------------

In PERL_SYS_INIT3 the SIGFPE handler unconditionally gets changed to
SIG_IGN.

Can you identify the place in the Perl 5 source code where this happens?
(I was unable to do so, myself).
Sure​:

The callpath (including macros) is something like in v5.17.3-85-g3f6a9d3​:

PERL_SYS_INIT3 (perl.h)
Perl_sys_init3 (perl.c)
PERL_SYS_INIT3_BODY (perl.h)
PERL_SYS_INIT_BODY (unixish.h)
PERL_FPU_INIT (perl.h)
signal(SIGFPE, SIG_IGN)

I tried to send that before, but it didn't get through, possibly because I
didn't have an account yet​: The corresponding postgres thread is at​:
http​://archives.postgresql.org/message-
id/201208240644.15864.andres@​2ndquadrant.com

Greetings,

Andres

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From @Leont

On Fri, Aug 24, 2012 at 11​:37 PM, andres@​anarazel.de
<perlbug-followup@​perl.org> wrote​:

In PERL_SYS_INIT3 the SIGFPE handler unconditionally gets changed to
SIG_IGN.

This generally doesn't seem to be a good idea and actually hits undefined
behaviour in POSIXish systems​:
An excerpt from POSIX 2001/IEEE Std 1003.1-2004​:
"Delivery of the signal shall have no effect on the process. The behavior of a
process is undefined after it ignores a SIGFPE, SIGILL, SIGSEGV, [RTS] [Option
Start] or SIGBUS [Option End] signal that was not generated by kill(), [RTS]
[Option Start] sigqueue(), [Option End] or raise()."

In fact linux ignores setting SIGFPE to SIG_IGN and resets it to SIG_DFL
whenever its about to generate a SIGFPE. It seems to have done so for at least
8 years (I don't have anything older lying around). See
arch/x86/kernel/traps.c​:math_error for details if interested. It calls
force_sig_info(SIGFPE, &info, task); which resets SIG_IGN to SIG_DFL...

I agree this is problematic. I've been meaning to do the git
archeology on it but I never came around to it. It was effectively
added in 7014c40, which sadly doesn't give more information than
«Ignore SIGFPE everywhere.». TBH I don't see the rationale of doing
this and I think we should get rid of it. I CCed this to Jarko, hoping
he still remembers 11 years after.

In a situation where perl gets embedded it doesn't seem like a good idea to
implicitly muck around with signal handlers from my pov. Especially not if that
has an effect outside the scope where perl controls execution.

Do you have any suggestions how to handle this?

I'd just set it back to SIG_DFL right after PERL_SYS_INIT

As far as I can see that behaviour is pretty much the same across all supported
perl version including the current git head.

Yeah, it's been in since 2001, and on some OSes longer.

Leon

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From andres@anarazel.de

Leon Timmermans via RT <perlbug-followup@​perl.org> schrieb​:

On Fri, Aug 24, 2012 at 11​:37 PM, andres@​anarazel.de
<perlbug-followup@​perl.org> wrote​:

In PERL_SYS_INIT3 the SIGFPE handler unconditionally gets changed to
SIG_IGN.

This generally doesn't seem to be a good idea and actually hits
undefined
behaviour in POSIXish systems​:
An excerpt from POSIX 2001/IEEE Std 1003.1-2004​:
"Delivery of the signal shall have no effect on the process. The
behavior of a
process is undefined after it ignores a SIGFPE, SIGILL, SIGSEGV,
[RTS] [Option
Start] or SIGBUS [Option End] signal that was not generated by
kill(), [RTS]
[Option Start] sigqueue(), [Option End] or raise()."

In fact linux ignores setting SIGFPE to SIG_IGN and resets it to
SIG_DFL
whenever its about to generate a SIGFPE. It seems to have done so for
at least
8 years (I don't have anything older lying around). See
arch/x86/kernel/traps.c​:math_error for details if interested. It
calls
force_sig_info(SIGFPE, &info, task); which resets SIG_IGN to
SIG_DFL...

I agree this is problematic. I've been meaning to do the git
archeology on it but I never came around to it. It was effectively
added in 7014c40, which sadly doesn't give more information than
«Ignore SIGFPE everywhere.». TBH I don't see the rationale of doing
this and I think we should get rid of it. I CCed this to Jarko, hoping
he still remembers 11 years after.
Nice to see that I am not the only one seeing it that way.

In a situation where perl gets embedded it doesn't seem like a good
idea to
implicitly muck around with signal handlers from my pov. Especially
not if that
has an effect outside the scope where perl controls execution.

Do you have any suggestions how to handle this?

I'd just set it back to SIG_DFL right after PERL_SYS_INIT
Setting it back to PG's handler is what I suggested and tested in the referenced thread. Given perl not really needing - it doesn't work on Linux atm anyway - that seems unproblematic. The only problem I see is that PG uses siglongjmp in the handler which would be bad if perl ever generates a sigfpe itself. I didn't manage to do so so far.


Please excuse the brevity and formatting - I am writing this on my mobile phone.

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From @Leont

On Sat, Aug 25, 2012 at 12​:18 PM, anarazel@​anarazel.de
<andres@​anarazel.de> wrote​:

I'd just set it back to SIG_DFL right after PERL_SYS_INIT
Setting it back to PG's handler is what I suggested and tested in the referenced thread. Given perl not really needing - it doesn't work on Linux atm anyway - that seems unproblematic. The only problem I see is that PG uses siglongjmp in the handler which would be bad if perl ever generates a sigfpe itself. I didn't manage to do so so far.

Longjumping out of a signal handler is undefined and dangerous (see
CERT's [SIG32-C]), though I can see why it (usually?) works in this
case.

Leon

SIG32-C​: https://www.securecoding.cert.org/confluence/display/seccode/SIG32-C.+Do+not+call+longjmp%28%29+from+inside+a+signal+handler

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From andres@anarazel.de

On Saturday, August 25, 2012 12​:37​:20 PM Leon Timmermans via RT wrote​:

On Sat, Aug 25, 2012 at 12​:18 PM, anarazel@​anarazel.de

<andres@​anarazel.de> wrote​:

I'd just set it back to SIG_DFL right after PERL_SYS_INIT

Setting it back to PG's handler is what I suggested and tested in the
referenced thread. Given perl not really needing - it doesn't work on
Linux atm anyway - that seems unproblematic. The only problem I see is
that PG uses siglongjmp in the handler which would be bad if perl ever
generates a sigfpe itself. I didn't manage to do so so far.

Longjumping out of a signal handler is undefined and dangerous (see
CERT's [SIG32-C]), though I can see why it (usually?) works in this
case.
I think pg is actually careful enough (deactivates further jumps till cleaned
up, only jumps if the section is declared safe).
As the perl code runs in a section not declared as "ImmediateInterruptOK =
true" I think the risk I saw doesn't exist. In that case the process will be
abort()ed directly, but thats ok because thats what would have happened anyway
because SIGFPE cannot be ignored.

Thanks,

Andres

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From @jhi

I agree this is problematic. I've been meaning to do the git
archeology on it but I never came around to it. It was effectively
added in 7014c40, which sadly doesn't give more information than
«Ignore SIGFPE everywhere.». TBH I don't see the rationale of doing
this and I think we should get rid of it. I CCed this to Jarko, hoping
he still remembers 11 years after.

No recollection, sorry. I'd look around in the p5p archives to see
whether there was some discussion in there.

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From @Leont

On Sat, Aug 25, 2012 at 11​:31 AM, Leon Timmermans <fawaka@​gmail.com> wrote​:

I agree this is problematic. I've been meaning to do the git
archeology on it but I never came around to it. It was effectively
added in 7014c40, which sadly doesn't give more information than
«Ignore SIGFPE everywhere.». TBH I don't see the rationale of doing
this and I think we should get rid of it. I CCed this to Jarko, hoping
he still remembers 11 years after.

It seems to me like if we want to do anything (not sure we should), we
should use fenv.h to disable the generation of such exceptions on any
system that supports it (it's in C99).

Leon

@p5pRT
Copy link
Author

p5pRT commented Aug 25, 2012

From andres@anarazel.de

On Saturday, August 25, 2012 09​:44​:38 PM Leon Timmermans via RT wrote​:

On Sat, Aug 25, 2012 at 11​:31 AM, Leon Timmermans <fawaka@​gmail.com> wrote​:

I agree this is problematic. I've been meaning to do the git
archeology on it but I never came around to it. It was effectively
added in 7014c40, which sadly doesn't give more information than
«Ignore SIGFPE everywhere.». TBH I don't see the rationale of doing
this and I think we should get rid of it. I CCed this to Jarko, hoping
he still remembers 11 years after.

It seems to me like if we want to do anything (not sure we should), we
should use fenv.h to disable the generation of such exceptions on any
system that supports it (it's in C99).
Well, that doesn't help anything against cases like the aforementioned
INT_MIN/-1 which is - to my knowledge - not affected by fenv changes?

Andres

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants