diff --git a/channel.c b/channel.c index 407ce632f..10401d595 100644 --- a/channel.c +++ b/channel.c @@ -226,13 +226,16 @@ static char * get_scan_element(const struct iio_channel *chn, size_t *length) /* Returns a string containing the XML representation of this channel */ char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length) { - size_t len = sizeof("") - + strlen(chn->id) + (chn->name ? strlen(chn->name) : 0); - char *ptr, *str, **attrs, *scan_element = NULL; + ssize_t len; + char *ptr, *eptr, *str, **attrs, *scan_element = NULL; size_t *attrs_len, scan_element_len = 0; unsigned int i; + len = sizeof(""); + len += strnlen(chn->id, MAX_CHN_ID); + len += chn->is_output ? sizeof("output") : sizeof("input"); + len += chn->name ? sizeof(" name= ") + strnlen(chn->name, MAX_CHN_NAME) : 0; + if (chn->is_scan_element) { scan_element = get_scan_element(chn, &scan_element_len); if (!scan_element) @@ -260,26 +263,36 @@ char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length) str = malloc(len); if (!str) goto err_free_attrs; + ptr = str; + eptr = str + len; - iio_snprintf(str, len, "id); - ptr = strrchr(str, '\0'); + if (len > 0) { + ptr += iio_snprintf(str, len, "id); + len = eptr - ptr; + } - if (chn->name) { - sprintf(ptr, " name=\"%s\"", chn->name); - ptr = strrchr(ptr, '\0'); + if (chn->name && len > 0) { + ptr += iio_snprintf(ptr, len, " name=\"%s\"", chn->name); + len = eptr - ptr; } - sprintf(ptr, " type=\"%s\" >", chn->is_output ? "output" : "input"); - ptr = strrchr(ptr, '\0'); + if (len > 0) { + ptr += iio_snprintf(ptr, len, " type=\"%s\" >", chn->is_output ? "output" : "input"); + len = eptr - ptr; + } - if (chn->is_scan_element) { - strcpy(ptr, scan_element); + if (chn->is_scan_element && len > scan_element_len) { + memcpy(ptr, scan_element, scan_element_len); /* Flawfinder: ignore */ ptr += scan_element_len; + len -= scan_element_len; } for (i = 0; i < chn->nb_attrs; i++) { - strcpy(ptr, attrs[i]); - ptr += attrs_len[i]; + if (len > attrs_len[i]) { + memcpy(ptr, attrs[i], attrs_len[i]); /* Flawfinder: ignore */ + ptr += attrs_len[i]; + len -= attrs_len[i]; + } free(attrs[i]); } @@ -287,8 +300,19 @@ char * iio_channel_get_xml(const struct iio_channel *chn, size_t *length) free(attrs); free(attrs_len); - strcpy(ptr, ""); - *length = ptr - str + sizeof("") - 1; + if (len > 0) { + ptr += iio_strlcpy(ptr, "", len); + len -= sizeof("") -1; + } + + *length = ptr - str; + + if (len < 0) { + IIO_ERROR("Internal libIIO error: iio_channel_get_xml str length isssue\n"); + free(str); + return NULL; + } + return str; err_free_attrs: diff --git a/context.c b/context.c index c12395c3d..eed8730c2 100644 --- a/context.c +++ b/context.c @@ -53,20 +53,25 @@ static const char xml_header[] = "" /* Returns a string containing the XML representation of this context */ char * iio_context_create_xml(const struct iio_context *ctx) { - size_t len, *devices_len = NULL; - char *str, *ptr, **devices = NULL; + ssize_t len; + size_t *devices_len = NULL; + char *str, *ptr, *eptr, **devices = NULL; unsigned int i; - len = strlen(ctx->name) + sizeof(xml_header) - 1 + - sizeof(""); - if (ctx->description) - len += strlen(ctx->description) + - sizeof(" description=\"\"") - 1; + len = sizeof(xml_header) - 1; + len += strnlen(ctx->name, MAX_CTX_NAME); + len += sizeof("") - 1; - for (i = 0; i < ctx->nb_attrs; i++) - len += strlen(ctx->attrs[i]) + - strlen(ctx->values[i]) + - sizeof(""); + if (ctx->description) { + len += strnlen(ctx->description, MAX_CTX_DESC); + len += sizeof(" description=\"\"") - 1; + } + + for (i = 0; i < ctx->nb_attrs; i++) { + len += strnlen(ctx->attrs[i], MAX_ATTR_NAME); + len += strnlen(ctx->values[i], MAX_ATTR_VALUE); + len += sizeof("") - 1; + } if (ctx->nb_devices) { devices_len = malloc(ctx->nb_devices * sizeof(*devices_len)); @@ -94,32 +99,50 @@ char * iio_context_create_xml(const struct iio_context *ctx) errno = ENOMEM; goto err_free_devices; } - - if (ctx->description) { - iio_snprintf(str, len, "%s", - xml_header, ctx->name, ctx->description); - } else { - iio_snprintf(str, len, "%s", - xml_header, ctx->name); + eptr = str + len; + ptr = str; + + if (len > 0) { + if (ctx->description) { + ptr += iio_snprintf(str, len, "%s", + xml_header, ctx->name, ctx->description); + } else { + ptr += iio_snprintf(str, len, "%s", + xml_header, ctx->name); + } + len = eptr - ptr; } - ptr = strrchr(str, '\0'); - - for (i = 0; i < ctx->nb_attrs; i++) - ptr += sprintf(ptr, "", + for (i = 0; i < ctx->nb_attrs && len > 0; i++) { + ptr += iio_snprintf(ptr, len, "", ctx->attrs[i], ctx->values[i]); - + len = eptr - ptr; + } for (i = 0; i < ctx->nb_devices; i++) { - strcpy(ptr, devices[i]); - ptr += devices_len[i]; + if (len > devices_len[i]) { + memcpy(ptr, devices[i], devices_len[i]); /* Flawfinder: ignore */ + ptr += devices_len[i]; + len -= devices_len[i]; + } free(devices[i]); } free(devices); free(devices_len); - strcpy(ptr, ""); + + if (len > 0) { + ptr += iio_strlcpy(ptr, "", len); + len -= sizeof("") - 1; + } + + if (len < 0) { + IIO_ERROR("Internal libIIO error: iio_context_create_xml str length isssue\n"); + free(str); + return NULL; + } + return str; err_free_devices: diff --git a/device.c b/device.c index d3ec781c6..e847264a0 100644 --- a/device.c +++ b/device.c @@ -65,12 +65,18 @@ static char *get_attr_xml(const char *attr, size_t *length, enum iio_attr_type t /* Returns a string containing the XML representation of this device */ char * iio_device_get_xml(const struct iio_device *dev, size_t *length) { - size_t len = sizeof("") - + strlen(dev->id) + (dev->name ? strlen(dev->name) : 0); - char *ptr, *str, **attrs, **channels, **buffer_attrs, **debug_attrs; + ssize_t len; + char *ptr, *eptr, *str, **attrs, **channels, **buffer_attrs, **debug_attrs; size_t *attrs_len, *channels_len, *buffer_attrs_len, *debug_attrs_len; unsigned int i, j, k; + len = sizeof(" ") - 1; + len += strnlen(dev->id, MAX_DEV_ID); + if (dev->name) { + len += sizeof(" name=\"\"") - 1; + len += strnlen(dev->name, MAX_DEV_NAME); + } + attrs_len = malloc(dev->nb_attrs * sizeof(*attrs_len)); if (!attrs_len) return NULL; @@ -143,21 +149,30 @@ char * iio_device_get_xml(const struct iio_device *dev, size_t *length) str = malloc(len); if (!str) goto err_free_debug_attrs; + eptr = str + len; + ptr = str; - iio_snprintf(str, len, "id); - ptr = strrchr(str, '\0'); + if (len > 0) { + ptr += iio_snprintf(str, len, "id); + len = eptr - ptr; + } - if (dev->name) { - sprintf(ptr, " name=\"%s\"", dev->name); - ptr = strrchr(ptr, '\0'); + if (dev->name && len > 0) { + ptr += iio_snprintf(ptr, len, " name=\"%s\"", dev->name); + len = eptr - ptr; } - strcpy(ptr, " >"); - ptr += 2; + if (len > 0) { + ptr += iio_strlcpy(ptr, " >", len); + len -= 2; + } for (i = 0; i < dev->nb_channels; i++) { - strcpy(ptr, channels[i]); - ptr += channels_len[i]; + if (len > channels_len[i]) { + memcpy(ptr, channels[i], channels_len[i]); /* Flawfinder: ignore */ + ptr += channels_len[i]; + len -= channels_len[i]; + } free(channels[i]); } @@ -165,8 +180,11 @@ char * iio_device_get_xml(const struct iio_device *dev, size_t *length) free(channels_len); for (i = 0; i < dev->nb_attrs; i++) { - strcpy(ptr, attrs[i]); - ptr += attrs_len[i]; + if (len > attrs_len[i]) { + memcpy(ptr, attrs[i], attrs_len[i]); /* Flawfinder: ignore */ + ptr += attrs_len[i]; + len -= attrs_len[i]; + } free(attrs[i]); } @@ -174,8 +192,11 @@ char * iio_device_get_xml(const struct iio_device *dev, size_t *length) free(attrs_len); for (i = 0; i < dev->nb_buffer_attrs; i++) { - strcpy(ptr, buffer_attrs[i]); - ptr += buffer_attrs_len[i]; + if (len > buffer_attrs_len[i]) { + memcpy(ptr, buffer_attrs[i], buffer_attrs_len[i]); /* Flawfinder: ignore */ + ptr += buffer_attrs_len[i]; + len -= buffer_attrs_len[i]; + } free(buffer_attrs[i]); } @@ -183,16 +204,30 @@ char * iio_device_get_xml(const struct iio_device *dev, size_t *length) free(buffer_attrs_len); for (i = 0; i < dev->nb_debug_attrs; i++) { - strcpy(ptr, debug_attrs[i]); - ptr += debug_attrs_len[i]; + if (len > debug_attrs_len[i]) { + memcpy(ptr, debug_attrs[i], debug_attrs_len[i]); /* Flawfinder: ignore */ + ptr += debug_attrs_len[i]; + len -= debug_attrs_len[i]; + } free(debug_attrs[i]); } free(debug_attrs); free(debug_attrs_len); - strcpy(ptr, ""); - *length = ptr - str + sizeof("") - 1; + if (len > 0) { + ptr += iio_strlcpy(ptr, "", len); + len -= sizeof("") - 1; + } + + *length = ptr - str; + + if (len < 0) { + IIO_ERROR("Internal libIIO error: iio_device_get_xml str length isssue\n"); + free(str); + return NULL; + } + return str; err_free_debug_attrs: diff --git a/iio-private.h b/iio-private.h index 7077bbae6..dafcfd841 100644 --- a/iio-private.h +++ b/iio-private.h @@ -60,6 +60,15 @@ #define CLEAR_BIT(addr, bit) \ *(((uint32_t *) addr) + BIT_WORD(bit)) &= ~BIT_MASK(bit) +/* 256 is the MAX_NAME (file name) on Linux, 4096 is PAGESIZE */ +#define MAX_CHN_ID 256 /* encoded in the sysfs filename */ +#define MAX_CHN_NAME 256 /* encoded in the sysfs filename */ +#define MAX_DEV_ID 256 /* encoded in the sysfs filename */ +#define MAX_DEV_NAME 256 /* encoded in the sysfs filename */ +#define MAX_CTX_NAME 256 /* nominally "xml" */ +#define MAX_CTX_DESC 256 /* nominally "linux ..." */ +#define MAX_ATTR_NAME 256 /* encoded in the sysfs filename */ +#define MAX_ATTR_VALUE 4096 /* Linux page size, could be anything */ /* ntohl/htonl are a nightmare to use in cross-platform applications, * since they are defined in different headers on different platforms. @@ -289,6 +298,7 @@ void iio_channel_init_finalize(struct iio_channel *chn); unsigned int find_channel_modifier(const char *s, size_t *len_p); char *iio_strdup(const char *str); +size_t iio_strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize); int iio_context_add_attr(struct iio_context *ctx, const char *key, const char *value); diff --git a/utilities.c b/utilities.c index 85a4813cc..2178f194c 100644 --- a/utilities.c +++ b/utilities.c @@ -214,3 +214,46 @@ char *iio_strdup(const char *str) return buf; #endif } + +/* strlcpy is designed to be safer, more consistent, and less error prone + * replacements for strncpy, since it guarantees to NUL-terminate the result. + * + * This function + * Copyright (c) 1998, 2015 Todd C. Miller + * https://github.com/freebsd/freebsd/blob/master/sys/libkern/strlcpy.c + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + * + * src is assumed to be null terminated, if it is not, this function will + * dereference unknown memory beyond src. + */ +size_t iio_strlcpy(char * __restrict dst, const char * __restrict src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +}