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