Path: blob/main/sys/contrib/openzfs/module/os/linux/spl/spl-xdr.c
48775 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* Copyright (c) 2008-2010 Sun Microsystems, Inc.3* Written by Ricardo Correia <[email protected]>4*5* This file is part of the SPL, Solaris Porting Layer.6*7* The SPL is free software; you can redistribute it and/or modify it8* under the terms of the GNU General Public License as published by the9* Free Software Foundation; either version 2 of the License, or (at your10* option) any later version.11*12* The SPL is distributed in the hope that it will be useful, but WITHOUT13* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or14* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License15* for more details.16*17* You should have received a copy of the GNU General Public License along18* with the SPL. If not, see <http://www.gnu.org/licenses/>.19*20* Solaris Porting Layer (SPL) XDR Implementation.21*/2223#include <linux/string.h>24#include <sys/kmem.h>25#include <sys/debug.h>26#include <sys/types.h>27#include <sys/sysmacros.h>28#include <rpc/types.h>29#include <rpc/xdr.h>3031/*32* SPL's XDR mem implementation.33*34* This is used by libnvpair to serialize/deserialize the name-value pair data35* 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 various38* information in memory and later serialize it/deserialize it to disk.39* Examples of usages include the pool configuration, lists of pool and dataset40* properties, etc.41*42* Reference documentation for the XDR representation and XDR operations can be43* 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 byte50* short/unsigned short: 2 bytes51* int/unsigned int: 4 bytes52* longlong_t/u_longlong_t: 8 bytes53*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 build56* 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 the61* caller doesn't pass arguments which exceed the expected range.62* 2) Functions which take signed integers should be changed to properly do63* sign extension.64* 3) For ints with less than 32 bits, well.. I suspect you'll have bigger65* 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-aligned71* memcpy, memset and memcmp.72* 3) Arrays passed to xdr_array() are packed and the compiler/architecture73* supports element-sized-aligned memory accesses.74* 4) Negative integers are natively stored in two's complement binary75* 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 very82* well. Therefore, some assumptions about failure semantics will be made and83* will be described below:84*85* 1) If any encoding operation fails (e.g., due to lack of buffer space), the86* the stream should be considered valid only up to the encoding operation87* previous to the one that first failed. However, the stream size as returned88* by xdr_control() cannot be considered to be strictly correct (it may be89* bigger).90*91* Putting it another way, if there is an encoding failure it's undefined92* whether anything is added to the stream in that operation and therefore93* neither xdr_control() nor future encoding operations on the same stream can94* be relied upon to produce correct results.95*96* 2) If a decoding operation fails, it's undefined whether anything will be97* decoded into passed buffers/pointers during that operation, or what the98* values on those buffers will look like.99*100* Future decoding operations on the same stream will also have similar101* undefined behavior.102*103* 3) When the first decoding operation fails it is OK to trust the results of104* previous decoding operations on the same stream, as long as the caller105* 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 the108* stream size and should be coded to expect any decoding failure to be data109* corruption due to hardware, accidental or even malicious causes, which should110* be handled gracefully in all cases.111*112* In very rare situations where there are strong reasons to believe the data113* can be trusted to be valid and non-tampered with, then the caller may assume114* a decoding failure to be a bug (e.g. due to mismatched data types) and may115* 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 the122* caller is 32-bit aligned or the architecture supports non-32-bit-aligned int123* 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 user128* space or MMIO space), the computer may explode.129*/130131static const struct xdr_ops xdrmem_encode_ops;132static const struct xdr_ops xdrmem_decode_ops;133134void135xdrmem_create(XDR *xdrs, const caddr_t addr, const uint_t size,136const enum xdr_op op)137{138switch (op) {139case XDR_ENCODE:140xdrs->x_ops = &xdrmem_encode_ops;141break;142case XDR_DECODE:143xdrs->x_ops = &xdrmem_decode_ops;144break;145default:146xdrs->x_ops = NULL; /* Let the caller know we failed */147return;148}149150xdrs->x_op = op;151xdrs->x_addr = addr;152xdrs->x_addr_end = addr + size;153154if (xdrs->x_addr_end < xdrs->x_addr) {155xdrs->x_ops = NULL;156}157}158EXPORT_SYMBOL(xdrmem_create);159160static bool_t161xdrmem_control(XDR *xdrs, int req, void *info)162{163struct xdr_bytesrec *rec = (struct xdr_bytesrec *)info;164165if (req != XDR_GET_BYTES_AVAIL)166return (FALSE);167168rec->xc_is_last_record = TRUE; /* always TRUE in xdrmem streams */169rec->xc_num_avail = xdrs->x_addr_end - xdrs->x_addr;170171return (TRUE);172}173174static bool_t175xdrmem_enc_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt)176{177uint_t size = roundup(cnt, 4);178uint_t pad;179180if (size < cnt)181return (FALSE); /* Integer overflow */182183if (xdrs->x_addr > xdrs->x_addr_end)184return (FALSE);185186if (xdrs->x_addr_end - xdrs->x_addr < size)187return (FALSE);188189memcpy(xdrs->x_addr, cp, cnt);190191xdrs->x_addr += cnt;192193pad = size - cnt;194if (pad > 0) {195memset(xdrs->x_addr, 0, pad);196xdrs->x_addr += pad;197}198199return (TRUE);200}201202static bool_t203xdrmem_dec_bytes(XDR *xdrs, caddr_t cp, const uint_t cnt)204{205static uint32_t zero = 0;206uint_t size = roundup(cnt, 4);207uint_t pad;208209if (size < cnt)210return (FALSE); /* Integer overflow */211212if (xdrs->x_addr > xdrs->x_addr_end)213return (FALSE);214215if (xdrs->x_addr_end - xdrs->x_addr < size)216return (FALSE);217218memcpy(cp, xdrs->x_addr, cnt);219xdrs->x_addr += cnt;220221pad = size - cnt;222if (pad > 0) {223/* An inverted memchr() would be useful here... */224if (memcmp(&zero, xdrs->x_addr, pad) != 0)225return (FALSE);226227xdrs->x_addr += pad;228}229230return (TRUE);231}232233static bool_t234xdrmem_enc_uint32(XDR *xdrs, uint32_t val)235{236if (xdrs->x_addr + sizeof (uint32_t) > xdrs->x_addr_end)237return (FALSE);238239*((uint32_t *)xdrs->x_addr) = cpu_to_be32(val);240241xdrs->x_addr += sizeof (uint32_t);242243return (TRUE);244}245246static bool_t247xdrmem_dec_uint32(XDR *xdrs, uint32_t *val)248{249if (xdrs->x_addr + sizeof (uint32_t) > xdrs->x_addr_end)250return (FALSE);251252*val = be32_to_cpu(*((uint32_t *)xdrs->x_addr));253254xdrs->x_addr += sizeof (uint32_t);255256return (TRUE);257}258259static bool_t260xdrmem_enc_char(XDR *xdrs, char *cp)261{262uint32_t val;263264BUILD_BUG_ON(sizeof (char) != 1);265val = *((unsigned char *) cp);266267return (xdrmem_enc_uint32(xdrs, val));268}269270static bool_t271xdrmem_dec_char(XDR *xdrs, char *cp)272{273uint32_t val;274275BUILD_BUG_ON(sizeof (char) != 1);276277if (!xdrmem_dec_uint32(xdrs, &val))278return (FALSE);279280/*281* If any of the 3 other bytes are non-zero then val will be greater282* than 0xff and we fail because according to the RFC, this block does283* not have a char encoded in it.284*/285if (val > 0xff)286return (FALSE);287288*((unsigned char *) cp) = val;289290return (TRUE);291}292293static bool_t294xdrmem_enc_ushort(XDR *xdrs, unsigned short *usp)295{296BUILD_BUG_ON(sizeof (unsigned short) != 2);297298return (xdrmem_enc_uint32(xdrs, *usp));299}300301static bool_t302xdrmem_dec_ushort(XDR *xdrs, unsigned short *usp)303{304uint32_t val;305306BUILD_BUG_ON(sizeof (unsigned short) != 2);307308if (!xdrmem_dec_uint32(xdrs, &val))309return (FALSE);310311/*312* Short ints are not in the RFC, but we assume similar logic as in313* xdrmem_dec_char().314*/315if (val > 0xffff)316return (FALSE);317318*usp = val;319320return (TRUE);321}322323static bool_t324xdrmem_enc_uint(XDR *xdrs, unsigned *up)325{326BUILD_BUG_ON(sizeof (unsigned) != 4);327328return (xdrmem_enc_uint32(xdrs, *up));329}330331static bool_t332xdrmem_dec_uint(XDR *xdrs, unsigned *up)333{334BUILD_BUG_ON(sizeof (unsigned) != 4);335336return (xdrmem_dec_uint32(xdrs, (uint32_t *)up));337}338339static bool_t340xdrmem_enc_ulonglong(XDR *xdrs, u_longlong_t *ullp)341{342BUILD_BUG_ON(sizeof (u_longlong_t) != 8);343344if (!xdrmem_enc_uint32(xdrs, *ullp >> 32))345return (FALSE);346347return (xdrmem_enc_uint32(xdrs, *ullp & 0xffffffff));348}349350static bool_t351xdrmem_dec_ulonglong(XDR *xdrs, u_longlong_t *ullp)352{353uint32_t low, high;354355BUILD_BUG_ON(sizeof (u_longlong_t) != 8);356357if (!xdrmem_dec_uint32(xdrs, &high))358return (FALSE);359if (!xdrmem_dec_uint32(xdrs, &low))360return (FALSE);361362*ullp = ((u_longlong_t)high << 32) | low;363364return (TRUE);365}366367static bool_t368xdr_enc_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize,369const uint_t elsize, const xdrproc_t elproc)370{371uint_t i;372caddr_t addr = *arrp;373374if (*sizep > maxsize || *sizep > UINT_MAX / elsize)375return (FALSE);376377if (!xdrmem_enc_uint(xdrs, sizep))378return (FALSE);379380for (i = 0; i < *sizep; i++) {381if (!elproc(xdrs, addr))382return (FALSE);383addr += elsize;384}385386return (TRUE);387}388389static bool_t390xdr_dec_array(XDR *xdrs, caddr_t *arrp, uint_t *sizep, const uint_t maxsize,391const uint_t elsize, const xdrproc_t elproc)392{393uint_t i, size;394bool_t alloc = FALSE;395caddr_t addr;396397if (!xdrmem_dec_uint(xdrs, sizep))398return (FALSE);399400size = *sizep;401402if (size > maxsize || size > UINT_MAX / elsize)403return (FALSE);404405/*406* The Solaris man page says: "If *arrp is NULL when decoding,407* xdr_array() allocates memory and *arrp points to it".408*/409if (*arrp == NULL) {410BUILD_BUG_ON(sizeof (uint_t) > sizeof (size_t));411412*arrp = kmem_alloc(size * elsize, KM_NOSLEEP);413if (*arrp == NULL)414return (FALSE);415416alloc = TRUE;417}418419addr = *arrp;420421for (i = 0; i < size; i++) {422if (!elproc(xdrs, addr)) {423if (alloc)424kmem_free(*arrp, size * elsize);425return (FALSE);426}427addr += elsize;428}429430return (TRUE);431}432433static bool_t434xdr_enc_string(XDR *xdrs, char **sp, const uint_t maxsize)435{436size_t slen = strlen(*sp);437uint_t len;438439if (slen > maxsize)440return (FALSE);441442len = slen;443444if (!xdrmem_enc_uint(xdrs, &len))445return (FALSE);446447return (xdrmem_enc_bytes(xdrs, *sp, len));448}449450static bool_t451xdr_dec_string(XDR *xdrs, char **sp, const uint_t maxsize)452{453uint_t size;454bool_t alloc = FALSE;455456if (!xdrmem_dec_uint(xdrs, &size))457return (FALSE);458459if (size > maxsize || size > UINT_MAX - 1)460return (FALSE);461462/*463* Solaris man page: "If *sp is NULL when decoding, xdr_string()464* allocates memory and *sp points to it".465*/466if (*sp == NULL) {467BUILD_BUG_ON(sizeof (uint_t) > sizeof (size_t));468469*sp = kmem_alloc(size + 1, KM_NOSLEEP);470if (*sp == NULL)471return (FALSE);472473alloc = TRUE;474}475476if (!xdrmem_dec_bytes(xdrs, *sp, size))477goto fail;478479if (memchr(*sp, 0, size) != NULL)480goto fail;481482(*sp)[size] = '\0';483484return (TRUE);485486fail:487if (alloc)488kmem_free(*sp, size + 1);489490return (FALSE);491}492493static const 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_array502};503504static const 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_array513};514515516