To avoid buffer overflows, add sizes and use safe functions. Buffers are now limited to NAME_MAX. Potentially, it may cause regression in non-standard usecases. Adapt description to kernel-doc standard. Add verification for name and dev to ensure that at least one of them is set. Remove redundant chosen update after verification. It is set already. Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@xxxxxxxxxxxxxxx> --- Assemble.c | 2 +- Build.c | 2 +- Create.c | 3 +- Incremental.c | 10 ++-- mdadm.h | 5 +- mdopen.c | 132 ++++++++++++++++++++++++++++++++------------------ 6 files changed, 96 insertions(+), 58 deletions(-) diff --git a/Assemble.c b/Assemble.c index 0df46244..77bfc6f7 100644 --- a/Assemble.c +++ b/Assemble.c @@ -1572,7 +1572,7 @@ try_again: name = strchr(name, ':')+1; mdfd = create_mddev(mddev, name, ident->autof, trustworthy, - chosen_name, 0); + chosen_name, sizeof(chosen_name), 0); } if (mdfd < 0) { st->ss->free_super(st); diff --git a/Build.c b/Build.c index 962c2e37..0561beb5 100644 --- a/Build.c +++ b/Build.c @@ -97,7 +97,7 @@ int Build(char *mddev, struct mddev_dev *devlist, /* We need to create the device. It can have no name. */ map_lock(&map); mdfd = create_mddev(mddev, NULL, c->autof, LOCAL, - chosen_name, 0); + chosen_name, sizeof(chosen_name), 0); if (mdfd < 0) { map_unlock(&map); return 1; diff --git a/Create.c b/Create.c index f5d57f8c..12e41b06 100644 --- a/Create.c +++ b/Create.c @@ -627,7 +627,8 @@ int Create(struct supertype *st, char *mddev, /* We need to create the device */ map_lock(&map); - mdfd = create_mddev(mddev, name, c->autof, LOCAL, chosen_name, 1); + mdfd = create_mddev(mddev, name, c->autof, LOCAL, chosen_name, + sizeof(chosen_name), 1); if (mdfd < 0) { map_unlock(&map); return 1; diff --git a/Incremental.c b/Incremental.c index cd9cc0fc..e849db01 100644 --- a/Incremental.c +++ b/Incremental.c @@ -297,7 +297,8 @@ int Incremental(struct mddev_dev *devlist, struct context *c, /* Couldn't find an existing array, maybe make a new one */ mdfd = create_mddev(match ? match->devname : NULL, - name_to_use, c->autof, trustworthy, chosen_name, 0); + name_to_use, c->autof, trustworthy, + chosen_name, sizeof(chosen_name), 0); if (mdfd < 0) goto out_unlock; @@ -1568,10 +1569,9 @@ static int Incremental_container(struct supertype *st, char *devname, trustworthy = LOCAL; mdfd = create_mddev(match ? match->devname : NULL, - ra->name, - c->autof, - trustworthy, - chosen_name, 0); + ra->name, c->autof, trustworthy, + chosen_name, sizeof(chosen_name), + 0); } if (only && (!mp || strcmp(mp->devnm, only) != 0)) continue; diff --git a/mdadm.h b/mdadm.h index 8f8841d8..fa550e4d 100644 --- a/mdadm.h +++ b/mdadm.h @@ -1599,8 +1599,9 @@ extern char *get_md_name(char *devnm); extern char DefaultConfFile[]; -extern int create_mddev(char *dev, char *name, int autof, int trustworthy, - char *chosen, int block_udev); +extern int create_mddev(const char *dev, const char *name, int autof, + int trustworthy, char *chosen, const size_t chosen_size, + int block_udev); /* values for 'trustworthy' */ #define LOCAL 1 #define LOCAL_ANY 10 diff --git a/mdopen.c b/mdopen.c index 245be537..7ab23dbf 100644 --- a/mdopen.c +++ b/mdopen.c @@ -25,6 +25,7 @@ #include "mdadm.h" #include "md_p.h" #include <ctype.h> +#include <linux/limits.h> void make_parts(char *dev, int cnt) { @@ -128,7 +129,16 @@ int create_named_array(char *devnm) return 1; } -/* +/** + * create_mddev() - Create new MD device. + * @dev: name given by the user, will be used to determine wanted /dev/md/name. + * @name: secondary name specifier, used to determine md-device numer. + * @autof: obsolete. + * @trustworthy: trustworthy type. + * @chosen: pointer to buffer. + * @chosen_size: size of @chosen. Minimal length is %DEV_MD_PATH + %NAME_MAX. + * @block_udev: if set udev will be blocked. + * * We need a new md device to assemble/build/create an array. * 'dev' is a name given us by the user (command line or mdadm.conf) * It might start with /dev or /dev/md any might end with a digit @@ -160,10 +170,13 @@ int create_named_array(char *devnm) * /dev/mdXX in 'chosen'. * * When we create devices, we use uid/gid/umask from config file. + * + * Return: O_EXCL file descriptor or negative integer. + * + * Null terminated name for the volume is returned via *chosen. */ - -int create_mddev(char *dev, char *name, int autof, int trustworthy, - char *chosen, int block_udev) +int create_mddev(const char *dev, const char *name, int autof, int trustworthy, + char *chosen, const size_t chosen_size, int block_udev) { int mdfd; struct stat stb; @@ -171,16 +184,24 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, int use_mdp = -1; struct createinfo *ci = conf_get_create_info(); int parts; + const size_t cname_size = NAME_MAX; char *cname; - char devname[37]; - char devnm[32]; - char cbuf[400]; + char devname[NAME_MAX + 5]; + char devnm[NAME_MAX] = "\0"; + static const char dev_md_path[] = "/dev/md/"; if (!use_udev()) block_udev = 0; - if (chosen == NULL) - chosen = cbuf; + if (chosen == NULL) { + dprintf("Chosen buffer cannot be NULL.\n"); + return -1; + } + + if (!dev && !name) { + dprintf("Both dev and name cannot be NULL.\n"); + return -1; + } if (autof == 0) autof = ci->autof; @@ -188,35 +209,48 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, parts = autof >> 3; autof &= 7; - strcpy(chosen, "/dev/md/"); - cname = chosen + strlen(chosen); + if (chosen_size <= strlen(dev_md_path) + cname_size) { + dprintf("Chosen buffer is to small.\n"); + return -1; + } + + strncpy(chosen, dev_md_path, chosen_size); + cname = chosen + strlen(dev_md_path); if (dev) { - if (strncmp(dev, "/dev/md/", 8) == 0) { - strcpy(cname, dev+8); - } else if (strncmp(dev, "/dev/", 5) == 0) { - char *e = dev + strlen(dev); + if (strncmp(dev, dev_md_path, 8) == 0) + strncpy(cname, dev+8, cname_size); + else if (strncmp(dev, dev_md_path, 5) == 0) { + const char *e = dev + 5 + strnlen(dev + 5, NAME_MAX); + while (e > dev && isdigit(e[-1])) e--; if (e[0]) num = strtoul(e, NULL, 10); - strcpy(cname, dev+5); + strncpy(cname, dev + 5, cname_size); cname[e-(dev+5)] = 0; + /* name *must* be mdXX or md_dXX in this context */ if (num < 0 || - (strcmp(cname, "md") != 0 && strcmp(cname, "md_d") != 0)) { + (strncmp(cname, "md", 2) != 0 && + strncmp(cname, "md_d", 4) != 0)) { pr_err("%s is an invalid name for an md device. Try /dev/md/%s\n", dev, dev+5); return -1; } - if (strcmp(cname, "md") == 0) + if (strncmp(cname, "md", 2) == 0) use_mdp = 0; else use_mdp = 1; /* recreate name: /dev/md/0 or /dev/md/d0 */ - sprintf(cname, "%s%d", use_mdp?"d":"", num); + snprintf(cname, cname_size, "%s%d", + use_mdp ? "d" : "", num); } else - strcpy(cname, dev); + strncpy(cname, dev, cname_size); + /* + * Force null termination for long names. + */ + cname[cname_size - 1] = '\0'; /* 'cname' must not contain a slash, and may not be * empty. @@ -271,8 +305,9 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, * 'md' or '/dev/md', use that for num * if it is not already in use */ char *ep; - char *n2 = name; - if (strncmp(n2, "/dev/", 5) == 0) + const char *n2 = name; + + if (strncmp(n2, dev_md_path, 5) == 0) n2 += 5; if (strncmp(n2, "md", 2) == 0) n2 += 2; @@ -282,7 +317,8 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, if (ep == n2 || *ep) num = -1; else { - sprintf(devnm, "md%s%d", use_mdp ? "_d":"", num); + snprintf(devnm, sizeof(devnm), "md%s%d", + use_mdp ? "_d" : "", num); if (mddev_busy(devnm)) num = -1; } @@ -290,16 +326,17 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, if (cname[0] == 0 && name) { /* Need to find a name if we can - * We don't completely trust 'name'. Truncate to - * reasonable length and remove '/' + * We don't completely trust 'name'. */ char *cp; struct map_ent *map = NULL; int conflict = 1; int unum = 0; int cnlen; - strncpy(cname, name, 200); - cname[200] = 0; + + strncpy(cname, name, cname_size); + cname[cname_size - 1] = '\0'; + for (cp = cname; *cp ; cp++) switch (*cp) { case '/': @@ -317,15 +354,17 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, if (map_by_name(&map, cname) == NULL) conflict = 0; } - cnlen = strlen(cname); + cnlen = strnlen(cname, cname_size); while (conflict) { if (trustworthy == METADATA && !isdigit(cname[cnlen-1])) - sprintf(cname+cnlen, "%d", unum); + snprintf(cname + cnlen, cname_size - cnlen, + "%d", unum); else /* add _%d to FOREIGN array that don't * a 'host:' prefix */ - sprintf(cname+cnlen, "_%d", unum); + snprintf(cname + cnlen, cname_size - cnlen, + "_%d", unum); unum++; if (map_by_name(&map, cname) == NULL) conflict = 0; @@ -333,8 +372,8 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, } devnm[0] = 0; - if (num < 0 && cname && ci->names) { - sprintf(devnm, "md_%s", cname); + if (num < 0 && ci->names) { + snprintf(devnm, sizeof(devnm), "md_%s", cname); if (block_udev) udev_block(devnm); if (!create_named_array(devnm)) { @@ -343,7 +382,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, } } if (num >= 0) { - sprintf(devnm, "md%d", num); + snprintf(devnm, sizeof(devnm), "md%d", num); if (block_udev) udev_block(devnm); if (!create_named_array(devnm)) { @@ -359,12 +398,13 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, pr_err("No avail md devices - aborting\n"); return -1; } - strcpy(devnm, _devnm); + strncpy(devnm, _devnm, sizeof(devnm) - 1); } else { - sprintf(devnm, "%s%d", use_mdp?"md_d":"md", num); + snprintf(devnm, sizeof(devnm), "%s%d", + use_mdp ? "md_d" : "md", num); if (mddev_busy(devnm)) { pr_err("%s is already in use.\n", - dev); + devnm); return -1; } } @@ -372,12 +412,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, udev_block(devnm); } - sprintf(devname, "/dev/%s", devnm); - - if (dev && dev[0] == '/') - strcpy(chosen, dev); - else if (cname[0] == 0) - strcpy(chosen, devname); + snprintf(devname, sizeof(devname), "/dev/%s", devnm); /* We have a device number and name. * If we cannot detect udev, we need to make @@ -410,7 +445,7 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, if (use_mdp == 1) make_parts(devname, parts); - if (strcmp(chosen, devname) != 0) { + if (strncmp(chosen, devname, sizeof(devname)) != 0) { if (mkdir("/dev/md",0700) == 0) { if (chown("/dev/md", ci->uid, ci->gid)) perror("chown /dev/md"); @@ -418,27 +453,28 @@ int create_mddev(char *dev, char *name, int autof, int trustworthy, perror("chmod /dev/md"); } - if (dev && strcmp(chosen, dev) == 0) + if (dev && strncmp(chosen, dev, chosen_size) == 0) /* We know we are allowed to use this name */ unlink(chosen); if (lstat(chosen, &stb) == 0) { - char buf[300]; + char buf[PATH_MAX]; ssize_t link_len = readlink(chosen, buf, sizeof(buf)-1); if (link_len >= 0) buf[link_len] = '\0'; if ((stb.st_mode & S_IFMT) != S_IFLNK || link_len < 0 || - strcmp(buf, devname) != 0) { + strncmp(buf, devname, sizeof(devname)) != 0) { pr_err("%s exists - ignoring\n", chosen); - strcpy(chosen, devname); + strncpy(chosen, devname, chosen_size); } } else if (symlink(devname, chosen) != 0) pr_err("failed to create %s: %s\n", chosen, strerror(errno)); - if (use_mdp && strcmp(chosen, devname) != 0) + if (use_mdp && + strncmp(chosen, devname, sizeof(devname)) != 0) make_parts(chosen, parts); } } -- 2.26.2