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