1/* This changes the definition of ucontext_t */
2#define _XOPEN_SOURCE 1
3#include <stdio.h>
4#include <unistd.h>
5#include <signal.h>
6#include <string.h>
7#include <stdbool.h>
8#include <valgrind.h>
9
10#define offsetof(type, fld)	((unsigned long)&((type *)0)->fld)
11#define stringify(x)		#x
12
13static int verbose = 0;
14
15#define _ASSERT_OP(a, op, b) \
16    do { \
17	unsigned long long _a = (unsigned long long)(a); \
18	unsigned long long _b = (unsigned long long)(b); \
19	if (verbose) \
20	    fprintf(stderr, "%s:%d: ASSERT(" stringify(a) \
21		    " " stringify(op) " " stringify(b) ")\n", \
22		    __FILE__, __LINE__); \
23	if (!(_a op _b)) { \
24	    fprintf(stderr, "%s:%d: FAILED ASSERT((" stringify(a) \
25		    "=0x%016llx) " stringify(op) " (" stringify(b) "=0x%016llx))\n", \
26		    __FILE__, __LINE__, _a, _b); \
27	    _exit(1); \
28	} \
29    } while(0)
30#define ASSERT_EQ(a, b) _ASSERT_OP(a, ==, b)
31#define ASSERT_NE(a, b) _ASSERT_OP(a, !=, b)
32#define ASSERT_LTE(a, b) _ASSERT_OP(a, <=, b)
33#define ASSERT_GTE(a, b) _ASSERT_OP(a, >=, b)
34#define ASSERT(e) \
35    do { \
36	if (verbose) \
37	    fprintf(stderr, "%s:%d: ASSERT(" stringify(e) ")\n", \
38		    __FILE__, __LINE__); \
39	if (!(e)) { \
40	    fprintf(stderr, "%s:%d: FAILED ASSERT(" stringify(e) ")\n", \
41		    __FILE__, __LINE__); \
42	    _exit(1); \
43	} \
44    } while(0)
45
46
47static bool using_int3 = false;
48static volatile int sig_count = 0;
49static volatile int ran_after_fault = 0;
50static void *top_of_stack;
51static void *bottom_of_stack;
52
53void this_function_halts(void)
54{
55    int foo;
56    bottom_of_stack = &foo - 4;
57    /* EAX is used by compiler-generated code to
58     * increment ran_after_fault, so we need to
59     * preserve it in our asm section */
60    unsigned long saved_eax;
61
62    /* Set up registers with known values which will be tested in the signal handler */
63    __asm__ volatile("movl %%eax, %0" : "=m"(saved_eax));
64    __asm__ volatile("movl $0xfeed0101,%eax");
65    __asm__ volatile("movl $0xfeed0202,%ebx");
66    __asm__ volatile("movl $0xfeed0303,%ecx");
67    __asm__ volatile("movl $0xfeed0404,%edx");
68    __asm__ volatile("movl $0xfeed0505,%edi");
69    __asm__ volatile("movl $0xfeed0606,%esi");
70    __asm__ volatile("hlt");
71    __asm__ volatile("movl %0, %%eax" : : "m"(saved_eax));
72    ran_after_fault++;
73}
74
75void this_function_int3s(void)
76{
77    int foo;
78    bottom_of_stack = &foo - 4;
79    unsigned long saved_eax;
80
81    /* Set up registers with known values which will be tested in the signal handler */
82    __asm__ volatile("movl %%eax, %0" : "=m"(saved_eax));
83    __asm__ volatile("movl $0xfeed0101,%eax");
84    __asm__ volatile("movl $0xfeed0202,%ebx");
85    __asm__ volatile("movl $0xfeed0303,%ecx");
86    __asm__ volatile("movl $0xfeed0404,%edx");
87    __asm__ volatile("movl $0xfeed0505,%edi");
88    __asm__ volatile("movl $0xfeed0606,%esi");
89    __asm__ volatile("int $3");
90    __asm__ volatile("movl %0, %%eax" : : "m"(saved_eax));
91    ran_after_fault++;
92}
93
94
95static void
96handle_signal(int sig, siginfo_t *si, void *vuc)
97{
98    ucontext_t *uc = (ucontext_t *)vuc;
99
100    if (verbose)
101    {
102	fprintf(stderr, "handle_signal\n");
103	fflush(stderr);
104    }
105
106    sig_count++;
107    ASSERT(sig_count == 1);
108
109    int expected_sig = (using_int3 ? SIGTRAP : SIGSEGV);
110    ASSERT_EQ(sig, expected_sig);
111    ASSERT_NE(si, NULL);
112    ASSERT_NE(uc, NULL);
113    ASSERT_NE(uc->uc_mcontext, NULL);
114
115    /* Test that the siginfo is set up right for this signal */
116    ASSERT_EQ(si->si_signo, expected_sig);
117    ASSERT_EQ(si->si_errno, 0);
118    int expected_code = (using_int3 ? 1 : 0);
119    ASSERT_EQ(si->si_code, expected_code);
120    ASSERT_EQ(si->si_pid, 0);
121    ASSERT_EQ(si->si_uid, 0);
122    ASSERT_EQ(si->si_status, 0);
123    ASSERT_EQ(si->si_addr, 0);
124    ASSERT_EQ(si->si_band, 0);
125
126    /* Test that various registers were saved in the signal ucontext */
127    ASSERT_EQ(uc->uc_mcontext->__ss.__eax, 0xfeed0101);
128    ASSERT_EQ(uc->uc_mcontext->__ss.__ebx, 0xfeed0202);
129    ASSERT_EQ(uc->uc_mcontext->__ss.__ecx, 0xfeed0303);
130    ASSERT_EQ(uc->uc_mcontext->__ss.__edx, 0xfeed0404);
131    ASSERT_EQ(uc->uc_mcontext->__ss.__edi, 0xfeed0505);
132    ASSERT_EQ(uc->uc_mcontext->__ss.__esi, 0xfeed0606);
133
134    /* Test that the saved EBP and ESP point into roughly the right
135     * part of the stack */
136    ASSERT_GTE(uc->uc_mcontext->__ss.__ebp, bottom_of_stack);
137    ASSERT_LTE(uc->uc_mcontext->__ss.__ebp, top_of_stack);
138    ASSERT_GTE(uc->uc_mcontext->__ss.__esp, bottom_of_stack);
139    ASSERT_LTE(uc->uc_mcontext->__ss.__esp, top_of_stack);
140
141    /* Test that the saved EIP points into roughly the
142     * right part of the text segment */
143    char *calling_fn = (using_int3 ? (char *)&this_function_int3s : (char *)&this_function_halts);
144    ASSERT_GTE(uc->uc_mcontext->__ss.__eip, calling_fn);
145    ASSERT_LTE(uc->uc_mcontext->__ss.__eip, calling_fn+400);
146
147    /*
148    printf("	    RFLAGS 0x%016llx\n", (unsigned long long)uc->uc_mcontext->__ss.__rflags);
149    */
150
151    /*
152     * Test that the EIP is restored from the signal ucontext;
153     * this should skip past the HLT/INT instruction and
154     * allow execution to continue back out to main()
155     */
156    if (verbose)
157    {
158	fprintf(stderr, "Setting up to return past the HLT\n");
159	fflush(stderr);
160    }
161    uc->uc_mcontext->__ss.__eip += (using_int3 ? 0 : 1);
162
163    if (verbose)
164    {
165	fprintf(stderr, "Returning from signal handler\n");
166	fflush(stderr);
167    }
168}
169
170int main(int argc, char **argv)
171{
172    int r;
173    struct sigaction act;
174
175    top_of_stack = (void *)&act;
176
177    if (argc > 1 && !strcmp(argv[1], "--verbose"))
178	verbose = 1;
179
180    if (verbose)
181	printf("Setting up signal handler\n");
182    memset(&act, 0, sizeof(act));
183    act.sa_sigaction = handle_signal;
184    act.sa_flags |= SA_SIGINFO;
185    if (RUNNING_ON_VALGRIND)
186	using_int3 = true;
187    r = sigaction((using_int3 ? SIGTRAP : SIGSEGV), &act, NULL);
188    ASSERT_EQ(r, 0);
189
190    if (verbose)
191    {
192	fprintf(stderr, "Calling function with a breakpoint insn in it\n");
193	fflush(stderr);
194    }
195    if (using_int3)
196	this_function_int3s();
197    else
198	this_function_halts();
199    ASSERT_EQ(ran_after_fault, 1);
200
201    fprintf(stderr, "PASS\n");
202    return 0;
203}
204