Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/extable.c
Line
Count
Source (jump to first uncovered line)
1
2
#include <xen/init.h>
3
#include <xen/list.h>
4
#include <xen/perfc.h>
5
#include <xen/rcupdate.h>
6
#include <xen/sort.h>
7
#include <xen/spinlock.h>
8
#include <asm/uaccess.h>
9
#include <xen/domain_page.h>
10
#include <xen/virtual_region.h>
11
#include <xen/livepatch.h>
12
13
3.44k
#define EX_FIELD(ptr, field) ((unsigned long)&(ptr)->field + (ptr)->field)
14
15
static inline unsigned long ex_addr(const struct exception_table_entry *x)
16
3.43k
{
17
3.43k
  return EX_FIELD(x, addr);
18
3.43k
}
19
20
static inline unsigned long ex_cont(const struct exception_table_entry *x)
21
4
{
22
4
  return EX_FIELD(x, cont);
23
4
}
24
25
static int init_or_livepatch cmp_ex(const void *a, const void *b)
26
1.70k
{
27
1.70k
  const struct exception_table_entry *l = a, *r = b;
28
1.70k
  unsigned long lip = ex_addr(l);
29
1.70k
  unsigned long rip = ex_addr(r);
30
1.70k
31
1.70k
  /* avoid overflow */
32
1.70k
  if (lip > rip)
33
458
    return 1;
34
1.24k
  if (lip < rip)
35
1.24k
    return -1;
36
0
  return 0;
37
1.24k
}
38
39
#ifndef swap_ex
40
static void init_or_livepatch swap_ex(void *a, void *b, int size)
41
995
{
42
995
  struct exception_table_entry *l = a, *r = b, tmp;
43
995
  long delta = b - a;
44
995
45
995
  tmp = *l;
46
995
  l->addr = r->addr + delta;
47
995
  l->cont = r->cont + delta;
48
995
  r->addr = tmp.addr - delta;
49
995
  r->cont = tmp.cont - delta;
50
995
}
51
#endif
52
53
void init_or_livepatch sort_exception_table(struct exception_table_entry *start,
54
                                 const struct exception_table_entry *stop)
55
2
{
56
2
    sort(start, stop - start,
57
2
         sizeof(struct exception_table_entry), cmp_ex, swap_ex);
58
2
}
59
60
void __init sort_exception_tables(void)
61
1
{
62
1
    sort_exception_table(__start___ex_table, __stop___ex_table);
63
1
    sort_exception_table(__start___pre_ex_table, __stop___pre_ex_table);
64
1
}
65
66
static unsigned long
67
search_one_extable(const struct exception_table_entry *first,
68
                   const struct exception_table_entry *last,
69
                   unsigned long value)
