GNU Linux-libre blobhush WIP patch

Alexandre Oliva lxoliva at fsfla.org
Fri Apr 25 16:24:50 UTC 2014


So, I've worked a bit on the idea of using hashes of blob names to refer
to them in error messages and requests to hotplug scripts.
Unfortunately, a number of hard disk failures since before LibrePlanet
took much of my time and ability to focus on this :-(

The stuff in firmware.h should give you an idea of the hash function and
hashed (or hushed) names I intend to use.  The changes to
firmware_class.c are mostly noise; it was just me starting to track down
what I'd have to change in there to avoid leaking actual blob names to
error messages and userland requests, while retaining for lookups in the
built-in firmware list and in the kernel-internal firmware loader, that
avoids using the hotplug script.


The goal is to turn code such as:

  request_firmware(... blobname ...);

into something like:

  request_firmware(... HUSHBLOB (blobname) ...

and:

  err = request_firmware(... blobname ...);
  if (err)
    printk(... blobname ...);

into something like:

  err = request_firmware(... HUSHBLOB (blobname) ...
  if (err)
    printk(... HUSHBLOB (blobname).hushed ...);

or:

  const char *hushed;
  err = request_firmware(... HUSHBLOBANDSAVE (blobname, hushed) ...
  if (err)
    printk(... hushed ...);

or:

  hashed_firmware_t hushed;
  err = request_firmware(... (hushed = HUSHBLOB (blobname)) ...
  if (err)
    printk(... hushed.hushed ...);


I'm still not settled on hashed_firmware_t; I'm pondering using a char[]
such as "/\x00<hushedblobname>\x00<origblobname>" instead, for
internally storing hushed and original names, particularly in _nowait
calls, and perhaps even for the user-exposed API.

Finding the perfect API will still require some experimentation, with an
eye to minimizing changes to the request_firmware callers and to the
firmware_class.c implementation.

I hope this helps.



diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index c30df50e..063bce3 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -992,12 +992,14 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf)
  * or a negative error code
  */
 static int
-_request_firmware_prepare(struct firmware **firmware_p, const char *name,
+_request_firmware_prepare(struct firmware **firmware_p, const char *oname,
 			  struct device *device)
 {
 	struct firmware *firmware;
 	struct firmware_buf *buf;
 	int ret;
+	hashed_firmware_t hname = firmware_hush_name (oname);
+	const char *name = hname.hname;
 
 	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
 	if (!firmware) {
@@ -1006,12 +1008,12 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
 		return -ENOMEM;
 	}
 
-	if (fw_get_builtin_firmware(firmware, name)) {
+	if (fw_get_builtin_firmware(firmware, oname)) {
 		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
 		return 0; /* assigned */
 	}
 
-	ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
+	ret = fw_lookup_and_allocate_buf(o?name, &fw_cache, &buf);
 
 	/*
 	 * bind with 'buf' now to avoid warning in failure path
@@ -1071,17 +1073,19 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
 
 /* called from request_firmware() and request_firmware_work_func() */
 static int
-_request_firmware(const struct firmware **firmware_p, const char *name,
+_request_firmware(const struct firmware **firmware_p, const char *oname,
 		  struct device *device, unsigned int opt_flags)
 {
 	struct firmware *fw;
 	long timeout;
 	int ret;
+	hashed_firmware_t hname = firmware_hush_name (oname);
+	const char *name = hname.hname;
 
 	if (!firmware_p)
 		return -EINVAL;
 
-	ret = _request_firmware_prepare(&fw, name, device);
+	ret = _request_firmware_prepare(&fw, oname, device);
 	if (ret <= 0) /* error or already assigned */
 		goto out;
 
@@ -1111,7 +1115,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
 				 "Direct firmware load failed with error %d\n",
 				 ret);
 			dev_warn(device, "Falling back to user helper\n");
-			ret = fw_load_from_user_helper(fw, name, device,
+			ret = fw_load_from_user_helper(fw, o?name, device,
 						       opt_flags, timeout);
 		}
 	}
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index 5952933..d5d360c 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -75,4 +75,52 @@ int request_firmware_direct(const struct firmware **fw, const char *name,
 #define request_firmware_direct	request_firmware
 #endif
 
+#define HUSHED_FIRMWARE_PATTERN "nonFree/hush2/00000000/00000000"
+typedef struct {
+  char hname[sizeof(HUSHED_FIRMWARE_PATTERN)];
+} hashed_firmware_t;
+
+static inline void __fwu32tohex(uint32_t h, char *p)
+{
+	while (h) {
+		char c = '0' + (h & 0xf);
+		if (c > '9')
+			c += 'A' - ('0' + 10);
+		*--p = c;
+		h >>= 4;
+	}
+}
+
+static inline uint32_t __fwhashincrstr(uint32_t r, const char *name)
+{
+	unsigned char c;
+
+	while ((c = *name++) != 0)
+		r = r * 67 + c - 113;
+
+	return r;
+}
+
+/* This string hashing function, borrowed from GNU libiberty, is not
+   cryptographically strong, but it should be enough to avoid
+   suggesting the installation of non-Free Software.  */
+static inline hashed_firmware_t firmware_hush_name(const char *name,
+						   uint32_t r)
+{
+	hashed_firmware_t hname = { HUSHED_FIRMWARE_PATTERN };
+
+	__fwu32tohex(r, hname.hname + sizeof (hname.hname) - 10);
+
+	r = __fwhashincrstr(r, name);
+
+	__fwu32tohex(r, hname.hname + sizeof (hname.hname) - 1);
+
+	return hname;
+}
+
+#define FWHUSHSEED() (__fwhashincrstr(0x2F8EED03 + __LINE__,		\
+				      __DATE__ __TIME__ __FILE__))
+#define fwhush(name) (firmware_hush_name ((name), FWHUSHSEED()))
+#define fwhushtmp(name) (fwhush(name).hname)
+
 #endif


-- 
Alexandre Oliva, freedom fighter    http://FSFLA.org/~lxoliva/
You must be the change you wish to see in the world. -- Gandhi
Be Free! -- http://FSFLA.org/   FSF Latin America board member
Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer


More information about the linux-libre mailing list