Re: reliable reproducer, was Re: core dump analysis

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Andreas,

On 24/04/23 09:48, Andreas Schwab wrote:
On Apr 24 2023, Michael Schmitz wrote:

Not sure what third argument you referred to in another mail.
See struct sigframe and struct rt_sigframe.  The non-rt signal handler
gets signal number, vector number and sigcontext*.  The rt signal
handler gets signal number, siginfo* and ucontext*.

Thanks, I see now. Got confused by the sigaction man page (despite working out that it's all there on the stack before...). Might need a comment in the code (or an update to the man pages).

I've rewritten my test program to make the non-rt handler take three arguments, just to simplify things. Also fixed the end of signal frame calculation for the non-rt case where the exception places additional data on the stack.

Running with the non-rt handler, the crash appears to happen right at the end of the recursion (or at least, I take no further SIGCHLD on the way back up the stack). With the rt handler, I see the stack depth decreased on the last signal taken before the crash.

When I enable dumping the extra exception frame contents (which will show prior stack contents when the exception only used a four-word frame) in the rt handler case, I only see saved register data placed there at the very end. That's different from previous tests where I saw the saved register patterns all the time. (but might have had the offsets wrong).

I'll see what peeking at the registers shows (now that I can be confident I have got the offsets correct).

Cheers,

    Michael



#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <ucontext.h>
#include <asm/ptrace.h>

/* how deep at recursion */
int depth = 200000;
const int max_depth = 200000; 
/* how deep still on the stack while returning */
int rdepth = 0;

const unsigned long i0 = 0x91929394;
const unsigned long i1 = 0xa1a2a3a4;
const unsigned long i2 = 0xb1b2b3b4;
const unsigned long i3 = 0xc1c2c3c4;
const unsigned long i4 = 0xd1d2d3d4;
const unsigned long i5 = 0xe1e2e3e4;
const unsigned long i6 = 0xf1f2f3f4;

unsigned long o0;
unsigned long o1;
unsigned long o2;
unsigned long o3;
unsigned long o4;
unsigned long o5;
unsigned long o6;

unsigned long parent_usp;

/* extra frame size used for sa_handler case */
static const int frame_size_change[16] = {
  [1]   = -1, /* sizeof_field(struct frame, un.fmt1), */
  [2]   = 4, /* sizeof_field(struct frame, un.fmt2), */
  [3]   = 4, /* sizeof_field(struct frame, un.fmt3), */
  [4]   = 8, /* sizeof_field(struct frame, un.fmt4), */
  [5]   = -1, /* sizeof_field(struct frame, un.fmt5), */
  [6]   = -1, /* sizeof_field(struct frame, un.fmt6), */
  [7]   = 52, /* sizeof_field(struct frame, un.fmt7), */
  [8]   = -1, /* sizeof_field(struct frame, un.fmt8), */
  [9]   = 12, /* sizeof_field(struct frame, un.fmt9), */
  [10]  = 20, /* sizeof_field(struct frame, un.fmta), */
  [11]  = 84, /* sizeof_field(struct frame, un.fmtb), */
  [12]  = -1, /* sizeof_field(struct frame, un.fmtc), */
  [13]  = -1, /* sizeof_field(struct frame, un.fmtd), */
  [14]  = -1, /* sizeof_field(struct frame, un.fmte), */
  [15]  = -1, /* sizeof_field(struct frame, un.fmtf), */
};

static inline int frame_extra_sizes(int f)
{
        return frame_size_change[f];
}

static void rec(void)
{
	// initialize registers
	asm(	"	move.l %0, %%a2\n" 
		"	move.l %1, %%a3\n"
		"	move.l %2, %%a4\n"
		"	move.l %3, %%a5\n"
		"	move.l %4, %%d2\n"
		"	move.l %5, %%d3\n"
		"	move.l %6, %%d4\n"
		:
		: "m" (i0), "m" (i1), "m" (i2),
		  "m" (i3), "m" (i4), "m" (i5), "m" (i6)
		: "a2", "a3", "a4", "a5", "d2", "d3", "d4"
	);

	// note current usp
	asm(	"	move.l %%sp, %0\n" 
		: "=m" (parent_usp)
		:
		: "memory" 
	);

	// maybe fork a short-lived process
	if ((depth & 0x7ff) == 0)
		if (fork() == 0)
			exit(0);

	if (--depth)
		rec();	// callee to save & restore registers

	// compare register contents
	asm(	"	move.l %%a2, %0\n" 
		"	move.l %%a3, %1\n"
		"	move.l %%a4, %2\n"
		"	move.l %%a5, %3\n"
		"	move.l %%d2, %4\n"
		"	move.l %%d3, %5\n"
		"	move.l %%d4, %6\n"
		: "=m" (o0), "=m" (o1), "=m" (o2),
		  "=m" (o3), "=m" (o4), "=m" (o5), "=m" (o6)
		:
		: "memory" /* "a2", "a3", "a4", "a5", "d2", "d3", "d4" */
	);
	if (o0 != i0 || o1 != i1 || o2 != i2 ||
	    o3 != i3 || o4 != i4 || o5 != i5 || o6 != i6)
		asm("illegal");

	rdepth++;
}

static void handler(int signo, int vector, struct sigcontext *ctx)
{
	// use if only taking signo argument  
	// struct sigcontext *ctx = (struct sigcontext *) ((unsigned long) &signo + 24);
	unsigned long *frame = (unsigned long *)((unsigned long) &signo - 4);

	if (signo == SIGCHLD) {
		unsigned long usp, end_frame, extra;
		int i;

		asm(	"	move.l %%sp, %0\n" 
			: "=m" (usp)
			:
			: "memory" 
		);

		fprintf(stderr, "stack depth : %d\n", max_depth - depth);
		fprintf(stderr, "retn. depth : %d\n", max_depth - rdepth);
		fprintf(stderr, "parent usp  : 0x%lx\n", parent_usp);
		/* top of signal stack - including extra size for
		 * exception frames longer than four words
		 */
		// end_frame = (unsigned long) frame + 32 + sizeof(struct sigcontext);
		extra = frame_extra_sizes(ctx->sc_formatvec >> 12);
		end_frame = (unsigned long) ctx + sizeof(struct sigcontext) + extra;
                if (end_frame  > parent_usp)
                        fprintf(stderr, "signal frame overwrote parent usp!\n");
		fprintf(stderr, "frame end   : 0x%lx\n", end_frame);
		fprintf(stderr, "frame start : 0x%lx\n", frame);
		fprintf(stderr, "handler usp : 0x%lx\n", usp);	
		// fprintf(stderr, "signal vec  : 0x%lx\n", vector);
		fprintf(stderr, "signal usp  : 0x%lx\n", ctx->sc_usp);
		fprintf(stderr, "signal pc   : 0x%lx\n", ctx->sc_pc);
		fprintf(stderr, "signal fmtv : 0x%lx\n", ctx->sc_formatvec);
		fprintf(stderr, "\n");
	}
}
            
static void rthandler(int signo, siginfo_t *info, void *ucontext)
{
	ucontext_t *ctx = (ucontext_t *) ucontext;
	unsigned long *frame = (unsigned long *)((unsigned long) &signo - 4);

	if (signo == SIGCHLD) {
		unsigned long usp, end_frame;
		int i;

		asm(	"	move.l %%sp, %0\n" 
			: "=m" (usp)
			:
			: "memory" 
		);
		fprintf(stderr, "stack depth : %d\n", max_depth - depth);
		fprintf(stderr, "retn. depth : %d\n", max_depth - rdepth);
		fprintf(stderr, "parent usp  : 0x%lx\n", parent_usp);
		/* top of signal stack - extra size for exception
		 * frames longer than four words follows uc_filler[54]
		 */
		// end_frame = (unsigned long) info + sizeof(struct siginfo) + sizeof(struct ucontext);
		end_frame = (unsigned long) ucontext + sizeof(struct ucontext);
		if (end_frame > parent_usp)
			fprintf(stderr, "signal stack overwrote parent usp!\n");
		fprintf(stderr, "frame end   : 0x%lx\n", end_frame);
		fprintf(stderr, "frame start : 0x%lx\n", frame);
		fprintf(stderr, "handler usp : 0x%lx\n", usp);
		fprintf(stderr, "signal usp  : 0x%lx\n", ctx->uc_mcontext.gregs[15]);
		fprintf(stderr, "signal pc   : 0x%lx\n", ctx->uc_mcontext.gregs[16]);
		fprintf(stderr, "signal fmtv : 0x%lx\n", ctx->uc_filler[54]);
#if 0
		/* dump frame contents not used for exception frame extra */
		if (frame_extra_sizes(ctx->uc_filler[54] >> 12) <= 0)
			for (i=0; i < 26; i++) {
				if ( !(i & 3) ) fprintf(stderr, "\nextra %d\t", i);
				fprintf(stderr, "0x%lx\t", ctx->uc_filler[55+i]);
			}
#endif
		fprintf(stderr, "\n");
#if 0
		/* dump stored registers */
		fprintf(stderr, "signal stack: 0x%lx\n", ctx->uc_stack.ss_sp);
		fprintf(stderr, "signal usp  : 0x%lx\n", ctx->uc_mcontext.gregs[15]);
		fprintf(stderr, "signal pc   : 0x%lx\n", ctx->uc_mcontext.gregs[16]);
		fprintf(stderr, "signal f.d1 : 0x%lx\n", ctx->uc_filler[41]);
		fprintf(stderr, "signal f.d2 : 0x%lx\n", ctx->uc_filler[42]);
		fprintf(stderr, "signal f.d3 : 0x%lx\n", ctx->uc_filler[43]);
		fprintf(stderr, "signal f.d4 : 0x%lx\n", ctx->uc_filler[44]);
		fprintf(stderr, "signal f.d5 : 0x%lx\n", ctx->uc_filler[45]);
		fprintf(stderr, "signal f.a0 : 0x%lx\n", ctx->uc_filler[46]);
		fprintf(stderr, "signal f.a1 : 0x%lx\n", ctx->uc_filler[47]);
		fprintf(stderr, "signal f.a2 : 0x%lx\n", ctx->uc_filler[48]);
		fprintf(stderr, "signal f.d0 : 0x%lx\n", ctx->uc_filler[49]);
		fprintf(stderr, "signal f.od0: 0x%lx\n", ctx->uc_filler[50]);
		fprintf(stderr, "signal f.sad: 0x%lx\n", ctx->uc_filler[51]);
		fprintf(stderr, "signal f.sr : 0x%lx\n", ctx->uc_filler[52]);
		fprintf(stderr, "signal f.pc : 0x%lx\n", ctx->uc_filler[53]);
		fprintf(stderr, "signal a2   : 0x%lx\n", ctx->uc_mcontext.gregs[10]);
		fprintf(stderr, "signal a3   : 0x%lx\n", ctx->uc_mcontext.gregs[11]);
		fprintf(stderr, "signal a4   : 0x%lx\n", ctx->uc_mcontext.gregs[12]);
		fprintf(stderr, "signal a5   : 0x%lx\n", ctx->uc_mcontext.gregs[13]);
		fprintf(stderr, "signal d2   : 0x%lx\n", ctx->uc_mcontext.gregs[2]);
		fprintf(stderr, "signal d3   : 0x%lx\n", ctx->uc_mcontext.gregs[3]);
		fprintf(stderr, "signal d4   : 0x%lx\n\n", ctx->uc_mcontext.gregs[4]);
#endif
	}
}

int main(int use_rt)

{
	struct sigaction act;
	stack_t ss;

	use_rt--;
	/* poor man's getopt - use none, one or two args */ 
	fprintf(stderr, "use_rt = %d\n", use_rt);

	ss.ss_sp = malloc(SIGSTKSZ);
	if (ss.ss_sp == NULL) {
		perror("malloc");
		exit(EXIT_FAILURE);
	}
	
	ss.ss_size = SIGSTKSZ;
	ss.ss_flags = 0;

	if (use_rt & 2)
		fprintf(stderr, "alt stack 0x%lx - 0x%lx\n", ss.ss_sp, ss.ss_sp + ss.ss_size); 

	if (sigaltstack(&ss, NULL) == -1) {
		perror("sigaltstack");
		exit(EXIT_FAILURE);
	}

	memset(&act, 0, sizeof(act));
	sigemptyset(&act.sa_mask);

	if (use_rt) { 
		act.sa_flags = SA_SIGINFO;
		if (use_rt & 2) 
			act.sa_flags |= SA_ONSTACK;
		fprintf(stderr, "using sa_sigaction\n");
		act.sa_sigaction = rthandler;
	} else {
		fprintf(stderr, "using sa_handler\n");
		act.sa_handler = handler;
	}

	if (sigaction(SIGCHLD, &act, NULL) == -1) {
		perror("sigaction");
		exit(EXIT_FAILURE);
	}

	rec();
}

[Index of Archives]     [Video for Linux]     [Yosemite News]     [Linux S/390]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux