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;