kselftest/arm64: Fix validation of EXTRA_CONTEXT signal context location
[platform/kernel/linux-starfive.git] / tools / testing / selftests / arm64 / signal / testcases / testcases.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2019 ARM Limited */
3 #include "testcases.h"
4
5 struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
6                                 size_t resv_sz, size_t *offset)
7 {
8         size_t offs = 0;
9         struct _aarch64_ctx *found = NULL;
10
11         if (!head || resv_sz < HDR_SZ)
12                 return found;
13
14         while (offs <= resv_sz - HDR_SZ &&
15                head->magic != magic && head->magic) {
16                 offs += head->size;
17                 head = GET_RESV_NEXT_HEAD(head);
18         }
19         if (head->magic == magic) {
20                 found = head;
21                 if (offset)
22                         *offset = offs;
23         }
24
25         return found;
26 }
27
28 bool validate_extra_context(struct extra_context *extra, char **err)
29 {
30         struct _aarch64_ctx *term;
31
32         if (!extra || !err)
33                 return false;
34
35         fprintf(stderr, "Validating EXTRA...\n");
36         term = GET_RESV_NEXT_HEAD(&extra->head);
37         if (!term || term->magic || term->size) {
38                 *err = "Missing terminator after EXTRA context";
39                 return false;
40         }
41         if (extra->datap & 0x0fUL)
42                 *err = "Extra DATAP misaligned";
43         else if (extra->size & 0x0fUL)
44                 *err = "Extra SIZE misaligned";
45         else if (extra->datap != (uint64_t)term + 0x10UL)
46                 *err = "Extra DATAP misplaced (not contiguous)";
47         if (*err)
48                 return false;
49
50         return true;
51 }
52
53 bool validate_sve_context(struct sve_context *sve, char **err)
54 {
55         /* Size will be rounded up to a multiple of 16 bytes */
56         size_t regs_size
57                 = ((SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve->vl)) + 15) / 16) * 16;
58
59         if (!sve || !err)
60                 return false;
61
62         /* Either a bare sve_context or a sve_context followed by regs data */
63         if ((sve->head.size != sizeof(struct sve_context)) &&
64             (sve->head.size != regs_size)) {
65                 *err = "bad size for SVE context";
66                 return false;
67         }
68
69         if (!sve_vl_valid(sve->vl)) {
70                 *err = "SVE VL invalid";
71
72                 return false;
73         }
74
75         return true;
76 }
77
78 bool validate_za_context(struct za_context *za, char **err)
79 {
80         /* Size will be rounded up to a multiple of 16 bytes */
81         size_t regs_size
82                 = ((ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(za->vl)) + 15) / 16) * 16;
83
84         if (!za || !err)
85                 return false;
86
87         /* Either a bare za_context or a za_context followed by regs data */
88         if ((za->head.size != sizeof(struct za_context)) &&
89             (za->head.size != regs_size)) {
90                 *err = "bad size for ZA context";
91                 return false;
92         }
93
94         if (!sve_vl_valid(za->vl)) {
95                 *err = "SME VL in ZA context invalid";
96
97                 return false;
98         }
99
100         return true;
101 }
102
103 bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
104 {
105         bool terminated = false;
106         size_t offs = 0;
107         int flags = 0;
108         struct extra_context *extra = NULL;
109         struct sve_context *sve = NULL;
110         struct za_context *za = NULL;
111         struct _aarch64_ctx *head =
112                 (struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
113
114         if (!err)
115                 return false;
116         /* Walk till the end terminator verifying __reserved contents */
117         while (head && !terminated && offs < resv_sz) {
118                 if ((uint64_t)head & 0x0fUL) {
119                         *err = "Misaligned HEAD";
120                         return false;
121                 }
122
123                 switch (head->magic) {
124                 case 0:
125                         if (head->size)
126                                 *err = "Bad size for terminator";
127                         else
128                                 terminated = true;
129                         break;
130                 case FPSIMD_MAGIC:
131                         if (flags & FPSIMD_CTX)
132                                 *err = "Multiple FPSIMD_MAGIC";
133                         else if (head->size !=
134                                  sizeof(struct fpsimd_context))
135                                 *err = "Bad size for fpsimd_context";
136                         flags |= FPSIMD_CTX;
137                         break;
138                 case ESR_MAGIC:
139                         if (head->size != sizeof(struct esr_context))
140                                 *err = "Bad size for esr_context";
141                         break;
142                 case SVE_MAGIC:
143                         if (flags & SVE_CTX)
144                                 *err = "Multiple SVE_MAGIC";
145                         /* Size is validated in validate_sve_context() */
146                         sve = (struct sve_context *)head;
147                         flags |= SVE_CTX;
148                         break;
149                 case ZA_MAGIC:
150                         if (flags & ZA_CTX)
151                                 *err = "Multiple ZA_MAGIC";
152                         /* Size is validated in validate_za_context() */
153                         za = (struct za_context *)head;
154                         flags |= ZA_CTX;
155                         break;
156                 case EXTRA_MAGIC:
157                         if (flags & EXTRA_CTX)
158                                 *err = "Multiple EXTRA_MAGIC";
159                         else if (head->size !=
160                                  sizeof(struct extra_context))
161                                 *err = "Bad size for extra_context";
162                         flags |= EXTRA_CTX;
163                         extra = (struct extra_context *)head;
164                         break;
165                 case KSFT_BAD_MAGIC:
166                         /*
167                          * This is a BAD magic header defined
168                          * artificially by a testcase and surely
169                          * unknown to the Kernel parse_user_sigframe().
170                          * It MUST cause a Kernel induced SEGV
171                          */
172                         *err = "BAD MAGIC !";
173                         break;
174                 default:
175                         /*
176                          * A still unknown Magic: potentially freshly added
177                          * to the Kernel code and still unknown to the
178                          * tests.
179                          */
180                         fprintf(stdout,
181                                 "SKIP Unknown MAGIC: 0x%X - Is KSFT arm64/signal up to date ?\n",
182                                 head->magic);
183                         break;
184                 }
185
186                 if (*err)
187                         return false;
188
189                 offs += head->size;
190                 if (resv_sz < offs + sizeof(*head)) {
191                         *err = "HEAD Overrun";
192                         return false;
193                 }
194
195                 if (flags & EXTRA_CTX)
196                         if (!validate_extra_context(extra, err))
197                                 return false;
198                 if (flags & SVE_CTX)
199                         if (!validate_sve_context(sve, err))
200                                 return false;
201                 if (flags & ZA_CTX)
202                         if (!validate_za_context(za, err))
203                                 return false;
204
205                 head = GET_RESV_NEXT_HEAD(head);
206         }
207
208         if (terminated && !(flags & FPSIMD_CTX)) {
209                 *err = "Missing FPSIMD";
210                 return false;
211         }
212
213         return true;
214 }
215
216 /*
217  * This function walks through the records inside the provided reserved area
218  * trying to find enough space to fit @need_sz bytes: if not enough space is
219  * available and an extra_context record is present, it throws away the
220  * extra_context record.
221  *
222  * It returns a pointer to a new header where it is possible to start storing
223  * our need_sz bytes.
224  *
225  * @shead: points to the start of reserved area
226  * @need_sz: needed bytes
227  * @resv_sz: reserved area size in bytes
228  * @offset: if not null, this will be filled with the offset of the return
229  *          head pointer from @shead
230  *
231  * @return: pointer to a new head where to start storing need_sz bytes, or
232  *          NULL if space could not be made available.
233  */
234 struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
235                                        size_t need_sz, size_t resv_sz,
236                                        size_t *offset)
237 {
238         size_t offs = 0;
239         struct _aarch64_ctx *head;
240
241         head = get_terminator(shead, resv_sz, &offs);
242         /* not found a terminator...no need to update offset if any */
243         if (!head)
244                 return head;
245         if (resv_sz - offs < need_sz) {
246                 fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
247                         resv_sz - offs);
248                 head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
249                 if (!head || resv_sz - offs < need_sz) {
250                         fprintf(stderr,
251                                 "Failed to reclaim space on sigframe.\n");
252                         return NULL;
253                 }
254         }
255
256         fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
257         if (offset)
258                 *offset = offs;
259         return head;
260 }