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);
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 + sizeof(*term))
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_reserved(ucontext_t *uc, size_t resv_sz, char **err)
79 {
80 	bool terminated = false;
81 	size_t offs = 0;
82 	int flags = 0;
83 	struct extra_context *extra = NULL;
84 	struct sve_context *sve = NULL;
85 	struct _aarch64_ctx *head =
86 		(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
87 
88 	if (!err)
89 		return false;
90 	/* Walk till the end terminator verifying __reserved contents */
91 	while (head && !terminated && offs < resv_sz) {
92 		if ((uint64_t)head & 0x0fUL) {
93 			*err = "Misaligned HEAD";
94 			return false;
95 		}
96 
97 		switch (head->magic) {
98 		case 0:
99 			if (head->size)
100 				*err = "Bad size for terminator";
101 			else
102 				terminated = true;
103 			break;
104 		case FPSIMD_MAGIC:
105 			if (flags & FPSIMD_CTX)
106 				*err = "Multiple FPSIMD_MAGIC";
107 			else if (head->size !=
108 				 sizeof(struct fpsimd_context))
109 				*err = "Bad size for fpsimd_context";
110 			flags |= FPSIMD_CTX;
111 			break;
112 		case ESR_MAGIC:
113 			if (head->size != sizeof(struct esr_context))
114 				*err = "Bad size for esr_context";
115 			break;
116 		case SVE_MAGIC:
117 			if (flags & SVE_CTX)
118 				*err = "Multiple SVE_MAGIC";
119 			/* Size is validated in validate_sve_context() */
120 			sve = (struct sve_context *)head;
121 			flags |= SVE_CTX;
122 			break;
123 		case EXTRA_MAGIC:
124 			if (flags & EXTRA_CTX)
125 				*err = "Multiple EXTRA_MAGIC";
126 			else if (head->size !=
127 				 sizeof(struct extra_context))
128 				*err = "Bad size for extra_context";
129 			flags |= EXTRA_CTX;
130 			extra = (struct extra_context *)head;
131 			break;
132 		case KSFT_BAD_MAGIC:
133 			/*
134 			 * This is a BAD magic header defined
135 			 * artificially by a testcase and surely
136 			 * unknown to the Kernel parse_user_sigframe().
137 			 * It MUST cause a Kernel induced SEGV
138 			 */
139 			*err = "BAD MAGIC !";
140 			break;
141 		default:
142 			/*
143 			 * A still unknown Magic: potentially freshly added
144 			 * to the Kernel code and still unknown to the
145 			 * tests.
146 			 */
147 			fprintf(stdout,
148 				"SKIP Unknown MAGIC: 0x%X - Is KSFT arm64/signal up to date ?\n",
149 				head->magic);
150 			break;
151 		}
152 
153 		if (*err)
154 			return false;
155 
156 		offs += head->size;
157 		if (resv_sz < offs + sizeof(*head)) {
158 			*err = "HEAD Overrun";
159 			return false;
160 		}
161 
162 		if (flags & EXTRA_CTX)
163 			if (!validate_extra_context(extra, err))
164 				return false;
165 		if (flags & SVE_CTX)
166 			if (!validate_sve_context(sve, err))
167 				return false;
168 
169 		head = GET_RESV_NEXT_HEAD(head);
170 	}
171 
172 	if (terminated && !(flags & FPSIMD_CTX)) {
173 		*err = "Missing FPSIMD";
174 		return false;
175 	}
176 
177 	return true;
178 }
179 
180 /*
181  * This function walks through the records inside the provided reserved area
182  * trying to find enough space to fit @need_sz bytes: if not enough space is
183  * available and an extra_context record is present, it throws away the
184  * extra_context record.
185  *
186  * It returns a pointer to a new header where it is possible to start storing
187  * our need_sz bytes.
188  *
189  * @shead: points to the start of reserved area
190  * @need_sz: needed bytes
191  * @resv_sz: reserved area size in bytes
192  * @offset: if not null, this will be filled with the offset of the return
193  *	    head pointer from @shead
194  *
195  * @return: pointer to a new head where to start storing need_sz bytes, or
196  *	    NULL if space could not be made available.
197  */
198 struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
199 				       size_t need_sz, size_t resv_sz,
200 				       size_t *offset)
201 {
202 	size_t offs = 0;
203 	struct _aarch64_ctx *head;
204 
205 	head = get_terminator(shead, resv_sz, &offs);
206 	/* not found a terminator...no need to update offset if any */
207 	if (!head)
208 		return head;
209 	if (resv_sz - offs < need_sz) {
210 		fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
211 			resv_sz - offs);
212 		head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
213 		if (!head || resv_sz - offs < need_sz) {
214 			fprintf(stderr,
215 				"Failed to reclaim space on sigframe.\n");
216 			return NULL;
217 		}
218 	}
219 
220 	fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
221 	if (offset)
222 		*offset = offs;
223 	return head;
224 }
225