signal() anomaly

Discussion in 'C Programming' started by hip cat, Jul 31, 2012.

  1. hip cat

    hip cat Guest

    What up

    I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    a null pointer my program still crashes out with segfault SIGSEGV. What
    gives?

    Communicate laterz ++
     
    hip cat, Jul 31, 2012
    #1
    1. Advertising

  2. hip cat

    Eric Sosman Guest

    On 7/31/2012 4:13 PM, hip cat wrote:
    > What up
    >
    > I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    > a null pointer my program still crashes out with segfault SIGSEGV. What
    > gives?


    Did you check that the signal() call succeeded? That is,
    was the returned value SIG_ERR or something else?

    On POSIX systems, ignoring SIGSEGV produces undefined behavior.
    The C Standard isn't quite so clear to me: It's U.B. if a signal
    handler for SIGSEGV returns (7.14.1.1p3), but SIG_IGN is not a
    "signal handler" under the definition of 7.14.1.1p2.

    --
    Eric Sosman
    d
     
    Eric Sosman, Jul 31, 2012
    #2
    1. Advertising

  3. In article <jv9e9e$bj3$>,
    hip cat <> wrote:
    >What up
    >
    >I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    >a null pointer my program still crashes out with segfault SIGSEGV. What
    >gives?


    I am amused and curious -- what did you *want* to happen on a null
    pointer dereference?

    --
    -Ed Falk,
    http://thespamdiaries.blogspot.com/
     
    Edward A. Falk, Jul 31, 2012
    #3
  4. hip cat

    Nobody Guest

    On Tue, 31 Jul 2012 20:13:34 +0000, hip cat wrote:

    > I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference a
    > null pointer my program still crashes out with segfault SIGSEGV. What
    > gives?


    POSIX-2008 §2.4.3 says:

    SIG_IGN

    Ignore signal.

    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, or
    SIGBUS signal that was not generated by kill(), sigqueue(), or raise().

    http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html

    And also:

    The behavior of a process is undefined after it returns normally from a
    signal-catching function for a SIGBUS, SIGFPE, SIGILL, or SIGSEGV signal
    that was not generated by kill(), sigqueue(), or raise().
     
    Nobody, Aug 1, 2012
    #4
  5. hip cat

    hip cat Guest

    On Tue, 31 Jul 2012 22:12:41 +0000, Edward A. Falk wrote:
    > In article <jv9e9e$bj3$>, hip cat
    > <> wrote:
    >>What up
    >>
    >>I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    >>a null pointer my program still crashes out with segfault SIGSEGV. What
    >>gives?

    >
    > I am amused and curious -- what did you *want* to happen on a null
    > pointer dereference?


    Word Ed,

    My code has a certain pointer that sometimes unexpectedly becomes null.
    It would be a lot of work to find every place the pointer gets
    dereferenced and add a null check. So I want to just ignore it by
    catching the signal.

    Communicate laterz ++
     
    hip cat, Aug 1, 2012
    #5
  6. hip cat

    hip cat Guest

    On Tue, 31 Jul 2012 16:35:41 -0400, Eric Sosman wrote:

    > On 7/31/2012 4:13 PM, hip cat wrote:
    >> What up
    >>
    >> I've used signal to set SIGSEGV to SIG_IGN. But when I later
    >> dereference a null pointer my program still crashes out with segfault
    >> SIGSEGV. What gives?

    >
    > Did you check that the signal() call succeeded? That is,
    > was the returned value SIG_ERR or something else?
    >
    > On POSIX systems, ignoring SIGSEGV produces undefined behavior.
    > The C Standard isn't quite so clear to me: It's U.B. if a signal handler
    > for SIGSEGV returns (7.14.1.1p3), but SIG_IGN is not a "signal handler"
    > under the definition of 7.14.1.1p2.


    Word Eric,

    The return value is 0.

    Communicate laterz ++
     
    hip cat, Aug 1, 2012
    #6
  7. hip cat

    James Kuyper Guest

    On 08/01/2012 02:58 PM, hip cat wrote:
    > On Tue, 31 Jul 2012 22:12:41 +0000, Edward A. Falk wrote:
    >> In article <jv9e9e$bj3$>, hip cat
    >> <> wrote:
    >>> What up
    >>>
    >>> I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    >>> a null pointer my program still crashes out with segfault SIGSEGV. What
    >>> gives?

    >>
    >> I am amused and curious -- what did you *want* to happen on a null
    >> pointer dereference?

    >
    > Word Ed,
    >
    > My code has a certain pointer that sometimes unexpectedly becomes null.


    Rather than trying to make this work, you should be trying to find out
    why the pointer is becoming null.

    > It would be a lot of work to find every place the pointer gets
    > dereferenced and add a null check. So I want to just ignore it by
    > catching the signal.


    Well, as "Nobody" has already pointed out, that's not an option. Using
    SIG_IGN for this has undefined behavior. So does registering your own
    SIGSEGV "handler" that doesn't bother to actually do anything, and
    simply returns. Therefore, you're going to have to either insert the
    null checks, or let the program abort() - your choice.
     
    James Kuyper, Aug 1, 2012
    #7
  8. hip cat

    Angel Guest

    On 2012-08-01, hip cat <> wrote:
    >
    > My code has a certain pointer that sometimes unexpectedly becomes null.
    > It would be a lot of work to find every place the pointer gets
    > dereferenced and add a null check. So I want to just ignore it by
    > catching the signal.


    And you expect the program will work correctly and produce meaningful
    output if you ignore what is normally a very fatal error condition?

    I do hope you're not writing code that will be used in any important
    production environment.


    --
    "C provides a programmer with more than enough rope to hang himself.
    C++ provides a firing squad, blindfold and last cigarette."
    - seen in comp.lang.c
     
    Angel, Aug 1, 2012
    #8
  9. hip cat

    Nick Bowler Guest

    On Tue, 31 Jul 2012 16:35:41 -0400, Eric Sosman wrote:

    > On 7/31/2012 4:13 PM, hip cat wrote:
    >> What up
    >>
    >> I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    >> a null pointer my program still crashes out with segfault SIGSEGV. What
    >> gives?

    >
    > Did you check that the signal() call succeeded? That is,
    > was the returned value SIG_ERR or something else?
    >
    > On POSIX systems, ignoring SIGSEGV produces undefined behavior.


    POSIX is a bit more specific than that. But regardless...

    > The C Standard isn't quite so clear to me: It's U.B. if a signal
    > handler for SIGSEGV returns (7.14.1.1p3), but SIG_IGN is not a
    > "signal handler" under the definition of 7.14.1.1p2.


    ....the C standard doesn't need to be any more explicit on this matter.
    The behaviour is undefined because the program has dereferenced an
    invalid (null) pointer. The handling of SIGSEGV is irrelevant.
     
    Nick Bowler, Aug 1, 2012
    #9
  10. hip cat

    Eric Sosman Guest

    On 8/1/2012 2:58 PM, hip cat wrote:
    > On Tue, 31 Jul 2012 22:12:41 +0000, Edward A. Falk wrote:
    >> In article <jv9e9e$bj3$>, hip cat
    >> <> wrote:
    >>> What up
    >>>
    >>> I've used signal to set SIGSEGV to SIG_IGN. But when I later dereference
    >>> a null pointer my program still crashes out with segfault SIGSEGV. What
    >>> gives?

    >>
    >> I am amused and curious -- what did you *want* to happen on a null
    >> pointer dereference?

    >
    > Word Ed,
    >
    > My code has a certain pointer that sometimes unexpectedly becomes null.
    > It would be a lot of work to find every place the pointer gets
    > dereferenced and add a null check. So I want to just ignore it by
    > catching the signal.


    That's not going to work. As I wrote earlier, the C Standard
    doesn't seem entirely clear on this matter, but the fact that you
    get undefined behavior if a SIGSEGV handler returns suggests that
    there's no way to recover. That's explicit under POSIX: If you
    ignore SIGSEGV, there's no telling what might happen.

    But, back to your problem: What do you *expect* should happen
    if you could somehow continue after dereferencing your null pointer?
    Somewhere, your program did `*ptr = 42' and ptr was null: Where do
    expect the 42 to go? Or somewhere you did `x = *ptr' and ptr was
    null: What value should x now have? Or, here's a good one:

    for (ptr = accidentally_null; *ptr != 0; ++ptr) {
    putchar(*ptr);
    }

    How many times should the loop execute, what output should it
    produce, and what value should ptr have when (if) it finishes?

    SIGSEGV means your car's motor has thrown a rod. You can't
    just ignore the broken engine and keep on driving.

    --
    Eric Sosman
    d
     
    Eric Sosman, Aug 1, 2012
    #10
  11. hip cat

    hip cat Guest

    On Wed, 01 Aug 2012 15:33:20 -0400, James Kuyper wrote:
    > On 08/01/2012 02:58 PM, hip cat wrote:
    >> On Tue, 31 Jul 2012 22:12:41 +0000, Edward A. Falk wrote:
    >>> In article <jv9e9e$bj3$>, hip cat
    >>> <> wrote:
    >>>> What up
    >>>>
    >>>> I've used signal to set SIGSEGV to SIG_IGN. But when I later
    >>>> dereference a null pointer my program still crashes out with segfault
    >>>> SIGSEGV. What gives?
    >>>
    >>> I am amused and curious -- what did you *want* to happen on a null
    >>> pointer dereference?

    >>
    >> Word Ed,
    >>
    >> My code has a certain pointer that sometimes unexpectedly becomes null.

    >
    > Rather than trying to make this work, you should be trying to find out
    > why the pointer is becoming null.
    >
    >> It would be a lot of work to find every place the pointer gets
    >> dereferenced and add a null check. So I want to just ignore it by
    >> catching the signal.

    >
    > Well, as "Nobody" has already pointed out, that's not an option. Using
    > SIG_IGN for this has undefined behavior. So does registering your own
    > SIGSEGV "handler" that doesn't bother to actually do anything, and
    > simply returns. Therefore, you're going to have to either insert the
    > null checks, or let the program abort() - your choice.


    Word Jim,

    I think Eric was right on the money asking about the return value. If the
    signal() call returns success, but the "ignore" is ignored, I say that's
    a bug in the C library. I think I'll file a bug report against glibc.

    Communicate laterz ++
     
    hip cat, Aug 1, 2012
    #11
  12. hip cat

    Paul Guest

    hip cat wrote:

    >
    > Word Jim,
    >
    > I think Eric was right on the money asking about the return value. If the
    > signal() call returns success, but the "ignore" is ignored, I say that's
    > a bug in the C library. I think I'll file a bug report against glibc.
    >
    > Communicate laterz ++


    For your amusement.

    http://efreedom.com/Question/1-2663456/Write-Signal-Handler-Catch-SIGSEGV

    You can step around that busted line of code. But
    you'd have to be Yoda to succeed.

    Paul
     
    Paul, Aug 1, 2012
    #12
  13. hip cat

    Eric Sosman Guest

    On 8/1/2012 4:47 PM, hip cat wrote:
    >[...]
    > I think Eric was right on the money asking about the return value. If the
    > signal() call returns success, but the "ignore" is ignored, I say that's
    > a bug in the C library. I think I'll file a bug report against glibc.


    If signal(SIGSEGV, SIG_IGN) does nothing and indicates success,
    you may have a case. But if it can't in fact do anything, the fix
    will be to have it return SIG_ERR and tell you so. That is, you're
    still not going to get the signal ignored! (Nor should you, as has
    been pointed out elsethread.)

    You mention glibc, which means you're probably on a POSIX-like
    system. If so, the program's behavior is undefined if SIGSEGV
    occurs while it's being ignored -- and among the possible undefined
    behaviors is "SIGSEGV crashes the program anyhow." So from a POSIX
    perspective there's no bug: It might be a nice enhancement to have
    signal() return SIG_ERR, but returning 0 doesn't reach "bug" level.

    What remains unclear (to me) is how the C Standard rules on this
    issue. We know the behavior is undefined if SIGSEGV goes to a handler
    and the handler returns, but SIG_IGN isn't a "handler." So it's
    possible the C Standard and POSIX disagree here -- but it would not
    surprise me one little bit to learn that I've overlooked something.

    --
    Eric Sosman
    d
     
    Eric Sosman, Aug 1, 2012
    #13
  14. hip cat

    James Kuyper Guest

    On 08/01/2012 04:47 PM, hip cat wrote:
    > On Wed, 01 Aug 2012 15:33:20 -0400, James Kuyper wrote:

    ....
    >> Well, as "Nobody" has already pointed out, that's not an option. Using
    >> SIG_IGN for this has undefined behavior. So does registering your own
    >> SIGSEGV "handler" that doesn't bother to actually do anything, and
    >> simply returns. Therefore, you're going to have to either insert the
    >> null checks, or let the program abort() - your choice.

    >
    > Word Jim,
    >
    > I think Eric was right on the money asking about the return value. If the
    > signal() call returns success, but the "ignore" is ignored, I say that's
    > a bug in the C library. I think I'll file a bug report against glibc.


    The C standard specifies that dereferencing a null pointer has
    undefined behavior. One of the possibilities allowed by that fact is
    that a SIGSEGV signal is raised, but the standard does not require that
    it be raised. Whether or not that signal is raised, and regardless of
    whether the SIGSEGV handler has been set to SIG_IGN, SIG_DFL, or an
    actual signal handler, the behavior of your program remains undefined by
    the C standard. It doesn't matter what the signal handler actually does,
    the behavior of your program became undefined as soon as it dereferenced
    a null pointer.

    The POSIX standard does specify that a SIGSEGV signal be raised, but
    specifies that the behavior of your program is undefined if that signal
    is ignored, or if the signal handler returns.

    "undefined behavior" means that anything is permitted, including
    aborting your program. Your program aborted, which is one of the
    permitted behaviors. On what grounds are you going to report a bug? What
    requirement of any standard is violated by the fact that your program
    aborted?
     
    James Kuyper, Aug 1, 2012
    #14
  15. hip cat

    James Kuyper Guest

    On 08/01/2012 04:47 PM, hip cat wrote:
    > On Wed, 01 Aug 2012 15:33:20 -0400, James Kuyper wrote:

    ....
    >> Well, as "Nobody" has already pointed out, that's not an option. Using
    >> SIG_IGN for this has undefined behavior. So does registering your own
    >> SIGSEGV "handler" that doesn't bother to actually do anything, and
    >> simply returns. Therefore, you're going to have to either insert the
    >> null checks, or let the program abort() - your choice.

    >
    > Word Jim,
    >
    > I think Eric was right on the money asking about the return value. If the
    > signal() call returns success, but the "ignore" is ignored, I say that's
    > a bug in the C library. I think I'll file a bug report against glibc.


    The C standard specifies that dereferencing a null pointer has
    undefined behavior. One of the possibilities allowed by that fact is
    that a SIGSEGV signal is raised, but the standard does not require that
    it be raised. Whether or not that signal is raised, and regardless of
    whether the SIGSEGV handler has been set to SIG_IGN, SIG_DFL, or an
    actual signal handler, the behavior of your program remains undefined by
    the C standard. It doesn't matter what the signal handler actually does,
    the behavior of your program became undefined as soon as it dereferenced
    a null pointer.

    The POSIX standard does specify that a SIGSEGV signal be raised, but
    specifies that the behavior of your program is undefined if that signal
    is ignored, or if the signal handler returns.

    "undefined behavior" means that anything is permitted, including
    aborting your program. Your program aborted, which is one of the
    permitted behaviors. On what grounds are you going to report a bug? What
    requirement of any standard is violated by the fact that your program
    aborted?
     
    James Kuyper, Aug 1, 2012
    #15
  16. Eric Sosman <> writes:
    <snip>
    > What remains unclear (to me) is how the C Standard rules on this
    > issue. We know the behavior is undefined if SIGSEGV goes to a handler
    > and the handler returns, but SIG_IGN isn't a "handler." So it's
    > possible the C Standard and POSIX disagree here -- but it would not
    > surprise me one little bit to learn that I've overlooked something.


    I don't think there is anything explicit, but neither can I see any room
    for disagreement but, first, a nit: the behaviour is not undefined if
    the signal is the result of a call to 'raise'. The signal can be
    handled or ignored and, if handled, the handler can return. This is
    true of both C and POSIX.

    When the signal is not the result of a call to 'raise', both C and POSIX
    agree that the handler, if it is set, must not return. Thus the only
    possible disagreement could be when the signal was not raised explicitly
    and the signal is being ignored. As you say, POSIX declares this
    explicitly to be UB, but C does not.

    However, C does say that a SIGSEGV results from "an invalid access to
    storage". It's hard to imagine an invalid access that does not already
    result in UB as far as C is concerned. Some will be explicit (like *0)
    but, since the standard defines what valid accesses are, all the rest
    (that are not explicitly UB) must be UB by omission. And if the access
    has already made the behaviour of the program undefined, no signal
    setting can alter that.

    It's not exactly cast-iron, but I can't see anything in gap between the
    two standards on this issue.

    --
    Ben.
     
    Ben Bacarisse, Aug 2, 2012
    #16
  17. hip cat <> writes:
    <snip>
    > My code has a certain pointer that sometimes unexpectedly becomes null.
    > It would be a lot of work to find every place the pointer gets
    > dereferenced and add a null check. So I want to just ignore it by
    > catching the signal.


    From a purely practical point of view, I can't urge you strongly enough
    to run your program using a memory checker like valgrind. I'd have
    swapped all other debugging tools for something like valgrind when I was
    wrestling with loads of buggy C (well, non-portable C is probably more
    fair), but there was no such tool available. Don't ignore it now if it
    is available on your platform.

    --
    Ben.
     
    Ben Bacarisse, Aug 2, 2012
    #17
  18. hip cat

    Nick Bowler Guest

    On Thu, 02 Aug 2012 00:15:39 +0100, Ben Bacarisse wrote:

    > Eric Sosman <> writes:
    > <snip>
    >> What remains unclear (to me) is how the C Standard rules on this
    >> issue. We know the behavior is undefined if SIGSEGV goes to a handler
    >> and the handler returns, but SIG_IGN isn't a "handler." So it's
    >> possible the C Standard and POSIX disagree here -- but it would not
    >> surprise me one little bit to learn that I've overlooked something.

    >
    > I don't think there is anything explicit, but neither can I see any room
    > for disagreement but, first, a nit: the behaviour is not undefined if
    > the signal is the result of a call to 'raise'. The signal can be
    > handled or ignored and, if handled, the handler can return. This is
    > true of both C and POSIX.


    I'm not so certain if it is ok for such a handler to return normally in
    standard C. POSIX provides an explicit exception for a SIGSEGV handler
    which returns in the case where the signal was generated by a call to
    kill, sigqueue or raise. (Curiously, pthread_kill is missing from that
    list). However, there is no similar language in the C11 standard,
    leading me to believe that the behaviour is undefined regardless of
    whether the signal was the result of a call to raise or not. I've
    quoted the relevant passages from C11[1] and SUSv4[2] for comparison.

    [1] [C11§7.4.1.1#3] When a signal occurs and func points to a
    function, ... if and when the function returns, if the value
    of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-
    defined value corresponding to a computational exception, the
    behaviour is undefined; otherwise ...

    [2] [SUSv4§2.4.3] The behavior of a process is undefined after it
    returns normally from a signal-catching function for a SIGBUS,
    SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(),
    sigqueue(), or raise().

    Nothing in the C11 passage suggests that it is acceptable to return from
    a SIGSEGV handler that was invoked due to a call to raise. Moreover,
    from the above passage, this implies that a conforming implementation
    could define every single signal to "correspond to a computational
    exception". Therefore, no strictly conforming program may ever return
    normally from any signal handler.

    Perhaps this is an oversight in the standard, or it's specified
    elsewhere and I've simply missed it. But anyway, all this reinforces
    my belief that signals, as described by the C standards, are too
    ill-specified to be useful at all, and may as well not be there.
     
    Nick Bowler, Aug 2, 2012
    #18
  19. Nick Bowler <> writes:

    > On Thu, 02 Aug 2012 00:15:39 +0100, Ben Bacarisse wrote:
    >
    >> Eric Sosman <> writes:
    >> <snip>
    >>> What remains unclear (to me) is how the C Standard rules on this
    >>> issue. We know the behavior is undefined if SIGSEGV goes to a handler
    >>> and the handler returns, but SIG_IGN isn't a "handler." So it's
    >>> possible the C Standard and POSIX disagree here -- but it would not
    >>> surprise me one little bit to learn that I've overlooked something.

    >>
    >> I don't think there is anything explicit, but neither can I see any room
    >> for disagreement but, first, a nit: the behaviour is not undefined if
    >> the signal is the result of a call to 'raise'. The signal can be
    >> handled or ignored and, if handled, the handler can return. This is
    >> true of both C and POSIX.

    >
    > I'm not so certain if it is ok for such a handler to return normally in
    > standard C. POSIX provides an explicit exception for a SIGSEGV handler
    > which returns in the case where the signal was generated by a call to
    > kill, sigqueue or raise. (Curiously, pthread_kill is missing from that
    > list). However, there is no similar language in the C11 standard,
    > leading me to believe that the behaviour is undefined regardless of
    > whether the signal was the result of a call to raise or not. I've
    > quoted the relevant passages from C11[1] and SUSv4[2] for comparison.
    >
    > [1] [C11§7.4.1.1#3] When a signal occurs and func points to a
    > function, ... if and when the function returns, if the value
    > of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-
    > defined value corresponding to a computational exception, the
    > behaviour is undefined; otherwise ...
    >
    > [2] [SUSv4§2.4.3] The behavior of a process is undefined after it
    > returns normally from a signal-catching function for a SIGBUS,
    > SIGFPE, SIGILL, or SIGSEGV signal that was not generated by kill(),
    > sigqueue(), or raise().


    How odd! I am sure you are right, but I was equally sure I'd seen some
    claim that it was OK to return from explicitly raised "computational
    exceptions" in the C standard. Oh well, good to know. Thanks.

    <snip>
    --
    Ben.
     
    Ben Bacarisse, Aug 2, 2012
    #19
  20. Paul wrote:
    > hip cat wrote:
    >
    >>
    >> Word Jim,
    >>
    >> I think Eric was right on the money asking about the return value. If
    >> the signal() call returns success, but the "ignore" is ignored, I say
    >> that's a bug in the C library. I think I'll file a bug report against
    >> glibc.
    >>
    >> Communicate laterz ++

    >
    > For your amusement.
    >
    > http://efreedom.com/Question/1-2663456/Write-Signal-Handler-Catch-SIGSEGV
    >
    > You can step around that busted line of code. But
    > you'd have to be Yoda to succeed.
    >
    > Paul


    It is quite possible. In Linux, at least, where the machine state is
    passed to the signal handler. You can find out the instruction that
    caused the exception and advance the instruction pointer to point to the
    next one, thus avoiding subsequent segfaults. This is rather difficult
    with the usual variable-length opcodes, and would require limited
    instruction decoding.
    ....
    Of course, that does not mean that your program will do anything
    meaningful. Register contents will end up undefined etc...
     
    Johann Klammer, Aug 2, 2012
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael Pronath
    Replies:
    1
    Views:
    1,240
    Diez B. Roggisch
    Jan 3, 2005
  2. Jack Orenstein

    threading.Thread vs. signal.signal

    Jack Orenstein, Sep 18, 2005, in forum: Python
    Replies:
    0
    Views:
    500
    Jack Orenstein
    Sep 18, 2005
  3. Weng Tianxiang
    Replies:
    2
    Views:
    689
    Jonathan Bromley
    Jan 30, 2007
  4. Nicolas Moreau
    Replies:
    9
    Views:
    3,358
  5. dibacco73
    Replies:
    1
    Views:
    697
    joris
    Feb 12, 2009
Loading...

Share This Page