1// SPDX-License-Identifier: GPL-2.0
2/* Copyright (C) 2019 ARM Limited */
3#include "testcases.h"
4
5struct _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
28bool 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 + sizeof(*term))
46		*err = "Extra DATAP misplaced (not contiguous)";
47	if (*err)
48		return false;
49
50	return true;
51}
52
53bool 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 */
170struct _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