Project Home
Project Home
Documents
Documents
Wiki
Wiki
Discussion Forums
Discussions
Project Information
Project Info
Forum Topic - sigsetjmp appears broken on ARM 660: (2 Items)
   
sigsetjmp appears broken on ARM 660  
Hi,

I am trying to use the sigsetjmp/siglongjmp mechanism on an ARM qnx660 target.

I am getting strange errors, where the siglongjmp is trying to return to corrupted addresses.

I disassembled the sigsetjmp/siglongjmp implementation and believe that it is broken. My analysis of the implementation 
follows:

    LOAD:00020288 sigsetjmp
[0] LOAD:00020288                 STMFD           SP!, {R4,LR}
    LOAD:0002028C                 MOV             R4, R0
    LOAD:00020290                 BL              j___sigjmp_prolog
    LOAD:00020294                 MOV             R0, R4  ; env
    LOAD:00020298                 BL              _setjmp
[1] LOAD:0002029C                 LDMFD           SP!, {R4,PC}

    LOAD:00032808 _setjmp                                 ; CODE XREF: j_procmgr_event_notify_add+8j
    LOAD:00032808                                         ; j__FXp_addx+8j ...
[2] LOAD:00032808                 STMIA           R0, {R4-R11,SP,LR}
    LOAD:0003280C                 STMFD           SP!, {R0,LR}
    LOAD:00032810                 BL              sub_4B5A8
    LOAD:00032814                 LDMFD           SP!, {R1,LR}
    LOAD:00032818                 TEQ             R0, #0
    LOAD:0003281C                 STRNE           R0, [R1,#0x24]
    LOAD:00032820                 MOV             R0, #0
    LOAD:00032824                 RET

    LOAD:0004B5A8 sub_4B5A8                               ; CODE XREF: _setjmp+8p
    LOAD:0004B5A8                 LDR             R2, =(dword_8F978 - 0x4B5B8)
    LOAD:0004B5AC                 LDR             R3, =(_GLOBAL_OFFSET_TABLE_ - 0x4B5BC)
    LOAD:0004B5B0                 ADD             R2, PC, R2 ; dword_8F978
    LOAD:0004B5B4                 ADD             R3, PC, R3 ; _GLOBAL_OFFSET_TABLE_
    LOAD:0004B5B8                 LDR             R2, [R2,#(dword_8F994 - 0x8F978)]
    LOAD:0004B5BC                 CMP             R2, #0
    LOAD:0004B5C0                 BLT             loc_4B5CC
    LOAD:0004B5C4                 MOV             R0, #0
[3] LOAD:0004B5C8                 BX              LR
    LOAD:0004B5CC ; ---------------------------------------------------------------------------
    LOAD:0004B5CC
    LOAD:0004B5CC loc_4B5CC                               ; CODE XREF: sub_4B5A8+18j
    LOAD:0004B5CC                 LDR             R2, =(off_8E0B8 - 0x8D000)
    LOAD:0004B5D0                 LDR             R3, [R3,R2] ; off_8E0B8
    LOAD:0004B5D4                 LDR             R3, [R3]
    LOAD:0004B5D8                 LDR             R3, [R3]
    LOAD:0004B5DC                 LDR             R3, [R3,#0x38]
    LOAD:0004B5E0                 LDR             R3, [R3,#4]
    LOAD:0004B5E4                 LDR             R2, [R3,#4]
    LOAD:0004B5E8                 SUB             R1, R2, #1
    LOAD:0004B5EC                 STR             R1, [R0]
    LOAD:0004B5F0                 LDR             R0, [R3,R2,LSL#3]
    LOAD:0004B5F4                 BX              LR
    LOAD:0004B5F4 ; End of function sub_4B5A8

At [0] the original R4 and LR are saved on the stack. These values are used both to return from the sigsetjmp function 
at [1] and as the 
storage for the R4 and longjmp target PC as we'll see in a minute.

sigsetjmp then calls __sigjmp_prolog (which just saves sigmask in the env) and then calls _setjmp.

At [2] _setjmp saves register R4-R11, the SP (which points to our saved R4 and LR) and the current LR (which is [1]) 
into the env (R0). 

It then calls sub_4b5a8, which does nothing except set R0 to 0 and return at [3].

_setjmp then checks if R0 is non-zero, but this check fails so the store is skipped. Then R0 is set to 0 and we return 
to sigsetjmp at [1].

sigsetjmp now pops the R4 and LR values from the stack into R4 and PC, thereby returning to the sigsetjmp callsite. 
_BUT_ this also releases 
the two stack slots that are pointed to by the SP saved in the env, which are used by...
View Full Message
Re: sigsetjmp appears broken on ARM 660  
After further analysis, I have confirmed that sigsetjmp is defined as both a preprocessor macro and a function, and the 
behavior is different when using the macro and the function.

Using the sigsetjmp macro works, as the saved R4 and longjmp target return address are allocated on the sigsetjmp 
caller's stack and thus are protected from modification by nested function in the (sigsetjmp (...) == 0) context.

Using the sigsetjmp function, fails as described in my analysis.

I have further implemented a test case which demonstrates the two ways of using sigsetjmp. The test app will segfault 
when trying to longjmp back from the sigsetjmp function.

[code]
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>

//#undef sigsetjmp

sigjmp_buf jb;
struct sigaction old_handler;

typedef int (*sj_func) (sigjmp_buf, int);

static sj_func get_sigsetjmp ()
{
    return sigsetjmp;
}
static void on_signal (int sig, siginfo_t * siginfo, void * context)
{
    printf ("in on_signal (%d, %p, %p)\n", sig, siginfo, context);

    printf ("about to siglongjmp\n");
    siglongjmp (jb, 1);
}

void DumpHex(const void* data, size_t size) {
	char ascii[17];
	size_t i, j;
	ascii[16] = '\0';
	for (i = 0; i < size; ++i) {
		printf("%02X ", ((unsigned char*)data)[i]);
		if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
			ascii[i % 16] = ((unsigned char*)data)[i];
		} else {
			ascii[i % 16] = '.';
		}
		if ((i+1) % 8 == 0 || i+1 == size) {
			printf(" ");
			if ((i+1) % 16 == 0) {
				printf("|  %s \n", ascii);
			} else if (i+1 == size) {
				ascii[(i+1) % 16] = '\0';
				if ((i+1) % 16 <= 8) {
					printf(" ");
				}
				for (j = (i+1) % 16; j < 16; ++j) {
					printf("   ");
				}
				printf("|  %s \n", ascii);
			}
		}
	}
}

static void foo ()
{
    int buffer[10];
    DumpHex (buffer, sizeof(buffer));

    memset(buffer, 0xaa, sizeof(buffer));
}

int main (int argc, char **argv)
{
    struct sigaction action;


    printf ("before everything\n");

    action.sa_sigaction = on_signal;
    sigemptyset (&action.sa_mask);
    action.sa_flags = SA_SIGINFO;

    printf ("registering sigaction\n");
    sigaction (SIGSEGV, &action, &old_handler);

    printf ("before sigsetjmp using macro. This should work!\n");
    //if (get_sigsetjmp () (jb, 1) == 0)
    if (sigsetjmp (jb, 1) == 0)
    {
        volatile char * x = 0;
        printf ("in protected code\n");

        foo ();

        printf ("this should fail: %s\n", x);
    }
    else
    {
        printf ("in error handler. sigsetjmp macro works!\n");
    }

    printf ("\n\nbefore sigsetjmp using function. This will fail!\n");
    if (get_sigsetjmp () (jb, 1) == 0)
    {
        volatile char * x = 0;
        printf ("in protected code\n");

        foo ();

        printf ("this should fail: %s\n", x);
    }
    else
    {
        printf ("in error handler. we never get here!\n");
    }

    printf ("after everything\n");
    return 0;
}
[/code]