70
4
{
71
4
    const struct exception_table_entry *mid;
72
4
    long diff;
73
4
74
28
    while ( first <= last )
75
28
    {
76
28
        mid = (last - first) / 2 + first;
77
28
        diff = ex_addr(mid) - value;
78
28
        if (diff == 0)
79
4
            return ex_cont(mid);
80
24
        else if (diff < 0)
81
20
            first = mid+1;
82
24
        else
83
4
            last = mid-1;
84
28
    }
85
0
    return 0;
86
4
}
87
88
unsigned long
89
search_exception_table(const struct cpu_user_regs *regs)
90
4
{
91
4
    const struct virtual_region *region = find_text_region(regs->rip);
92
4
    unsigned long stub = this_cpu(stubs.addr);
93
4
94
4
    if ( region && region->ex )
95
0
        return search_one_extable(region->ex, region->ex_end - 1, regs->rip);
96
4
97
4
    if ( regs->rip >= stub + STUB_BUF_SIZE / 2 &&
98
4
         regs->rip < stub + STUB_BUF_SIZE &&
99
4
         regs->rsp > (unsigned long)regs &&
100
4
         regs->rsp < (unsigned long)get_cpu_info() )
101
4
    {
102
4
        unsigned long retptr = *(unsigned long *)regs->rsp;
103
4
104
4
        region = find_text_region(retptr);
105
4
        retptr = region && region->ex
106
4
                 ? search_one_extable(region->ex, region->ex_end - 1, retptr)
107
0
                 : 0;
108
4
        if ( retptr )
109
4
        {
110
4
            /*
111
4
             * Put trap number and error code on the stack (in place of the
112
4
             * original return address) for recovery code to pick up.
113
4
             */
114
4
            union stub_exception_token token = {
115
4
                .fields.ec = regs->error_code,
116
4
                .fields.trapnr = regs->entry_vector,
117
4
            };
118
4
119
4
            *(unsigned long *)regs->rsp = token.raw;
120
4
            return retptr;
121
4
        }
122
4
    }
123
4
124
0
    return 0;
125
4
}
126
127
#ifndef NDEBUG
128
static int __init stub_selftest(void)
129
1
{
130
1
    static const struct {
131
1
        uint8_t opc[4];
132
1
        uint64_t rax;
133
1
        union stub_exception_token res;
134
1
    } tests[] __initconst = {
135
1
        { .opc = { 0x0f, 0xb9, 0xc3, 0xc3 }, /* ud1 */
136
1
          .res.fields.trapnr = TRAP_invalid_op },
137
1
        { .opc = { 0x90, 0x02, 0x00, 0xc3 }, /* nop; add (%rax),%al */
138
1
          .rax = 0x0123456789abcdef,
139
1
          .res.fields.trapnr = TRAP_gp_fault },
140
1
        { .opc = { 0x02, 0x04, 0x04, 0xc3 }, /* add (%rsp,%rax),%al */
141
1
          .rax = 0xfedcba9876543210,
142
1
          .res.fields.trapnr = TRAP_stack_error },
143
1
        { .opc = { 0xcc, 0xc3, 0xc3, 0xc3 }, /* int3 */
144
1
          .res.fields.trapnr = TRAP_int3 },
145
1
    };
146
1
    unsigned long addr = this_cpu(stubs.addr) + STUB_BUF_SIZE / 2;
147
1
    unsigned int i;
148
1
149
1
    printk("Running stub recovery selftests...\n");
150
1
151
5
    for ( i = 0; i < ARRAY_SIZE(tests); ++i )
152
4
    {
153
4
        uint8_t *ptr = map_domain_page(_mfn(this_cpu(stubs.mfn))) +
154
4
                       (addr & ~PAGE_MASK);
155
4
        unsigned long res = ~0;
156
4
157
4
        memset(ptr, 0xcc, STUB_BUF_SIZE / 2);
158
4
        memcpy(ptr, tests[i].opc, ARRAY_SIZE(tests[i].opc));
159
4
        unmap_domain_page(ptr);
160
4
161
4
        asm volatile ( "call *%[stb]\n"
162
4
                       ".Lret%=:\n\t"
163
4
                       ".pushsection .fixup,\"ax\"\n"
164
4
                       ".Lfix%=:\n\t"
165
4
                       "pop %[exn]\n\t"
166
4
                       "jmp .Lret%=\n\t"
167
4
                       ".popsection\n\t"
168
4
                       _ASM_EXTABLE(.Lret%=, .Lfix%=)
169
4
                       : [exn] "+m" (res)
170
4
                       : [stb] "rm" (addr), "a" (tests[i].rax));
171
4
        ASSERT(res == tests[i].res.raw);
172
4
    }
173
1
174
1
    return 0;
175
1
}
176
__initcall(stub_selftest);
177
#endif
178
179
unsigned long
180
search_pre_exception_table(struct cpu_user_regs *regs)
181
0
{
182
0
    unsigned long addr = regs->rip;
183
0
    unsigned long fixup = search_one_extable(
184
0
        __start___pre_ex_table, __stop___pre_ex_table-1, addr);
185
0
    if ( fixup )
186
0
    {
187
0
        dprintk(XENLOG_INFO, "Pre-exception: %p -> %p\n", _p(addr), _p(fixup));
188
0
        perfc_incr(exception_fixed);
189
0
    }
190
0
    return fixup;
191
0
}