modutils: support finit_module syscall

On some systems like Chromium OS, loading modules from non-verified
filesystems is denied.  Only finit_module is allowed because an open
fd is passed which can be checked against a verified location.

Change the module loading code to first attempt finit_module and if
that fails for whatever reason, fall back to the existing logic.

On x86_64, this adds ~80 bytes to modutils/modutils.o and ~68 bytes
to modutils/modprobe-small.o.

Signed-off-by: Mike Frysinger <vapier@chromium.org>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/modutils/modutils.c b/modutils/modutils.c
index 0a05673..d36caaf 100644
--- a/modutils/modutils.c
+++ b/modutils/modutils.c
@@ -13,6 +13,9 @@
 #else
 # include <sys/syscall.h>
 # define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
+# if defined(__NR_finit_module)
+#  define finit_module(fd, uargs, flags) syscall(__NR_finit_module, fd, uargs, flags)
+# endif
 # define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
 #endif
 
@@ -212,6 +215,24 @@
 		return bb_init_module_24(filename, options);
 #endif
 
+	/*
+	 * First we try finit_module if available.  Some kernels are configured
+	 * to only allow loading of modules off of secure storage (like a read-
+	 * only rootfs) which needs the finit_module call.  If it fails, we fall
+	 * back to normal module loading to support compressed modules.
+	 */
+# ifdef __NR_finit_module
+	{
+		int fd = open(filename, O_RDONLY | O_CLOEXEC);
+		if (fd >= 0) {
+			rc = finit_module(fd, options, 0) != 0;
+			close(fd);
+			if (rc == 0)
+				return rc;
+		}
+	}
+# endif
+
 	image_size = INT_MAX - 4095;
 	mmaped = 0;
 	image = try_to_mmap_module(filename, &image_size);