Re: [PATCH] mdopen: use safe functions in create_mddev

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jes,
If you considering it as risky, let me know.
It can wait to 4.2.

Thanks,
Mariusz

On 21.09.2021 09:52, Mariusz Tkaczyk wrote:
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);
  		}
  	}





[Index of Archives]     [Linux RAID Wiki]     [ATA RAID]     [Linux SCSI Target Infrastructure]     [Linux Block]     [Linux IDE]     [Linux SCSI]     [Linux Hams]     [Device Mapper]     [Device Mapper Cryptographics]     [Kernel]     [Linux Admin]     [Linux Net]     [GFS]     [RPM]     [git]     [Yosemite Forum]


  Powered by Linux