]>
Commit | Line | Data |
---|---|---|
716154c5 BB |
1 | /*****************************************************************************\ |
2 | * Copyright (c) 2008-2010 Sun Microsystems, Inc. | |
3 | * Written by Ricardo Correia <Ricardo.M.Correia@Sun.COM> | |
f48b6193 | 4 | * |
716154c5 | 5 | * This file is part of the SPL, Solaris Porting Layer. |
3d6af2dd | 6 | * For details, see <http://zfsonlinux.org/>. |
f48b6193 | 7 | * |
716154c5 BB |
8 | * The SPL is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
f48b6193 | 12 | * |
716154c5 | 13 | * The SPL is distributed in the hope that it will be useful, but WITHOUT |
f48b6193 RC |
14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
15 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
16 | * for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
716154c5 BB |
19 | * with the SPL. If not, see <http://www.gnu.org/licenses/>. |
20 | ***************************************************************************** | |
21 | * Solaris Porting Layer (SPL) XDR Implementation. | |
22 | \*****************************************************************************/ | |
f48b6193 RC |
23 | |
24 | #include <linux/string.h> | |
f48b6193 RC |
25 | #include <sys/kmem.h> |
26 | #include <sys/debug.h> | |
27 | #include <sys/types.h> | |
f48b6193 RC |
28 | #include <rpc/types.h> |
29 | #include <rpc/xdr.h> | |
b17edc10 | 30 | |
f48b6193 RC |
31 | /* |
32 | * SPL's XDR mem implementation. | |
33 | * | |
34 | * This is used by libnvpair to serialize/deserialize the name-value pair data | |
35 | * structures into byte arrays in a well-defined and portable manner. | |
36 | * | |
37 | * These data structures are used by the DMU/ZFS to flexibly manipulate various | |
38 | * information in memory and later serialize it/deserialize it to disk. | |
39 | * Examples of usages include the pool configuration, lists of pool and dataset | |
40 | * properties, etc. | |
41 | * | |
42 | * Reference documentation for the XDR representation and XDR operations can be | |
43 | * found in RFC 1832 and xdr(3), respectively. | |
44 | * | |
45 | * === Implementation shortcomings === | |
46 | * | |
47 | * It is assumed that the following C types have the following sizes: | |
48 | * | |
49 | * char/unsigned char: 1 byte | |
50 | * short/unsigned short: 2 bytes | |
51 | * int/unsigned int: 4 bytes | |
52 | * longlong_t/u_longlong_t: 8 bytes | |
53 | * | |
54 | * The C standard allows these types to be larger (and in the case of ints, | |
55 | * shorter), so if that is the case on some compiler/architecture, the build | |
56 | * will fail (on purpose). | |
57 | * | |
58 | * If someone wants to fix the code to work properly on such environments, then: | |
59 | * | |
60 | * 1) Preconditions should be added to xdrmem_enc functions to make sure the | |
61 | * caller doesn't pass arguments which exceed the expected range. | |
62 | * 2) Functions which take signed integers should be changed to properly do | |
63 | * sign extension. | |
64 | * 3) For ints with less than 32 bits, well.. I suspect you'll have bigger | |
65 | * problems than this implementation. | |
66 | * | |
67 | * It is also assumed that: | |
68 | * | |
69 | * 1) Chars have 8 bits. | |
70 | * 2) We can always do 32-bit-aligned int memory accesses and byte-aligned | |
71 | * memcpy, memset and memcmp. | |
72 | * 3) Arrays passed to xdr_array() are packed and the compiler/architecture | |
73 | * supports element-sized-aligned memory accesses. | |
74 | * 4) Negative integers are natively stored in two's complement binary | |
75 | * representation. | |
76 | * | |
77 | * No checks are done for the 4 assumptions above, though. | |
78 | * | |
79 | * === Caller expectations === | |
80 | * | |
81 | * Existing documentation does not describe the semantics of XDR operations very | |
82 | * well. Therefore, some assumptions about failure semantics will be made and | |
83 | * will be described below: | |
84 | * | |
85 | * 1) If any encoding operation fails (e.g., due to lack of buffer space), the | |
86 | * the stream should be considered valid only up to the encoding operation | |
87 | * previous to the one that first failed. However, the stream size as returned | |
88 | * by xdr_control() cannot be considered to be strictly correct (it may be | |
89 | * bigger). | |
90 | * | |
91 | * Putting it another way, if there is an encoding failure it's undefined | |
92 | * whether anything is added to the stream in that operation and therefore | |
93 | * neither xdr_control() nor future encoding operations on the same stream can | |
94 | * be relied upon to produce correct results. | |
95 | * | |
96 | * 2) If a decoding operation fails, it's undefined whether anything will be | |
97 | * decoded into passed buffers/pointers during that operation, or what the | |
98 | * values on those buffers will look like. | |
99 | * | |
100 | * Future decoding operations on the same stream will also have similar | |
101 | * undefined behavior. | |
102 | * | |
103 | * 3) When the first decoding operation fails it is OK to trust the results of | |
104 | * previous decoding operations on the same stream, as long as the caller | |
105 | * expects a failure to be possible (e.g. due to end-of-stream). | |
106 | * | |
107 | * However, this is highly discouraged because the caller should know the | |
108 | * stream size and should be coded to expect any decoding failure to be data | |
109 | * corruption due to hardware, accidental or even malicious causes, which should | |
110 | * be handled gracefully in all cases. | |
111 | * | |
112 | * In very rare situations where there are strong reasons to believe the data | |
113 | * can be trusted to be valid and non-tampered with, then the caller may assume | |
114 | * a decoding failure to be a bug (e.g. due to mismatched data types) and may | |
115 | * fail non-gracefully. | |
116 | * | |
117 | * 4) Non-zero padding bytes will cause the decoding operation to fail. | |
118 | * | |
119 | * 5) Zero bytes on string types will also cause the decoding operation to fail. | |
120 | * | |
121 | * 6) It is assumed that either the pointer to the stream buffer given by the | |
122 | * caller is 32-bit aligned or the architecture supports non-32-bit-aligned int | |
123 | * memory accesses. | |
124 | * | |
125 | * 7) The stream buffer and encoding/decoding buffers/ptrs should not overlap. | |
126 | * | |
127 | * 8) If a caller passes pointers to non-kernel memory (e.g., pointers to user | |
128 | * space or MMIO space), the computer may explode. | |
129 | */ | |
130 | ||
131 | static struct xdr_ops xdrmem_encode_ops; | |
132 | static struct xdr_ops xdrmem_decode_ops; | |
133 | ||
134 | void | |
135 | xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size, | |
136 | const enum xdr_op op) | |
137 | { | |
138 | switch (op) { | |
139 | case XDR_ENCODE: | |
140 | xdrs->x_ops = &xdrmem_encode_ops; | |
141 | break; | |
142 | case XDR_DECODE: | |
143 | xdrs->x_ops = &xdrmem_decode_ops; | |
144 | break; | |
145 | default: | |
f48b6193 RC |
146 | xdrs->x_ops = NULL; /* Let the caller know we failed */ |
147 | return; | |
148 | } | |
149 | ||
150 | xdrs->x_op = op; | |
151 | xdrs->x_addr = addr; | |
152 | xdrs->x_addr_end = addr + size; | |
153 | ||
154 | if (xdrs->x_addr_end < xdrs->x_addr) { | |
f48b6193 RC |
155 | xdrs->x_ops = NULL; |
156 | } | |
157 | } | |
158 | EXPORT_SYMBOL(xdrmem_create); | |
159 | ||
160 | static bool_t | |
161 | xdrmem_control(XDR *xdrs, int req, void *info) | |
162 | { | |
163 | struct xdr_bytesrec *rec = (struct xdr_bytesrec *) info; | |
164 | ||
8d9a23e8 | 165 | if (req != XDR_GET_BYTES_AVAIL) |
f48b6193 | 166 | return FALSE; |
f48b6193 RC |
167 | |
168 | rec->xc_is_last_record = TRUE; /* always TRUE in xdrmem streams */ | |
169 | rec->xc_num_avail = xdrs->x_addr_end - xdrs->x_addr; | |
170 | ||
171 | return TRUE; | |
172 | } | |
173 | ||
174 | static bool_t | |
175 | xdrmem_enc_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt) | |
176 | { | |
177 | uint_t size = roundup(cnt, 4); | |
178 | uint_t pad; | |
179 | ||
180 | if (size < cnt) | |
181 | return FALSE; /* Integer overflow */ | |
182 | ||
183 | if (xdrs->x_addr > xdrs->x_addr_end) | |
184 | return FALSE; | |
185 | ||
6c33eb81 | 186 | if (xdrs->x_addr_end - xdrs->x_addr < size) |
f48b6193 RC |
187 | return FALSE; |
188 | ||
189 | memcpy(xdrs->x_addr, cp, cnt); | |
190 | ||
191 | xdrs->x_addr += cnt; | |
192 | ||
193 | pad = size - cnt; | |
194 | if (pad > 0) { | |
195 | memset(xdrs->x_addr, 0, pad); | |
196 | xdrs->x_addr += pad; | |
197 | } | |
198 | ||
199 | return TRUE; | |
200 | } | |
201 | ||
202 | static bool_t | |
203 | xdrmem_dec_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt) | |
204 | { | |
205 | static uint32_t zero = 0; | |
206 | uint_t size = roundup(cnt, 4); | |
207 | uint_t pad; | |
208 | ||
209 | if (size < cnt) | |
210 | return FALSE; /* Integer overflow */ | |
211 | ||
212 | if (xdrs->x_addr > xdrs->x_addr_end) | |
213 | return FALSE; | |
214 | ||
6c33eb81 | 215 | if (xdrs->x_addr_end - xdrs->x_addr < size) |
f48b6193 RC |
216 | return FALSE; |
217 | ||
218 | memcpy(cp, xdrs->x_addr, cnt); | |
219 | xdrs->x_addr += cnt; | |
220 | ||
221 | pad = size - cnt; | |
222 | if (pad > 0) { | |
223 | /* An inverted memchr() would be useful here... */ | |
224 | if (memcmp(&zero, xdrs->x_addr, pad) != 0) | |
225 | return FALSE; | |
226 | ||
227 | xdrs->x_addr += pad; | |
228 | } | |
229 | ||
230 | return TRUE; | |
231 | } | |
232 | ||
233 | static bool_t | |
234 | xdrmem_enc_uint32(XDR *xdrs, uint32_t val) | |
235 | { | |
236 | if (xdrs->x_addr + sizeof(uint32_t) > xdrs->x_addr_end) | |
237 | return FALSE; | |
238 | ||
239 | *((uint32_t *) xdrs->x_addr) = cpu_to_be32(val); | |
240 | ||
241 | xdrs->x_addr += sizeof(uint32_t); | |
242 | ||
243 | return TRUE; | |
244 | } | |
245 | ||
246 | static bool_t | |
247 | xdrmem_dec_uint32(XDR *xdrs, uint32_t *val) | |
248 | { | |
249 | if (xdrs->x_addr + sizeof(uint32_t) > xdrs->x_addr_end) | |
250 | return FALSE; | |
251 | ||
252 | *val = be32_to_cpu(*((uint32_t *) xdrs->x_addr)); | |
253 | ||
254 | xdrs->x_addr += sizeof(uint32_t); | |
255 | ||
256 | return TRUE; | |
257 | } | |
258 | ||
259 | static bool_t | |
260 | xdrmem_enc_char(XDR *xdrs, char *cp) | |
261 | { | |
262 | uint32_t val; | |
716154c5 | 263 | |
f48b6193 RC |
264 | BUILD_BUG_ON(sizeof(char) != 1); |
265 | val = *((unsigned char *) cp); | |
266 | ||
267 | return xdrmem_enc_uint32(xdrs, val); | |
268 | } | |
269 | ||
270 | static bool_t | |
271 | xdrmem_dec_char(XDR *xdrs, char *cp) | |
272 | { | |
273 | uint32_t val; | |
274 | ||
275 | BUILD_BUG_ON(sizeof(char) != 1); | |
276 | ||
277 | if (!xdrmem_dec_uint32(xdrs, &val)) | |
278 | return FALSE; | |
279 | ||
280 | /* | |
281 | * If any of the 3 other bytes are non-zero then val will be greater | |
282 | * than 0xff and we fail because according to the RFC, this block does | |
283 | * not have a char encoded in it. | |
284 | */ | |
285 | if (val > 0xff) | |
286 | return FALSE; | |
287 | ||
288 | *((unsigned char *) cp) = val; | |
289 | ||
290 | return TRUE; | |
291 | } | |
292 | ||
293 | static bool_t | |
294 | xdrmem_enc_ushort(XDR *xdrs, unsigned short *usp) | |
295 | { | |
296 | BUILD_BUG_ON(sizeof(unsigned short) != 2); | |
297 | ||
298 | return xdrmem_enc_uint32(xdrs, *usp); | |
299 | } | |
300 | ||
301 | static bool_t | |
302 | xdrmem_dec_ushort(XDR *xdrs, unsigned short *usp) | |
303 | { | |
304 | uint32_t val; | |
305 | ||
306 | BUILD_BUG_ON(sizeof(unsigned short) != 2); | |
307 | ||
308 | if (!xdrmem_dec_uint32(xdrs, &val)) | |
309 | return FALSE; | |
310 | ||
311 | /* | |
312 | * Short ints are not in the RFC, but we assume similar logic as in | |
313 | * xdrmem_dec_char(). | |
314 | */ | |
315 | if (val > 0xffff) | |
316 | return FALSE; | |
317 | ||
318 | *usp = val; | |
319 | ||
320 | return TRUE; | |
321 | } | |
322 | ||
323 | static bool_t | |
324 | xdrmem_enc_uint(XDR *xdrs, unsigned *up) | |
325 | { | |
326 | BUILD_BUG_ON(sizeof(unsigned) != 4); | |
327 | ||
328 | return xdrmem_enc_uint32(xdrs, *up); | |
329 | } | |
330 | ||
331 | static bool_t | |
332 | xdrmem_dec_uint(XDR *xdrs, unsigned *up) | |
333 | { | |
334 | BUILD_BUG_ON(sizeof(unsigned) != 4); | |
335 | ||
336 | return xdrmem_dec_uint32(xdrs, (uint32_t *) up); | |
337 | } | |
338 | ||
339 | static bool_t | |
340 | xdrmem_enc_ulonglong(XDR *xdrs, u_longlong_t *ullp) | |
341 | { | |
342 | BUILD_BUG_ON(sizeof(u_longlong_t) != 8); | |
343 | ||
344 | if (!xdrmem_enc_uint32(xdrs, *ullp >> 32)) | |
345 | return FALSE; | |
346 | ||
347 | return xdrmem_enc_uint32(xdrs, *ullp & 0xffffffff); | |
348 | } | |
349 | ||
350 | static bool_t | |
351 | xdrmem_dec_ulonglong(XDR *xdrs, u_longlong_t *ullp) | |
352 | { | |
353 | uint32_t low, high; | |
354 | ||
355 | BUILD_BUG_ON(sizeof(u_longlong_t) != 8); | |
356 | ||
357 | if (!xdrmem_dec_uint32(xdrs, &high)) | |
358 | return FALSE; | |
359 | if (!xdrmem_dec_uint32(xdrs, &low)) | |
360 | return FALSE; | |
361 | ||
362 | *ullp = ((u_longlong_t) high << 32) | low; | |
363 | ||
364 | return TRUE; | |
365 | } | |
366 | ||
367 | static bool_t | |
368 | xdr_enc_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize, | |
369 | const uint_t elsize, const xdrproc_t elproc) | |
370 | { | |
371 | uint_t i; | |
372 | caddr_t addr = *arrp; | |
373 | ||
374 | if (*sizep > maxsize || *sizep > UINT_MAX / elsize) | |
375 | return FALSE; | |
376 | ||
377 | if (!xdrmem_enc_uint(xdrs, sizep)) | |
378 | return FALSE; | |
379 | ||
380 | for (i = 0; i < *sizep; i++) { | |
381 | if (!elproc(xdrs, addr)) | |
382 | return FALSE; | |
383 | addr += elsize; | |
384 | } | |
385 | ||
386 | return TRUE; | |
387 | } | |
388 | ||
389 | static bool_t | |
390 | xdr_dec_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize, | |
391 | const uint_t elsize, const xdrproc_t elproc) | |
392 | { | |
393 | uint_t i, size; | |
394 | bool_t alloc = FALSE; | |
395 | caddr_t addr; | |
396 | ||
397 | if (!xdrmem_dec_uint(xdrs, sizep)) | |
398 | return FALSE; | |
399 | ||
400 | size = *sizep; | |
401 | ||
402 | if (size > maxsize || size > UINT_MAX / elsize) | |
403 | return FALSE; | |
404 | ||
405 | /* | |
406 | * The Solaris man page says: "If *arrp is NULL when decoding, | |
407 | * xdr_array() allocates memory and *arrp points to it". | |
408 | */ | |
409 | if (*arrp == NULL) { | |
410 | BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t)); | |
411 | ||
412 | *arrp = kmem_alloc(size * elsize, KM_NOSLEEP); | |
413 | if (*arrp == NULL) | |
414 | return FALSE; | |
415 | ||
416 | alloc = TRUE; | |
417 | } | |
418 | ||
419 | addr = *arrp; | |
420 | ||
421 | for (i = 0; i < size; i++) { | |
422 | if (!elproc(xdrs, addr)) { | |
423 | if (alloc) | |
424 | kmem_free(*arrp, size * elsize); | |
425 | return FALSE; | |
426 | } | |
427 | addr += elsize; | |
428 | } | |
429 | ||
430 | return TRUE; | |
431 | } | |
432 | ||
433 | static bool_t | |
434 | xdr_enc_string(XDR *xdrs, char **sp, const uint_t maxsize) | |
435 | { | |
436 | size_t slen = strlen(*sp); | |
437 | uint_t len; | |
438 | ||
439 | if (slen > maxsize) | |
440 | return FALSE; | |
441 | ||
442 | len = slen; | |
443 | ||
444 | if (!xdrmem_enc_uint(xdrs, &len)) | |
445 | return FALSE; | |
446 | ||
447 | return xdrmem_enc_bytes(xdrs, *sp, len); | |
448 | } | |
449 | ||
450 | static bool_t | |
451 | xdr_dec_string(XDR *xdrs, char **sp, const uint_t maxsize) | |
452 | { | |
453 | uint_t size; | |
454 | bool_t alloc = FALSE; | |
455 | ||
456 | if (!xdrmem_dec_uint(xdrs, &size)) | |
457 | return FALSE; | |
458 | ||
459 | if (size > maxsize || size > UINT_MAX - 1) | |
460 | return FALSE; | |
461 | ||
462 | /* | |
463 | * Solaris man page: "If *sp is NULL when decoding, xdr_string() | |
464 | * allocates memory and *sp points to it". | |
465 | */ | |
466 | if (*sp == NULL) { | |
467 | BUILD_BUG_ON(sizeof(uint_t) > sizeof(size_t)); | |
468 | ||
469 | *sp = kmem_alloc(size + 1, KM_NOSLEEP); | |
470 | if (*sp == NULL) | |
471 | return FALSE; | |
472 | ||
473 | alloc = TRUE; | |
474 | } | |
475 | ||
476 | if (!xdrmem_dec_bytes(xdrs, *sp, size)) | |
477 | goto fail; | |
478 | ||
479 | if (memchr(*sp, 0, size) != NULL) | |
480 | goto fail; | |
481 | ||
482 | (*sp)[size] = '\0'; | |
483 | ||
484 | return TRUE; | |
485 | ||
486 | fail: | |
487 | if (alloc) | |
488 | kmem_free(*sp, size + 1); | |
489 | ||
490 | return FALSE; | |
491 | } | |
492 | ||
493 | static struct xdr_ops xdrmem_encode_ops = { | |
494 | .xdr_control = xdrmem_control, | |
495 | .xdr_char = xdrmem_enc_char, | |
496 | .xdr_u_short = xdrmem_enc_ushort, | |
497 | .xdr_u_int = xdrmem_enc_uint, | |
498 | .xdr_u_longlong_t = xdrmem_enc_ulonglong, | |
499 | .xdr_opaque = xdrmem_enc_bytes, | |
500 | .xdr_string = xdr_enc_string, | |
501 | .xdr_array = xdr_enc_array | |
502 | }; | |
503 | ||
504 | static struct xdr_ops xdrmem_decode_ops = { | |
505 | .xdr_control = xdrmem_control, | |
506 | .xdr_char = xdrmem_dec_char, | |
507 | .xdr_u_short = xdrmem_dec_ushort, | |
508 | .xdr_u_int = xdrmem_dec_uint, | |
509 | .xdr_u_longlong_t = xdrmem_dec_ulonglong, | |
510 | .xdr_opaque = xdrmem_dec_bytes, | |
511 | .xdr_string = xdr_dec_string, | |
512 | .xdr_array = xdr_dec_array | |
513 | }; | |
514 |