xref: /minix/minix/lib/libsys/safecopies.c (revision bb9622b5)
1 
2 /* Library functions to maintain internal data copying tables.
3  *
4  * April 21 2006: Initial version (Ben Gras)
5  *
6  */
7 
8 #include <lib.h>
9 #include <errno.h>
10 #include <minix/sysutil.h>
11 #include <assert.h>
12 #include <stdlib.h>
13 #include <minix/syslib.h>
14 #include <minix/safecopies.h>
15 #include <minix/com.h>
16 #include <string.h>
17 
18 #define ACCESS_CHECK(a) { 			\
19 	if((a) & ~(CPF_READ|CPF_WRITE|CPF_TRY)) {	\
20 		errno = EINVAL;			\
21 		return -1;			\
22 	}					\
23    }
24 
25 #define GID_CHECK(gid) {					\
26 	if(!GRANT_VALID(gid) || GRANT_IDX(gid) >= ngrants ||	\
27 	    GRANT_SEQ(gid) != grants[GRANT_IDX(gid)].cp_seq) {	\
28 		errno = EINVAL;					\
29 		return -1;					\
30 	}							\
31    }
32 
33 #define GID_CHECK_USED(gid) {					\
34 	GID_CHECK(gid);						\
35 	if(!(grants[GRANT_IDX(gid)].cp_flags & CPF_USED)) {	\
36 		errno = EINVAL;					\
37 		return -1;					\
38 	}							\
39    }
40 
41 #define NR_STATIC_GRANTS 3
42 static cp_grant_t static_grants[NR_STATIC_GRANTS];
43 static cp_grant_t *grants = NULL;
44 static int ngrants = 0;
45 static int freelist = -1;
46 
47 /*
48  * Preallocate more grants that will be free for subsequent use.  If a specific
49  * number of grants is given (i.e., count > 0), the total number of grants will
50  * be increased by that amount.  If no number of grants is given (count == 0),
51  * double(ish) the size of the table.  The latter is used internally.  This
52  * function may fail, either because the maximum number of slots is reached or
53  * because no new memory can be allocated.  In that case, nothing will change;
54  * the caller must check afterward whether there are newly available grants.
55  */
56 void
57 cpf_prealloc(unsigned int count)
58 {
59 	cp_grant_t *new_grants;
60 	int g, new_size;
61 
62 	if (!ngrants && count <= NR_STATIC_GRANTS) {
63 		/* Use statically allocated grants the first time. */
64 		new_size = NR_STATIC_GRANTS;
65 		new_grants = static_grants;
66 	}
67 	else {
68 		if (ngrants >= GRANT_MAX_IDX)
69 			return;
70 		if (count != 0) {
71 			if (count > (unsigned)(GRANT_MAX_IDX - ngrants))
72 				count = (unsigned)(GRANT_MAX_IDX - ngrants);
73 			new_size = ngrants + (int)count;
74 		} else
75 			new_size = (1+ngrants)*2;
76 		if (new_size >= GRANT_MAX_IDX)
77 			new_size = GRANT_MAX_IDX;
78 		assert(new_size > ngrants);
79 
80 		/* Allocate a block of new size. */
81 		if(!(new_grants=malloc(new_size * sizeof(grants[0])))) {
82 			return;
83 		}
84 	}
85 
86 	/* Copy old block to new block. */
87 	if(grants && ngrants > 0)
88 		memcpy(new_grants, grants, ngrants * sizeof(grants[0]));
89 
90 	/*
91 	 * Make sure new slots are marked unused (CPF_USED is clear).
92 	 * Also start with a zero sequence number, for consistency; since the
93 	 * grant table is never shrunk, this introduces no issues by itself.
94 	 * Finally, form a new free list, in ascending order so that the lowest
95 	 * IDs get allocated first.  Both the zeroed sequence number and the
96 	 * ascending order are necessary so that the first grant to be
97 	 * allocated has a zero ID (see the live update comment below).
98 	 */
99 	for(g = ngrants; g < new_size; g++) {
100 		new_grants[g].cp_flags = 0;
101 		new_grants[g].cp_seq = 0;
102 		new_grants[g].cp_u.cp_free.cp_next =
103 		    (g < new_size - 1) ? (g + 1) : freelist;
104 	}
105 
106 	/* Inform kernel about new size (and possibly new location). */
107 	if((sys_setgrant(new_grants, new_size))) {
108                 if(new_grants != static_grants) free(new_grants);
109 		return;	/* Failed - don't grow then. */
110 	}
111 
112 	/* Update internal data. */
113 	if(grants && ngrants > 0 && grants != static_grants) free(grants);
114 	freelist = ngrants;
115 	grants = new_grants;
116 	ngrants = new_size;
117 }
118 
119 static int
120 cpf_new_grantslot(void)
121 {
122 /* Find a new, free grant slot in the grant table, grow it if
123  * necessary. If no free slot is found and the grow failed,
124  * return -1. Otherwise, return grant slot number.
125  */
126 	int g;
127 
128 	/* Obtain a free slot. */
129 	if ((g = freelist) == -1) {
130 		/* Table full - try to make the table larger. */
131 		cpf_prealloc(0);
132 		if ((g = freelist) == -1) {
133 			/* ngrants hasn't increased. */
134 			errno = ENOSPC;
135 			return -1;
136 		}
137 	}
138 
139 	/* Basic sanity checks - if we get this far, g must be a valid,
140 	 * free slot.
141 	 */
142 	assert(g >= 0);
143 	assert(g < ngrants);
144 	assert(!(grants[g].cp_flags & CPF_USED));
145 
146 	/* Take the slot off the free list, and return its slot number. */
147 	freelist = grants[g].cp_u.cp_free.cp_next;
148 
149 	return g;
150 }
151 
152 cp_grant_id_t
153 cpf_grant_direct(endpoint_t who_to, vir_bytes addr, size_t bytes, int access)
154 {
155 	int g;
156 
157 	ACCESS_CHECK(access);
158 
159 	/* Get new slot to put new grant in. */
160 	if((g = cpf_new_grantslot()) < 0)
161 		return -1;
162 
163 	/* Fill in new slot data. */
164 	grants[g].cp_u.cp_direct.cp_who_to = who_to;
165 	grants[g].cp_u.cp_direct.cp_start = addr;
166 	grants[g].cp_u.cp_direct.cp_len = bytes;
167 	grants[g].cp_faulted = GRANT_INVALID;
168 	__insn_barrier();
169 	grants[g].cp_flags = access | CPF_DIRECT | CPF_USED | CPF_VALID;
170 
171 	return GRANT_ID(g, grants[g].cp_seq);
172 }
173 
174 cp_grant_id_t
175 cpf_grant_indirect(endpoint_t who_to, endpoint_t who_from, cp_grant_id_t gr)
176 {
177 /* Grant process A access into process B. B has granted us access as grant
178  * id 'gr'.
179  */
180 	int g;
181 
182 	/* Obtain new slot. */
183 	if((g = cpf_new_grantslot()) < 0)
184 		return -1;
185 
186 	/* Fill in new slot data. */
187 	grants[g].cp_u.cp_indirect.cp_who_to = who_to;
188 	grants[g].cp_u.cp_indirect.cp_who_from = who_from;
189 	grants[g].cp_u.cp_indirect.cp_grant = gr;
190 	grants[g].cp_faulted = GRANT_INVALID;
191 	__insn_barrier();
192 	grants[g].cp_flags = CPF_USED | CPF_INDIRECT | CPF_VALID;
193 
194 	return GRANT_ID(g, grants[g].cp_seq);
195 }
196 
197 cp_grant_id_t
198 cpf_grant_magic(endpoint_t who_to, endpoint_t who_from,
199 	vir_bytes addr, size_t bytes, int access)
200 {
201 /* Grant process A access into process B. Not everyone can do this. */
202 	int g;
203 
204 	ACCESS_CHECK(access);
205 
206 	/* Obtain new slot. */
207 	if((g = cpf_new_grantslot()) < 0)
208 		return -1;
209 
210 	/* Fill in new slot data. */
211 	grants[g].cp_u.cp_magic.cp_who_to = who_to;
212 	grants[g].cp_u.cp_magic.cp_who_from = who_from;
213 	grants[g].cp_u.cp_magic.cp_start = addr;
214 	grants[g].cp_u.cp_magic.cp_len = bytes;
215 	grants[g].cp_faulted = GRANT_INVALID;
216 	__insn_barrier();
217 	grants[g].cp_flags = CPF_USED | CPF_MAGIC | CPF_VALID | access;
218 
219 	return GRANT_ID(g, grants[g].cp_seq);
220 }
221 
222 /*
223  * Revoke previously granted access, identified by grant ID.  Return -1 on
224  * error, with errno set as appropriate.  Return 0 on success, with one
225  * exception: return GRANT_FAULTED (1) if a grant was created with CPF_TRY and
226  * during its lifetime, a copy from or to the grant experienced a soft fault.
227  */
228 int
229 cpf_revoke(cp_grant_id_t grant)
230 {
231 	int r, g;
232 
233 	GID_CHECK_USED(grant);
234 
235 	g = GRANT_IDX(grant);
236 
237 	/*
238 	 * If a safecopy action on a (direct or magic) grant with the CPF_TRY
239 	 * flag failed on a soft fault, the kernel will have set the cp_faulted
240 	 * field to the grant identifier.  Here, we test this and return
241 	 * GRANT_FAULTED (1) on a match.
242 	 */
243 	r = ((grants[g].cp_flags & CPF_TRY) &&
244 	    grants[g].cp_faulted == grant) ? GRANT_FAULTED : 0;
245 
246 	/*
247 	 * Make grant invalid by setting flags to 0, clearing CPF_USED.
248 	 * This invalidates the grant.
249 	 */
250 	grants[g].cp_flags = 0;
251 	__insn_barrier();
252 
253 	/*
254 	 * Increase the grant slot's sequence number now, rather than on
255 	 * allocation, because live update relies on the first allocated grant
256 	 * having a zero ID (SEF_STATE_TRANSFER_GID) and thus a zero sequence
257 	 * number.
258 	 */
259 	if (grants[g].cp_seq < GRANT_MAX_SEQ - 1)
260 		grants[g].cp_seq++;
261 	else
262 		grants[g].cp_seq = 0;
263 
264 	/*
265 	 * Put the grant back on the free list.  The list is single-headed, so
266 	 * the last freed grant will be the first to be reused.  Especially
267 	 * given the presence of sequence numbers, this is not a problem.
268 	 */
269 	grants[g].cp_u.cp_free.cp_next = freelist;
270 	freelist = g;
271 
272 	return r;
273 }
274 
275 /*
276  * START OF DEPRECATED API
277  *
278  * The grant preallocation and (re)assignment API below imposes that grant IDs
279  * stay the same across reuse, thus disallowing that the grants' sequence
280  * numbers be updated as a part of reassignment.  As a result, this API does
281  * not offer the same protection against accidental reuse of an old grant by a
282  * remote party as the regular API does, and is therefore deprecated.
283  */
284 int
285 cpf_getgrants(cp_grant_id_t *grant_ids, int n)
286 {
287 	int i;
288 
289 	for(i = 0; i < n; i++) {
290 	  if((grant_ids[i] = cpf_new_grantslot()) < 0)
291 		break;
292 	  grants[grant_ids[i]].cp_flags = CPF_USED;
293 	  grants[grant_ids[i]].cp_seq = 0;
294 	}
295 
296 	/* return however many grants were assigned. */
297 	return i;
298 }
299 
300 int
301 cpf_setgrant_direct(gid, who, addr, bytes, access)
302 cp_grant_id_t gid;
303 endpoint_t who;
304 vir_bytes addr;
305 size_t bytes;
306 int access;
307 {
308 	GID_CHECK(gid);
309 	ACCESS_CHECK(access);
310 
311 	/* Fill in new slot data. */
312 	grants[gid].cp_flags = access | CPF_DIRECT | CPF_USED | CPF_VALID;
313 	grants[gid].cp_u.cp_direct.cp_who_to = who;
314 	grants[gid].cp_u.cp_direct.cp_start = addr;
315 	grants[gid].cp_u.cp_direct.cp_len = bytes;
316 
317 	return 0;
318 }
319 
320 int
321 cpf_setgrant_indirect(gid, who_to, who_from, his_gid)
322 cp_grant_id_t gid;
323 endpoint_t who_to, who_from;
324 cp_grant_id_t his_gid;
325 {
326 	GID_CHECK(gid);
327 
328 	/* Fill in new slot data. */
329 	grants[gid].cp_flags = CPF_USED | CPF_INDIRECT | CPF_VALID;
330 	grants[gid].cp_u.cp_indirect.cp_who_to = who_to;
331 	grants[gid].cp_u.cp_indirect.cp_who_from = who_from;
332 	grants[gid].cp_u.cp_indirect.cp_grant = his_gid;
333 
334 	return 0;
335 }
336 
337 int
338 cpf_setgrant_magic(gid, who_to, who_from, addr, bytes, access)
339 cp_grant_id_t gid;
340 endpoint_t who_to, who_from;
341 vir_bytes addr;
342 size_t bytes;
343 int access;
344 {
345 	GID_CHECK(gid);
346 	ACCESS_CHECK(access);
347 
348 	/* Fill in new slot data. */
349 	grants[gid].cp_flags = CPF_USED | CPF_MAGIC | CPF_VALID | access;
350 	grants[gid].cp_u.cp_magic.cp_who_to = who_to;
351 	grants[gid].cp_u.cp_magic.cp_who_from = who_from;
352 	grants[gid].cp_u.cp_magic.cp_start = addr;
353 	grants[gid].cp_u.cp_magic.cp_len = bytes;
354 
355 	return 0;
356 }
357 
358 int
359 cpf_setgrant_disable(gid)
360 cp_grant_id_t gid;
361 {
362 	GID_CHECK(gid);
363 
364 	/* Grant is now no longer valid, but still in use. */
365 	grants[gid].cp_flags = CPF_USED;
366 
367 	return 0;
368 }
369 /*
370  * END OF DEPRECATED API
371  */
372 
373 void
374 cpf_reload(void)
375 {
376 /* Inform the kernel about the location of the grant table. This is needed
377  * after a fork.
378  */
379 	if (grants)
380 		sys_setgrant(grants, ngrants);	/* Do we need error checking? */
381 }
382