Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 15 Apr 2020 03:59:26 +0000 (UTC)
From:      Kyle Evans <kevans@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r359953 - in head/sys: kern sys
Message-ID:  <202004150359.03F3xQnK077710@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: kevans
Date: Wed Apr 15 03:59:26 2020
New Revision: 359953
URL: https://svnweb.freebsd.org/changeset/base/359953

Log:
  kern uuid: break format validation out into a separate KPI
  
  This new KPI, validate_uuid, strictly validates the formatting of the input
  UUID and, optionally, populates a given struct uuid.
  
  As noted in the header, the key differences are that the new KPI won't
  recognize an empty string as a nil UUID and it won't do any kind of semantic
  validation on it. Also key is that populating a struct uuid is optional, so
  the caller doesn't necessarily need to allocate a bogus one on the stack
  just to validate the string.
  
  This KPI has specifically been broken out in support of D24288, which will
  preload /etc/hostid in loader so that early boot hostuuid users (e.g.
  anything that calls ether_gen_addr) can have a valid hostuuid to work with
  once it's been stashed in /etc/hostid.

Modified:
  head/sys/kern/kern_uuid.c
  head/sys/sys/uuid.h

Modified: head/sys/kern/kern_uuid.c
==============================================================================
--- head/sys/kern/kern_uuid.c	Wed Apr 15 03:40:33 2020	(r359952)
+++ head/sys/kern/kern_uuid.c	Wed Apr 15 03:59:26 2020	(r359953)
@@ -382,19 +382,16 @@ be_uuid_dec(void const *buf, struct uuid *uuid)
 }
 
 int
-parse_uuid(const char *str, struct uuid *uuid)
+validate_uuid(const char *str, size_t size, struct uuid *uuid)
 {
 	u_int c[11];
 	int n;
 
-	/* An empty string represents a nil UUID. */
-	if (*str == '\0') {
-		bzero(uuid, sizeof(*uuid));
-		return (0);
-	}
+	if (size == 0 || *str == '\0')
+		return (EINVAL);
 
 	/* The UUID string representation has a fixed length. */
-	if (strlen(str) != 36)
+	if (size != 36)
 		return (EINVAL);
 
 	/*
@@ -406,25 +403,48 @@ parse_uuid(const char *str, struct uuid *uuid)
 	if (str[8] != '-')
 		return (EINVAL);
 
+	/* Now check the format. */
 	n = sscanf(str, "%8x-%4x-%4x-%2x%2x-%2x%2x%2x%2x%2x%2x", c + 0, c + 1,
 	    c + 2, c + 3, c + 4, c + 5, c + 6, c + 7, c + 8, c + 9, c + 10);
 	/* Make sure we have all conversions. */
 	if (n != 11)
 		return (EINVAL);
 
-	/* Successful scan. Build the UUID. */
-	uuid->time_low = c[0];
-	uuid->time_mid = c[1];
-	uuid->time_hi_and_version = c[2];
-	uuid->clock_seq_hi_and_reserved = c[3];
-	uuid->clock_seq_low = c[4];
-	for (n = 0; n < 6; n++)
-		uuid->node[n] = c[n + 5];
+	/* Successful scan. Build the UUID if requested. */
+	if (uuid != NULL) {
+		uuid->time_low = c[0];
+		uuid->time_mid = c[1];
+		uuid->time_hi_and_version = c[2];
+		uuid->clock_seq_hi_and_reserved = c[3];
+		uuid->clock_seq_low = c[4];
+		for (n = 0; n < 6; n++)
+			uuid->node[n] = c[n + 5];
+	}
 
+	return (0);
+}
+
+int
+parse_uuid(const char *str, struct uuid *uuid)
+{
+	unsigned int clock_seq;
+	int ret;
+
+	/* An empty string represents a nil UUID. */
+	if (*str == '\0') {
+		bzero(uuid, sizeof(*uuid));
+		return (0);
+	}
+
+	ret = validate_uuid(str, strlen(str), uuid);
+	if (ret != 0)
+		return (ret);
+
 	/* Check semantics... */
-	return (((c[3] & 0x80) != 0x00 &&		/* variant 0? */
-	    (c[3] & 0xc0) != 0x80 &&			/* variant 1? */
-	    (c[3] & 0xe0) != 0xc0) ? EINVAL : 0);	/* variant 2? */
+	clock_seq = uuid->clock_seq_hi_and_reserved;
+	return (((clock_seq & 0x80) != 0x00 &&		/* variant 0? */
+	    (clock_seq & 0xc0) != 0x80 &&		/* variant 1? */
+	    (clock_seq & 0xe0) != 0xc0) ? EINVAL : 0);	/* variant 2? */
 }
 
 int

Modified: head/sys/sys/uuid.h
==============================================================================
--- head/sys/sys/uuid.h	Wed Apr 15 03:40:33 2020	(r359952)
+++ head/sys/sys/uuid.h	Wed Apr 15 03:59:26 2020	(r359953)
@@ -66,6 +66,21 @@ int uuid_ether_del(const uint8_t *);
 int snprintf_uuid(char *, size_t, struct uuid *);
 int printf_uuid(struct uuid *);
 int sbuf_printf_uuid(struct sbuf *, struct uuid *);
+
+/*
+ * There are a few key differences between validate_uuid and parse_uuid:
+ *
+ * - The struct uuid * parameter to validate_uuid is optional, so the caller
+ *    can simply validate UUID format without doing anything with the result.
+ * - validate_uuid will not pass an empty string as a valid UUID, as it doesn't
+ *    strictly meet the formatting requirements.  parse_uuid will accept an
+ *    empty string and zero out the uuid struct accordingly.
+ * - parse_uuid does additional semantic checks on clock_seq_hi_and_reserved
+ *    that validate_uuid will not do.
+ *
+ * validate_uuid is intended to strictly check that it's a well-formed uuid.
+ */
+int validate_uuid(const char *, size_t, struct uuid *);
 int parse_uuid(const char *, struct uuid *);
 int uuidcmp(const struct uuid *, const struct uuid *);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202004150359.03F3xQnK077710>