rootdev: Prevent infinite recursion when parsing /sys/block.
The mmcblk driver of the 3.0.8 kernel adds mmcblk0boot0 and mmcblk0boot1
directories under /sys/block or /sys/block/mmcblk0. I have seen it both ways.
The device symlink in those directories points to "../../mmcblk0" which
points to the grandparent directory.
When rootdev scans /sys/block looking for a device that matches "/", it
can fall into this loop and recurse until failure.
While a proper fix would detect loops to prevent recursion, that adds
considerable complexity to the directory scan.
This relatively simple solution limits recursion depth to 5 to prevent
infinitely looping. It will fail to find device matches beyond 5 levels
of directories, but we are nowhere near that in today's systems. We find
matches at level 3 as best I can tell.
BUG=chromium-os:22855
TEST=Run rootdev and check for failures.
Change-Id: I9ef3aa0a6d6f8143dd0b9e012ba4bb4adfa0c73b
Reviewed-on: https://gerrit.chromium.org/gerrit/11575
Reviewed-by: Will Drewry <wad@chromium.org>
Commit-Ready: Bryan Freed <bfreed@chromium.org>
Tested-by: Bryan Freed <bfreed@chromium.org>
diff --git a/rootdev.c b/rootdev.c
index 867974f..3162be9 100644
--- a/rootdev.c
+++ b/rootdev.c
@@ -79,7 +79,7 @@
* a block device to find sub-devices (partitions).
* If dev == 0, the first device in the directory will be returned. */
static int match_sysfs_device(char *name, size_t name_len,
- const char *basedir, dev_t *dev) {
+ const char *basedir, dev_t *dev, int depth) {
int found = -1;
size_t basedir_len;
DIR *dirp = NULL;
@@ -165,10 +165,14 @@
break;
}
+ /* Prevent infinite recursion on symlink loops by limiting depth. */
+ if (depth > 5)
+ break;
+
/* Recurse one level for devices that may have a matching partition. */
if (major(found_devt) == major(*dev) && minor(*dev) > minor(found_devt)) {
sprintf(working_path, "%s/%s", basedir, entry->d_name);
- found = match_sysfs_device(name, name_len, working_path, dev);
+ found = match_sysfs_device(name, name_len, working_path, dev, depth + 1);
if (found > 0)
break;
}
@@ -241,7 +245,7 @@
}
snprintf(dst, size, "%s", search);
- if (match_sysfs_device(dst, size, dst, &dev) <= 0) {
+ if (match_sysfs_device(dst, size, dst, &dev, 0) <= 0) {
fprintf (stderr, "unable to find match\n");
return 1;
}
@@ -264,7 +268,7 @@
return -1;
}
*dev = 0;
- if (match_sysfs_device(slave, size, dst, dev) <= 0)
+ if (match_sysfs_device(slave, size, dst, dev, 0) <= 0)
return -1;
return 0;